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.
| Location | Role today |
|---|---|
plugin/validators/validate_task.py | Validates 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.ts | Already 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.md | Currently 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.md | Drives 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.md | Step 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-commit | No body-shape gate at commit time. A task spec with malformed body lands on main until someone runs /sdlc:task-ensure-ready against it. |
Proposed
Section titled “Proposed”A unified body-shape validator runs in three places, sharing one implementation:
plugin/validators/validate_task.pygains a--bodyflag 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 ofnew,modify,delete; symbols-on-glob rejected), Today path-form parsability, and a gitignored-paths check (each cited path under Files-to-touch is run throughgit check-ignore— gitignored paths fail with a clear “this file won’t make it into the PR” message).plugin/skills/task-new/SKILL.mdinvokes the body validator immediately afternew_task.tsruns, with body validation enabled. A malformed template (shouldn’t happen ifnew_task.tsis doing its job, but) gets caught here.plugin/skills/task-define/SKILL.mdinvokes 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 finalvalidate_task.py --bodyrun at the end of the skill is gating.- Lefthook
pre-commitrunsvalidate_task.py --bodyagainst every staged task file underdocs/planning/tasks/. Commits carrying malformed body shape are rejected with the specific finding cited. task-ensure-ready/SKILL.mdStep 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.
Approach
Section titled “Approach”- 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.
- Extend
validate_task.pywith a--bodyflag and the three new check kinds. Each emits structured findings consumable by callers (matching the existing frontmatter-error shape). - Wire
task-new/SKILL.mdto invokevalidate_task.py --bodyafternew_task.tsruns. Fail fast on malformed template. - Wire
task-define/SKILL.mdto invoke it advisorily between fill-in steps; gate at the end. - Wire
lefthook.yml’spre-commit:to runvalidate_task.py --bodyagainst staged task files. Thelefthook-project-check-pre-committask (T-A5H1-lefthook-project-check-pre-commit) is the integration point — this new check joins the others. - 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. - Close the 3 originating drafts with
completion_notepointing here.
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
plugin/validators/validate_task.py | modify | Add --body flag + AC-bullet-shape, gitignored-paths, and touchpoint-kind checks. Wraps parse_touchpoints.ts’s output. |
plugin/validators/tests/test_validate_task_body.py | new | Fixture suite for each new check kind. |
plugin/skills/task-new/SKILL.md | modify | Invoke validate_task.py --body after new_task.ts. Fail fast on malformed template. |
plugin/skills/task-define/SKILL.md | modify | Invoke validate_task.py --body advisorily between fill-in steps; gate at the end. |
lefthook.yml | modify | Add 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.md | modify | Step 3 continues to invoke validate_task.py --body as a redundancy net; prose notes the gate is now upstream. |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
plugin/validators/validate_task.py --body <task-path>exits non-zero with a structured finding for: (a) a Files-to-touch row whosekindis not one ofnew/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-newinvokes the body validator afternew_task.tsand surfaces any malformed-template finding immediately. - AC-4:
/sdlc:task-defineinvokes the body validator advisorily between fill-in steps and gates the final commit on it. - AC-5: A
git commitof 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_notepointing to this task; their status isclosed/superseded.
Out of scope
Section titled “Out of scope”- 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
--bodyflag’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.
Dependencies
Section titled “Dependencies”- 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.
Discovery context
Section titled “Discovery context”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:
- T-G39V-validate-task-body-shape-at-commit-time
- T-24ZN-task-authoring-validates-touchpoint-kinds
- T-CWLT-files-to-touch-gitignore-resolution-lint