From b01f7b6934f3b7dc8633407382ef5aed9b96ff00 Mon Sep 17 00:00:00 2001 From: openhands Date: Tue, 25 Aug 2026 18:09:51 +0000 Subject: [PATCH] Relax ready-for-dev heading check to accept h2 headings The readiness parser split issue bodies on `###` (h3) headings only, so free-form issues using `##` (h2) headings were treated as missing their Desired Behavior / Acceptance Criteria sections. Match any heading level of two or more hashes so `##` and `###` are handled equivalently. Fixes #4631 Co-authored-by: openhands --- .github/scripts/check_issue_readiness.py | 10 ++++++---- tests/cross/test_check_issue_readiness.py | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) 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