Skip to content

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.

LocationRole 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:517surfaces files_to_touch_kind as a fact; a prose Files-to-touch produces no gaps[] entry
apps/sdlc/skills/task-ensure-ready/SKILL.md:112interpretation 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:259writes 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.

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.

  1. Replace the TBD pattern in scan-placeholders.ts so emphasis markers do not defeat the word boundary. Prefer an explicit delimiter class over \b (e.g. match TBD when 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.
  2. Add cases to scan-placeholders.test.ts covering _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.
  3. Decide where the files_to_touch_kind: prose disqualifier lands: as a section-category entry in gap-report’s gaps[], or as an interpretation-layer rule in task-ensure-ready. Prefer gap-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.
  4. If step 3 lands in the op, update task-ensure-ready’s SKILL.md so the interpretation layer no longer implies prose is silently acceptable for Files to touch, and keep the bulleted-legacy hard fail documented.
  5. Add a regression test asserting the compound case: a task with every required section present but stubbed _TBD — …_ and a prose Files to touch reports has_gaps: true.
LocationKindChange
apps/sdlc/lib/model/entities/task/ops/scan-placeholders.tsmodifywiden the TBD phrase pattern past markdown emphasis
apps/sdlc/lib/model/entities/task/ops/scan-placeholders.test.tsmodifyemphasis-wrapped cases; assert existing forms still match
apps/sdlc/lib/model/entities/task/ops/gap-report.tsmodifytreat files_to_touch_kind: prose as a disqualifier
apps/sdlc/lib/model/entities/task/ops/gap-report.test.tsmodifycompound all-stub regression case
apps/sdlc/skills/task-ensure-ready/SKILL.mdmodifydocument the prose-Files-to-touch rule alongside bulleted-legacy
  • AC-1: sdlc task gap-report on a task whose required sections all hold _TBD — receiver to fill before promoting from planning/draft._ reports has_gaps: true with a non-empty placeholders[].
  • AC-2: _TBD_, *TBD*, __TBD__, and **TBD** each produce a placeholder match; bare TBD, - TBD, and **TBD** continue to match (no regression).
  • AC-3: a task with a prose ## Files to touch reports a gap, while a task with a prose ## Today and a table ## Files to touch still passes.
  • AC-4: bun test apps/sdlc/lib/model/entities/task/ops/tests/ passes.
  • The <...> angle-bracket pattern and the other three phrase patterns — only the TBD boundary is known-broken.
  • Whether /sdlc:spawn-task-pr should 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.
  • none

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.


← Back to Tasks