fix(#2769): tolerate Requirements header with colon inside bold delimiters (#2782)

* fix(#2769): tolerate Requirements header with colon inside bold delimiters

extractReqIds in sdk/src/query/init.ts and the legacy init.cjs port only
matched `**Requirements**:` (colon outside bold), so phases declared with
the equally-valid markdown form `**Requirements:**` (colon inside bold,
which is what the project's own templates emit) returned phase_req_ids:
null for both `init plan-phase` and `init execute-phase`.

The mirror-image bug in `phase complete`'s REQUIREMENTS.md traceability
sweep at get-shit-done/bin/lib/phase.cjs:871 only matched the inside-bold
form, silently skipping the REQ-ID checkbox flips for any roadmap that
used the outside-bold form. Both parsers now share the same canonical
regex that accepts all three rendered-identical variants:

  **Requirements:**     (colon inside bold)
  **Requirements**:     (colon outside bold)
  **Requirements** :    (space before outside colon)

Tests:
- tests/init.test.cjs — parameterized over the three header variants for
  both init plan-phase and init execute-phase (6 new behavioral cases).
- sdk/src/query/init.test.ts — describe.each over the same variants
  exercising initPlanPhase through the SDK.
- tests/bug-2769-requirements-header-variants.test.cjs — phase complete
  flips REQ-001 in REQUIREMENTS.md across all three header variants.

Closes #2769

* refactor(#2769): centralize REQUIREMENTS_HEADER_RE constant per CodeRabbit
This commit is contained in:
Tom Boucher
2026-04-27 12:31:49 -04:00
committed by GitHub
parent 54e6da3126
commit 936cf26706
6 changed files with 227 additions and 4 deletions

View File

@@ -203,7 +203,12 @@ async function getPhaseInfoForVerifyWork(
*/
function extractReqIds(roadmapPhase: Record<string, unknown> | null): string | null {
const section = roadmapPhase?.section as string | undefined;
const reqMatch = section?.match(/^\*\*Requirements\*\*:[^\S\n]*([^\n]*)$/m);
// Accept all bold/colon variants of the Requirements header. The forms
// **Requirements:** (colon inside bold)
// **Requirements**: (colon outside bold)
// **Requirements** : (space before outside colon)
// render identically in markdown but differ textually. Issue #2769.
const reqMatch = section?.match(/^\*\*Requirements:?\*\*[^\S\n]*:?[^\S\n]*([^\n]*)$/m);
const reqExtracted = reqMatch
? reqMatch[1].replace(/[\[\]]/g, '').split(',').map((s: string) => s.trim()).filter(Boolean).join(', ')
: null;