Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions askcc/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,12 +299,17 @@ def _run_project_verification(cwd: Path) -> VerificationResult:


def _has_acceptance_criteria(body: str) -> bool:
"""Check for an acceptance criteria section with checklist items."""
match = re.search(r"#{2,}\s+acceptance\s+criteria", body, re.IGNORECASE)
"""Check for an acceptance criteria section with checklist items.

The section ends at the next heading at the same level or higher (fewer #s),
so deeper sub-headings (e.g. ### inside an ## section) stay part of it.
"""
match = re.search(r"(#{2,})\s+acceptance\s+criteria", body, re.IGNORECASE)
if not match:
return False
heading_level = len(match.group(1))
section_start = match.end()
next_heading = re.search(r"\n#{2,}\s+", body[section_start:])
next_heading = re.search(rf"\n#{{1,{heading_level}}}\s+", body[section_start:])
section = body[section_start : section_start + next_heading.start()] if next_heading else body[section_start:]
return bool(re.search(r"-\s*\[[\sx]\]", section))

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "askcc"
version = "0.2.6"
version = "0.2.7"
description = "A one-shot cc cli executor"
authors = [{ name = "mknt", email = "shane.cousins@gmail.com" }]
readme = "README.md"
Expand Down
25 changes: 25 additions & 0 deletions tests/test_askcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,31 @@ def test_h3_heading(self):
body = "### Acceptance Criteria\n- [ ] Works with h3\n"
assert _has_acceptance_criteria(body) is True

def test_h2_section_with_h3_subheadings(self):
body = (
"## Acceptance Criteria\n\n"
"### Core Responder Abstraction\n"
"- [ ] Responder ABC defined\n"
"- [ ] validate_input pass-through\n\n"
"### Guardrail\n"
"- [ ] LLMGuardrail class defined\n"
)
assert _has_acceptance_criteria(body) is True

def test_h2_section_with_h3_subheadings_then_other_h2(self):
body = (
"## Acceptance Criteria\n\n### Group A\n- [ ] Item one\n\n## Other Section\n- [ ] Should not be counted\n"
)
assert _has_acceptance_criteria(body) is True

def test_h3_section_with_h4_subheadings(self):
body = "### Acceptance Criteria\n\n#### Subgroup\n- [ ] Item under h4\n"
assert _has_acceptance_criteria(body) is True

def test_h3_section_terminated_by_sibling_h3(self):
body = "### Acceptance Criteria\nPlain text with no checklist.\n\n### Other\n- [ ] Wrong section\n"
assert _has_acceptance_criteria(body) is False


class TestHasDependenciesSection:
def test_dependencies_heading(self):
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading