T-KG6Y-task-ensure-ready-flags-spec-placeholders
Status: closed/done · Impact: medium · Complexity: small
Auto-generated from a /sdlc:task-work post-mortem. Review and
promote to open/ready before picking up.
Spec-drift fragments like (final name TBD) and (or final name) can
survive into an implementation-ready task and slip past the implementation
sub-agent, only to be caught by a manual post-implementation grep. The
readiness gate should flag these placeholder phrases as warnings (or
disqualifiers) so the spec is name-clean before pickup, instead of relying
on sub-agent diligence or post-hoc grepping. Cited by
T-T879-task-work-uses-per-project-quality-checks.
/sdlc:task-ensure-ready validates frontmatter and section presence but
does not scan the spec body for unresolved-decision markers. The originating
post-mortem reported:
Two spec-drift fragments survived sub-agent implementation:
(final name TBD)in Approach unit 2 and(or final name)in AC-5 — both referenced the abandonedconfigure-quality-checksname. Only surfaced when I grepped the spec post-implementation. Fix: the implementation-sub-agent brief should include “search the task spec for anyTBD/or final name/(pick one)phrases and resolve them” as an explicit Phase 7 sub-step. Better still: /sdlc:task-ensure-ready could flag those phrases as readiness warnings before the implementation gate so the spec is name-clean from the start.
The readiness contract at plugin/entities/task/implementation-ready.md
already lists disqualifier patterns; this would be one more pattern in
that family.
Proposed
Section titled “Proposed”/sdlc:task-ensure-ready scans the rendered task body (sections below
the frontmatter) for a small set of placeholder phrases and emits a
definition_gap for each one found, naming the section and the matched
phrase. Implementation choice — warning vs hard fail — should default to
hard fail: the spec author has already had a chance to resolve these
during /sdlc:task-define, and a downshift is cheap.
Recognized phrases (initial set, extensible):
TBD(whole word, case-insensitive)final name(in any(final name ...)parenthetical)(pick one)(or ...)where the parenthetical names an alternative — heuristic match<...>template skeleton angle-bracket placeholders left unfilled
Approach
Section titled “Approach”- Add a body-scan helper (or extend the existing readiness gate logic) in
plugin/skills/task-ensure-ready/that walks the markdown body and collects matches against the placeholder-phrase list. Skip fenced code blocks so example snippets don’t trigger false positives. - Wire the scanner into the readiness gate: any match downshifts the task
to
planning/needs-definitionwith adefinition_gapof the formspec body contains unresolved placeholder "<phrase>" in section "<H2>". - Document the new disqualifier in
plugin/entities/task/implementation-ready.mdalongside the existing disqualifier list. - Add a fixture task under
plugin/skills/task-ensure-ready/tests/that contains each placeholder variant and asserts the gate flags all of them.
Files to touch
Section titled “Files to touch”plugin/skills/task-ensure-ready/SKILL.md— add the body-scan step to the readiness procedure.plugin/entities/task/implementation-ready.md— list the new placeholder-phrase disqualifier.plugin/skills/task-ensure-ready/scripts/(possibly new) — extract the body-scan into a small helper if it doesn’t fit inline.plugin/skills/task-ensure-ready/tests/— add a fixture covering each placeholder variant.
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: A task whose body contains
(final name TBD)is flagged NEEDS-DEFINITION by/sdlc:task-ensure-readywith a gap naming the section and the matched phrase. - AC-2: A task whose body contains
(pick one)or<placeholder text>(template skeleton) is also flagged. - AC-3: A clean task body that mentions
TBDonly inside a fenced code block (e.g. as an example) is NOT flagged. - AC-4: After the placeholder is resolved, re-running
/sdlc:task-ensure-readyreaches READY and stampsreadiness_verified_at.
Out of scope
Section titled “Out of scope”- Detecting semantic gaps beyond the literal phrase list (e.g. “this section has placeholder energy but no literal placeholder marker”). That’s the /sdlc:task-define LLM’s job.
- Auto-fixing placeholders. The gate only flags; resolution belongs to the spec author.
Dependencies
Section titled “Dependencies”- none
Discovery context
Section titled “Discovery context”Spawned by /sdlc:task-work post-mortem of T-T879-task-work-uses-per-project-quality-checks on 2026-05-20.
Post-mortem
Section titled “Post-mortem”Captured by /sdlc:task-work on 2026-05-21. PR: pending.
Acceptance criteria coverage
Section titled “Acceptance criteria coverage”- AC-1: auto —
scan/approach-final-name-tbdandscan/or-final-name-flaggedinplugin/skills/task-ensure-ready/tests/run_evals.pyassert that(final name TBD)and(or final name)parentheticals are flagged with the correct section and phrase label. - AC-2: auto —
scan/ac-pick-one-and-angle-placeholderasserts(pick one)and a bare-prose<placeholder name>are both flagged. - AC-3: auto —
scan/fenced-tbd-not-flaggedproves aTBDinside a fenced code block is ignored;scan/backticked-mentions-not-flaggedandscan/post-mortem-mentions-not-flaggedcover the same intent for inline-code spans and non-required sections respectively. - AC-4: auto —
scan/clean-spec-no-outputproves the scanner emits nothing once placeholders are resolved; combined with the existingopen-ready-pass-keeps-statusmutation case, the gate’s full pass-stamp path is exercised end-to-end.
What worked
Section titled “What worked”- Co-locating the scanner next to ensure-ready followed the
“co-locate first, promote when shared” convention without friction —
one caller today, no premature move to
plugin/scripts/. - The eval suite’s existing structure (subprocess shell-out to the real script, fixtures written to a tmpdir) made adding seven scan cases mechanical — same shape, different fixture and expectation.
- Pre-running the scanner against every task under
docs/planning/tasks/before committing surfaced two real-world false positives (multi-line backtick spans, optional-section subject-matter mentions) that the initial single-line regex missed. The whole-population dry-run is a cheap step that should probably ride along with any future ensure-ready-disqualifier-extension.
Friction and automation gaps
Section titled “Friction and automation gaps”- The originating
/sdlc:task-workinvocation stopped after Step 5a (the ensure-ready stamp) and never proceeded to implementation. The parent had to be re-prompted to drive end-to-end. The Step 5aREADY:marker is documented as intermediate, not terminal, but a sub-agent still mistook it for “done.” → T-1QO7-task-work-resumes-from-verify-stamp-commit - The
start_task.pyrebase surfaced a real frontmatter conflict between thechore(tasks): startcommit on main and thedocs(tasks): verifycommit on the feat branch (both touchedreadiness_verified_atandlast_reviewed). Resolving it by hand was straightforward but the script’s--modeplumbing could potentially anticipate the conflict shape (same file, same two fields). Worth a tiny follow-up to see if the rebase can pre-merge the frontmatter fields cleanly instead of bailing toREBASEstate. → T-2QXZ-start-task-handles-frontmatter-rebase-cleanly - The scanner had to evolve twice during implementation: first the inline-code regex was single-line-only (missed multi-line backtick spans common in shell-command examples), then I had to scope to required sections (post-mortems legitimately discuss placeholder text). Both showed up only when I ran the scanner against the existing task population — neither was in the spec’s stated AC list. Fix: extending an ensure-ready disqualifier should include “dry-run against the live task population” as an explicit Step 7 sub-check, not a thing the implementer happens to think of. → T-VKFS-ensure-ready-disqualifier-population-dryrun
Spawned follow-up tasks
Section titled “Spawned follow-up tasks”- T-2QXZ-start-task-handles-frontmatter-rebase-cleanly — start_task.py auto-merges the predictable frontmatter conflict instead of bailing to REBASE state (created)
- T-VKFS-ensure-ready-disqualifier-population-dryrun — extending an ensure-ready disqualifier requires a population dry-run as an explicit step (created)
- T-1QO7-task-work-resumes-from-verify-stamp-commit — task-work resumes cleanly when only Step 5a’s verify commit landed in a prior session (linked)