T-2GR5-gap-report-detects-emphasis-wrapped-placeholders
Status: closed/superseded · Impact: high · Complexity: small
sdlc task gap-report certifies an entirely unfilled task as gap-free, so
/sdlc:task-ensure-ready stamps readiness_verified_at on a spec that says
nothing. A synthetic task whose every required section holds the scaffolder’s
own _TBD — receiver to fill before promoting from planning/draft._ stub
returns has_gaps: False, placeholders: 0, gaps: []. The readiness gate is
the only barrier between an auto-spawned stub and an implementer picking it up,
and against the exact stub convention /sdlc:spawn-task-pr writes, it does not
hold.
Two independent causes compound. Either alone would be caught by the other; both together open the hole.
| Location | Role today |
|---|---|
apps/sdlc/lib/model/entities/task/ops/scan-placeholders.ts:78 | ["TBD", /\bTBD\b/gi] — the leading \b cannot match inside _TBD_, because _ is a regex word character, so no boundary exists between _ and T |
apps/sdlc/lib/model/entities/task/ops/gap-report.ts:517 | surfaces files_to_touch_kind as a fact; a prose Files-to-touch produces no gaps[] entry |
apps/sdlc/skills/task-ensure-ready/SKILL.md:112 | interpretation layer hard-fails bulleted-legacy only; prose is unhandled for Files to touch, though it is legitimately accepted for ## Today |
apps/sdlc/skills/spawn-task-pr/SKILL.md:259 | writes the _TBD — …_ stub convention that cause 1 is blind to |
Empirically verified against the live op: _TBD_ and
_TBD — filled at Step 8._ MISS; bare TBD, - TBD, and **TBD** MATCH. The
emphasis form is the only one the scaffolder actually emits.
Proposed
Section titled “Proposed”gap-report flags a fully stubbed task as having gaps. Specifically: the TBD
phrase pattern matches regardless of surrounding markdown emphasis (_, *,
__, **), and a ## Files to touch section that parses as prose is a
disqualifier in the same way bulleted-legacy and missing already are — while
## Today continues to accept prose as the greenfield shape.
Approach
Section titled “Approach”- Replace the
TBDpattern inscan-placeholders.tsso emphasis markers do not defeat the word boundary. Prefer an explicit delimiter class over\b(e.g. matchTBDwhen flanked by start/end, whitespace, or punctuation including_and*) rather than stripping emphasis upstream — the op already strips inline code, and adding a second normalization pass would change reported column offsets. - Add cases to
scan-placeholders.test.tscovering_TBD_,*TBD*,__TBD__,**TBD**, and the full scaffolder stub line. Assert the existing bare/bulleted/bold forms still match — this is a widening, not a rewrite. - Decide where the
files_to_touch_kind: prosedisqualifier lands: as asection-category entry ingap-report’sgaps[], or as an interpretation-layer rule intask-ensure-ready. Prefergap-report— the asymmetry (Today accepts prose, Files to touch does not) is a deterministic fact about the v3 contract, not a judgment call, and putting it in the op means every caller inherits it. Record the choice in the PR body. - If step 3 lands in the op, update
task-ensure-ready’s SKILL.md so the interpretation layer no longer impliesproseis silently acceptable for Files to touch, and keep thebulleted-legacyhard fail documented. - Add a regression test asserting the compound case: a task with every required
section present but stubbed
_TBD — …_and a prose Files to touch reportshas_gaps: true.
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
apps/sdlc/lib/model/entities/task/ops/scan-placeholders.ts | modify | widen the TBD phrase pattern past markdown emphasis |
apps/sdlc/lib/model/entities/task/ops/scan-placeholders.test.ts | modify | emphasis-wrapped cases; assert existing forms still match |
apps/sdlc/lib/model/entities/task/ops/gap-report.ts | modify | treat files_to_touch_kind: prose as a disqualifier |
apps/sdlc/lib/model/entities/task/ops/gap-report.test.ts | modify | compound all-stub regression case |
apps/sdlc/skills/task-ensure-ready/SKILL.md | modify | document the prose-Files-to-touch rule alongside bulleted-legacy |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
sdlc task gap-reporton a task whose required sections all hold_TBD — receiver to fill before promoting from planning/draft._reportshas_gaps: truewith a non-emptyplaceholders[]. - AC-2:
_TBD_,*TBD*,__TBD__, and**TBD**each produce a placeholder match; bareTBD,- TBD, and**TBD**continue to match (no regression). - AC-3: a task with a prose
## Files to touchreports a gap, while a task with a prose## Todayand a table## Files to touchstill passes. - AC-4:
bun test apps/sdlc/lib/model/entities/task/ops/tests/passes.
Out of scope
Section titled “Out of scope”- The
<...>angle-bracket pattern and the other three phrase patterns — only theTBDboundary is known-broken. - Whether
/sdlc:spawn-task-prshould stop emitting stubs at all. The stub convention is deliberate; the gate is what should catch it. /sdlc:task-auto-define’s fill quality. In the 2026-07-19 batch it filled all 13 spawned tasks correctly, so no bad spec reached an implementer — the hole is latent, not yet realized.
Dependencies
Section titled “Dependencies”- none
Discovery context
Section titled “Discovery context”Spawned from the T-C9RD post-mortem review on 2026-07-20. Found while verifying
that the 13 tasks auto-spawned during the augmented → solutions migration
(#1021–#1033) carried real specs rather than stubs. They did — every one
reported stubs=0 with a populated Files-to-touch table — which is why this
went unnoticed: the gate would not have caught them had auto-define failed.
Confirmed by constructing a synthetic all-stub task and running the op against
it directly.