Skip to content

T-SJBT-task-spec-flags-schema-rejecting-placeholders

Status: closed/done · Impact: low · Complexity: small

Auto-generated from a /sdlc:task-work post-mortem. Review and promote to open/ready before picking up.

Task specs that propose making a previously-uninspected file pass validate_frontmatter.py should surface the file’s current schema-violating fields during authoring, so the spec can enumerate them in Approach before pickup. In T-3K8K-templates-html-comment-after-frontmatter the spec estimated small/medium complexity but mid-implementation surfaced that created: YYYY-MM-DD (a literal placeholder) is rejected by the schema’s ^\d{4}-\d{2}-\d{2}$ pattern — a small but unanticipated detour. An authoring-time advisory in /sdlc:task-define would catch this class of surprise before the sub-agent hits it.

Note: this is not a readiness-gate check. Failing the gate on “X doesn’t validate” would be circular — the entire point of these tasks is to make X validate. The dry-run is an authoring aid, not a disqualifier.

/sdlc:task-define walks the user through filling task sections but does not inspect the files the task proposes to bring into validation. /sdlc:task-ensure-ready likewise scopes its checks to the task’s own frontmatter. So a task that says “make file X validate” reaches open/ready without the spec author ever seeing which X-fields will fail when the implementer runs the validator.

Originating friction bullet:

Task spec didn’t anticipate that templates becoming validator-visible would expose created: YYYY-MM-DD as a schema violation — had to invent the '1970-01-01' sentinel mid-implementation.

When /sdlc:task-define detects (heuristic match) that the task’s ACs reference making a file pass validate_frontmatter.py, it runs the validator dry against that file, captures the failing fields, and offers via AskUserQuestion to inject a “known schema-violating fields in X” bullet under Approach (or add the fields to Out-of-scope). The author can accept, edit, or decline.

The readiness gate (/sdlc:task-ensure-ready) stays unchanged — this is purely an authoring advisory.

  1. In plugin/skills/task-define/SKILL.md, add a step (after the normal section-gap pass) that scans the task’s ACs for validate_frontmatter.py + a file path. For each match, run plugin/validators/validate_frontmatter.py <path> and capture stdout.
  2. If any failures are found, present them to the user via AskUserQuestion: “X currently fails N fields — inject the list into Approach?” Accept / edit / decline.
  3. On accept, Edit a bullet under Approach naming the failing fields. Don’t touch Out-of-scope unless the user picks that option.
  4. Add a fixture under plugin/skills/task-define/tests/ (or equivalent eval harness) that exercises the advisory against a known-bad target.
  • plugin/skills/task-define/SKILL.md — add the authoring-time advisory step.
  • plugin/skills/task-define/tests/run_evals.py (or equivalent) — fixture asserting the advisory fires on a known-bad target.
  • AC-1: Running /sdlc:task-define against a task whose AC says “X passes validate_frontmatter.py” and whose X has at least one schema-violating field surfaces an AskUserQuestion enumerating the failing fields.
  • AC-2: Accepting the offer Edits a bullet under Approach that names the failing fields. Declining leaves the body untouched.
  • AC-3: /sdlc:task-ensure-ready behavior is unchanged — a “make X validate” task that already enumerates the fields in Approach reaches READY whether or not X currently validates.
  • Generalizing the dry-run to ALL referenced files, not just “make-this-validate” ACs — that risks false positives on tasks that intentionally describe broken-on-purpose files.
  • Adding the dry-run as a readiness-gate disqualifier — that would be circular for fix-the-file tasks.
  • none

Spawned by /sdlc:task-work post-mortem of T-3K8K-templates-html-comment-after-frontmatter on 2026-05-20.

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

  • AC-1: auto — plugin/skills/task-define/tests/run_evals.py’s ac-with-bad-target case asserts the scanner emits the failing /created pointer that the SKILL.md procedure feeds into AskUserQuestion.
  • AC-2: agent-manual — verified by reading SKILL.md Section 4b: the procedure offers Inject / Add-to-Out-of-scope / Decline as AskUserQuestion options, and the prose test asserts all three labels are present. The Edit-vs-no-Edit branching itself is in prose, not script.
  • AC-3: auto — no edits were made to task-ensure-ready or implementation-ready.md; the existing plugin/skills/task-ensure-ready/tests/run_evals.py continues to pass (OK 8/8 in the run_quality_checks.py pass).
  • The validator’s line-based stdout ( at /pointer: message) is easy to parse; the scanner needed ~6 lines of regex to produce a structured JSON payload.
  • Co-locating the helper as scan_validate_ac.py next to SKILL.md matched the existing pattern (task-ensure-ready / start_task.py) and let the test suite drive it directly without indirection.
  • A narrow .md-only path regex avoided false positives on prose mentions like “validate_frontmatter.py” without a target (the validate-mention-no-path case guards this).
  • start_task.py’s rebase surfaced a conflict between the start-commit on main (which bumped last_reviewed: to today and set status in-progress) and ensure-ready’s stamp commit on the feat branch (which only touched readiness_verified_at: on the same lines). The conflict was mechanically trivial (take both: main’s status + last_reviewed, feat’s newer stamp) but it stopped automation cold. start_task.py could detect this specific shape — adjacent frontmatter scalar changes — and auto-resolve by preferring main’s status/last_reviewed and feat’s stamp; falling back to manual only on real semantic conflicts. → T-H0W9-task-work-rebase-frontmatter-conflict

← Back to Tasks