Skip to content

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 abandoned configure-quality-checks name. Only surfaced when I grepped the spec post-implementation. Fix: the implementation-sub-agent brief should include “search the task spec for any TBD / 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.

/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
  1. 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.
  2. Wire the scanner into the readiness gate: any match downshifts the task to planning/needs-definition with a definition_gap of the form spec body contains unresolved placeholder "<phrase>" in section "<H2>".
  3. Document the new disqualifier in plugin/entities/task/implementation-ready.md alongside the existing disqualifier list.
  4. Add a fixture task under plugin/skills/task-ensure-ready/tests/ that contains each placeholder variant and asserts the gate flags all of them.
  • 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.
  • AC-1: A task whose body contains (final name TBD) is flagged NEEDS-DEFINITION by /sdlc:task-ensure-ready with 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 TBD only 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-ready reaches READY and stamps readiness_verified_at.
  • 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.
  • none

Spawned by /sdlc:task-work post-mortem of T-T879-task-work-uses-per-project-quality-checks on 2026-05-20.

Captured by /sdlc:task-work on 2026-05-21. PR: pending.

  • AC-1: auto — scan/approach-final-name-tbd and scan/or-final-name-flagged in plugin/skills/task-ensure-ready/tests/run_evals.py assert 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-placeholder asserts (pick one) and a bare-prose <placeholder name> are both flagged.
  • AC-3: auto — scan/fenced-tbd-not-flagged proves a TBD inside a fenced code block is ignored; scan/backticked-mentions-not-flagged and scan/post-mortem-mentions-not-flagged cover the same intent for inline-code spans and non-required sections respectively.
  • AC-4: auto — scan/clean-spec-no-output proves the scanner emits nothing once placeholders are resolved; combined with the existing open-ready-pass-keeps-status mutation case, the gate’s full pass-stamp path is exercised end-to-end.
  • 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.
  • The originating /sdlc:task-work invocation 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 5a READY: 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.py rebase surfaced a real frontmatter conflict between the chore(tasks): start commit on main and the docs(tasks): verify commit on the feat branch (both touched readiness_verified_at and last_reviewed). Resolving it by hand was straightforward but the script’s --mode plumbing 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 to REBASE state. → 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

← Back to Tasks