Skip to content

T-EGI0-task-body-validator-at-authoring-and-commit-time

Status: closed/obsoleted · Impact: low · Complexity: medium

Three drafted tasks each propose pushing a single body-shape check from task-ensure-ready pickup time back to authoring/commit time: (1) reject bulleted AC checklists and bulleted Files-to-touch tables, (2) validate Files-to-touch kind values and Today path resolution at task-new/task-define time, (3) lint Files-to-touch entries against .gitignore. All three hook the same surfaces (validate_task.py, task-new, task-define, the implementation-ready gate) and use the same parse_touchpoints.ts parser. Shipping them as 3 separate PRs means 3 review cycles on overlapping code. This task merges them into one canonical body-validator at authoring/commit time. The 3 originating drafts are superseded by this task.

LocationRole today
plugin/validators/validate_task.pyValidates frontmatter shape via the JSON schema. Does NOT validate body section structure (canonical AC bullet shape, Files-to-touch kinds, gitignored paths). Body shortcomings surface only at /sdlc:task-ensure-ready time.
plugin/skills/task-ensure-ready/parse_touchpoints.tsAlready parses ## Today and ## Files to touch tables and emits structured JSON with per-row parsed/error fields. Exemplar parser; this task wires its output into earlier-running gates.
plugin/skills/task-new/SKILL.mdCurrently calls new_task.ts (which writes frontmatter + template body) and validates frontmatter. Does NOT validate body shape — the template’s <...> placeholders are author-intent, but malformed table rows / wrong AC bullet shape ship through.
plugin/skills/task-define/SKILL.mdDrives the user through filling body sections. Today the gate at the end is validate_frontmatter.ts; body content is checked at ensure-ready pickup.
plugin/skills/task-ensure-ready/SKILL.mdStep 3 invokes parse_touchpoints.ts and scan_placeholders.ts and the structural-disqualifier checks. This is the LATE gate — author cycles back here after writing the spec instead of getting fast feedback at write time.
Lefthook / pre-commitNo body-shape gate at commit time. A task spec with malformed body lands on main until someone runs /sdlc:task-ensure-ready against it.

A unified body-shape validator runs in three places, sharing one implementation:

  1. plugin/validators/validate_task.py gains a --body flag that, when set, also validates body shape: canonical AC bullet pattern (- [ ] AC-N: <text> or - [x] AC-N: <text> — not bare - **AC-N:**), Files-to-touch kinds (must be one of new, modify, delete; symbols-on-glob rejected), Today path-form parsability, and a gitignored-paths check (each cited path under Files-to-touch is run through git check-ignore — gitignored paths fail with a clear “this file won’t make it into the PR” message).
  2. plugin/skills/task-new/SKILL.md invokes the body validator immediately after new_task.ts runs, with body validation enabled. A malformed template (shouldn’t happen if new_task.ts is doing its job, but) gets caught here.
  3. plugin/skills/task-define/SKILL.md invokes the body validator after each interactive fill-in step to give the author fast feedback. The gate is advisory at this stage (the body is mid-edit); the final validate_task.py --body run at the end of the skill is gating.
  4. Lefthook pre-commit runs validate_task.py --body against every staged task file under docs/planning/tasks/. Commits carrying malformed body shape are rejected with the specific finding cited.
  5. task-ensure-ready/SKILL.md Step 3 continues to invoke the same body validator (as before), but in practice this is now a redundancy net — by the time a task reaches the readiness gate, it has already passed the commit-time gate.

Underneath, all five sites share one implementation in validate_task.py that wraps parse_touchpoints.ts’s parser output and runs three new checks: AC-bullet-shape, gitignored-paths, and touchpoint-kind enforcement. The implementation-ready gate’s existing disqualifiers (placeholder scan, path-resolution) remain in task-ensure-ready/SKILL.md per the resolver pattern in T-F61F-pluggable-claim-resolver-interface-for-task-ensure-ready — body-shape and claim-resolver concerns are layered, not duplicated.

The 3 originating drafts close as closed/superseded once this task lands.

  1. Survey the failure modes the 3 originating drafts describe; enumerate the body-shape rules to enforce. Likely set: AC-bullet shape, Files-to-touch kinds (new/modify/delete), gitignored-paths.
  2. Extend validate_task.py with a --body flag and the three new check kinds. Each emits structured findings consumable by callers (matching the existing frontmatter-error shape).
  3. Wire task-new/SKILL.md to invoke validate_task.py --body after new_task.ts runs. Fail fast on malformed template.
  4. Wire task-define/SKILL.md to invoke it advisorily between fill-in steps; gate at the end.
  5. Wire lefthook.yml’s pre-commit: to run validate_task.py --body against staged task files. The lefthook-project-check-pre-commit task (T-A5H1-lefthook-project-check-pre-commit) is the integration point — this new check joins the others.
  6. Tests. Each new check kind gets fixture cases under plugin/validators/tests/ (or wherever validator tests live). At least: malformed AC bullet, valid AC bullet, gitignored Files-to-touch path, valid Files-to-touch path, malformed kind, valid kind.
  7. Close the 3 originating drafts with completion_note pointing here.
LocationKindChange
plugin/validators/validate_task.pymodifyAdd --body flag + AC-bullet-shape, gitignored-paths, and touchpoint-kind checks. Wraps parse_touchpoints.ts’s output.
plugin/validators/tests/test_validate_task_body.pynewFixture suite for each new check kind.
plugin/skills/task-new/SKILL.mdmodifyInvoke validate_task.py --body after new_task.ts. Fail fast on malformed template.
plugin/skills/task-define/SKILL.mdmodifyInvoke validate_task.py --body advisorily between fill-in steps; gate at the end.
lefthook.ymlmodifyAdd pre-commit: hook running validate_task.py --body against staged task files. (Coordinate with T-A5H1-lefthook-project-check-pre-commit if it has not yet landed.)
plugin/skills/task-ensure-ready/SKILL.mdmodifyStep 3 continues to invoke validate_task.py --body as a redundancy net; prose notes the gate is now upstream.
  • AC-1: plugin/validators/validate_task.py --body <task-path> exits non-zero with a structured finding for: (a) a Files-to-touch row whose kind is not one of new/modify/delete, (b) a Files-to-touch row whose Location is a gitignored path, (c) an AC bullet that uses the legacy bulleted-checklist shape (- **AC-N:** instead of - [ ] AC-N:).
  • AC-2: A task spec that complies with all three rules exits 0 from validate_task.py --body.
  • AC-3: /sdlc:task-new invokes the body validator after new_task.ts and surfaces any malformed-template finding immediately.
  • AC-4: /sdlc:task-define invokes the body validator advisorily between fill-in steps and gates the final commit on it.
  • AC-5: A git commit of a task file whose body fails one of the three checks is rejected by lefthook’s pre-commit hook with the specific finding cited.
  • AC-6: The 3 superseded originating drafts (T-G39V-validate-task-body-shape-at-commit-time, T-24ZN-task-authoring-validates-touchpoint-kinds, T-CWLT-files-to-touch-gitignore-resolution-lint) have completion_note pointing to this task; their status is closed/superseded.
  • Refactoring parse_touchpoints.ts. It already does the parsing work; this task consumes its output without changing its contract.
  • The unstaged-body-edits gate (T-XBJY-ensure-ready-refuses-with-unstaged-body-edits) — that’s a staging-state concern, not a body-shape concern, and stays as its own task.
  • Adding new disqualifiers beyond the three named (AC shape, kinds, gitignored). Future check kinds add via the same --body flag’s check list; out of scope here.
  • LLM-driven validation. Every check is deterministic Python.
  • Modifying the template’s body shape. The template is the contract this task enforces; mismatches between template and reality get fixed in the template, not the validator.
  • T-A5H1-lefthook-project-check-pre-commit — provides the pre-commit hook infrastructure that AC-5 hooks into. If unmerged at pickup time, this task can either gate on it or land the lefthook entry as part of this PR.

Spawned 2026-05-28 from a consolidation audit that surfaced three drafts (validate-task-body-shape-at-commit-time, task-authoring-validates-touchpoint-kinds, files-to-touch-gitignore-resolution-lint) all hooking the same surfaces with the same parser. Merging them into one PR avoids three rounds of review on overlapping validate_task.py and SKILL.md surfaces.

The superseded drafts:


← Back to Tasks