diff --git a/.github/scripts/check_issue_readiness.py b/.github/scripts/check_issue_readiness.py index 208fdb1aa8..f9a4b2c75f 100644 --- a/.github/scripts/check_issue_readiness.py +++ b/.github/scripts/check_issue_readiness.py @@ -37,10 +37,12 @@ BUG_LABEL = "bug" ENHANCEMENT_LABEL = "enhancement" -# Issue-form fields render as `### Label` h3 headings. Match case-insensitively -# and tolerate trailing whitespace/colons. `^###\s+` is specific enough because -# h1/h2 are not produced by issue forms. -HEADING_RE = re.compile(r"(?m)^###\s+(.+?)\s*$") +# Issue-form fields render as `### Label` h3 headings, but free-form issues +# (and issues edited by agents) commonly use `##` h2 headings. Match any +# heading level of two or more hashes case-insensitively and tolerate trailing +# whitespace/colons, so `##` and `###` sections are treated equivalently. A +# bare `#` (single-hash title) is deliberately not matched. +HEADING_RE = re.compile(r"(?m)^#{2,}\s+(.+?)\s*$") # `_No response_` is what GitHub writes for an empty optional form field. NO_RESPONSE = "_No response_" diff --git a/tests/cross/test_check_issue_readiness.py b/tests/cross/test_check_issue_readiness.py index 67e21112d6..c3b3a54228 100644 --- a/tests/cross/test_check_issue_readiness.py +++ b/tests/cross/test_check_issue_readiness.py @@ -53,6 +53,26 @@ def test_extract_sections_splits_on_headings(): assert sections["beta"] == "\nmore\n" +def test_extract_sections_splits_on_h2_headings(): + sections = extract_sections("## Alpha\n\ntext\n\n## Beta\n\nmore\n") + assert sections["alpha"] == "\ntext\n\n" + assert sections["beta"] == "\nmore\n" + + +def test_enhancement_h2_headings_passes(): + body = ENHANCEMENT_READY.replace("### ", "## ") + result = evaluate_readiness(body, ["enhancement"]) + assert result.ready is True + assert result.reasons == [] + + +def test_bug_h2_headings_passes(): + body = BUG_READY.replace("### ", "## ") + result = evaluate_readiness(body, ["bug"]) + assert result.ready is True + assert result.reasons == [] + + def test_enhancement_ready_passes(): result = evaluate_readiness(ENHANCEMENT_READY, ["enhancement"]) assert result.ready is True