Skip to content

T-D7PW-templatized-commit-kinds

Status: closed/done · Impact: medium · Complexity: medium

The commit corpus is already machine-shaped — chore(tasks): start T-…, docs(tasks): verify T-… implementation-ready, docs(tasks): record PR for T-… account for the bulk of recent history — but every one of those subjects is composed by an LLM following SKILL.md prose, and several are pinned verbatim in invariants files. Give commit messages the same treatment reports (sdlc report render) and PR bodies (T-UZ7T-pr-bodies-as-report-style-eta-kinds) get: a Zod contract + Eta template per commit kind, rendered deterministically, so skills pass structured params instead of recalling prose shapes, and a subject-shape change is a one-file template edit instead of a cross-skill sweep.

LocationRole today
plugin/conventions/commit-messages.mdCanonical safe routing (tempfile + git commit -F; PR #281 names sdlc_commit.ts as preferred). Says nothing about message content — every subject shape lives in skill prose.
plugin/skills/task-work/SKILL.mdComposes chore(tasks): start <basename>, docs(tasks): record PR for <basename>, post-mortem commit subjects in prose
plugin/skills/task-ensure-ready/SKILL.mdComposes docs(tasks): verify <basename> implementation-ready / flag <basename> as needs-definition in prose
plugin/skills/task-close-out/SKILL.mdComposes docs(tasks): mark <basename> closed/done in prose
plugin/skills/task-work/invariants.yamlPins the literal phrase docs(tasks): verify <basename> implementation-ready
plugin/skills/task-close-out/invariants.yamlPins docs(tasks): record PR ... phrasing
plugin/lib/services/report/render.tsThe schema-contracted render engine (validate → Eta) this task reuses for plain-text output
plugin/lib/model/entities/task/reports/review/schema.tsPrecedent: entity-specific kind package consumed by a lib service

sdlc_commit.ts (lands via T-SPBO-commit-helper-routes-through-tempfile / PR #281) grows a --kind <slug> mode: stdin carries a JSON payload instead of a raw message, the helper validates it against the kind’s zod/v4 contract, renders the kind’s Eta template (plain text, autoEscape off) to the message, and proceeds through its existing tempfile + git commit -F tail. Kind packages follow the PR #278 placement rule: the task-lifecycle kind lives at plugin/lib/model/entities/task/commits/lifecycle/{schema.ts,template.eta} with a payload like {action: start|verify-ready|flag-needs-definition| record-pr|close-done|close-obsoleted|post-mortem, basename, detail?}; the registry and any shared machinery live beside the report service. Raw-message mode (--message -) survives unchanged as the freeform escape hatch. The skills that compose those subjects switch to one-line --kind calls; the invariants that pin subjects keep passing because the template emits the identical shapes — they now pin the template’s output, which has a single source.

  1. Extract or reuse the validate→render seam from plugin/lib/services/report/render.ts for plain-text output (same decision point as T-UZ7T-pr-bodies-as-report-style-eta-kinds Approach step 1 — whichever task lands second consumes the extraction the first one made).
  2. Create plugin/lib/services/commit/ (kind registry + render entry; no new CLI noun yet — sdlc_commit.ts is the door) and the entities/task/commits/lifecycle/ kind package. commits/ is a sibling of ops/, never inside it (the walk imports everything in ops/ as op modules).
  3. Extend sdlc_commit.ts: --kind <slug> + stdin-JSON mode; contract violations exit 2 with path-precise Zod issues; unknown kind exits 2 listing registered kinds. Raw mode untouched.
  4. Wire the three pilot skills (task-work, task-ensure-ready, task-close-out): replace composed-subject prose with the --kind call. Verify the pinned phrases in both invariants.yaml files still match the rendered output byte-for-byte; update the invariant notes to name the template as the source of the shape.
  5. Tests: each lifecycle action renders its exact historical subject shape; payload round-trips metacharacters; invalid payload / unknown kind exit 2; raw mode regression-covered by PR #281’s existing suite.
  6. Update plugin/conventions/commit-messages.md: kinds are the preferred shape for stereotyped messages, raw stdin for one-offs, heredoc fallback only where bun is unreachable.
LocationKindChange
plugin/scripts/sdlc_commit.tsmodifyadd --kind + stdin-JSON mode (file lands via PR #281 — see Dependencies)
plugin/lib/services/commit/newkind registry + plain-text render entry
plugin/lib/model/entities/task/commits/lifecycle/schema.tsnewlifecycle payload contract (action enum, basename, detail)
plugin/lib/model/entities/task/commits/lifecycle/template.etanewsubject/body shapes for each action
plugin/lib/services/report/render.tsmodifyextract shared validate→render seam (skip if T-UZ7T extracted it first)
plugin/skills/task-work/SKILL.mdmodifycomposed subjects → --kind calls
plugin/skills/task-ensure-ready/SKILL.mdmodifycomposed subjects → --kind calls
plugin/skills/task-close-out/SKILL.mdmodifycomposed subjects → --kind calls
plugin/skills/task-work/invariants.yamlmodifynote the template as the pinned phrase’s source
plugin/skills/task-close-out/invariants.yamlmodifysame
plugin/conventions/commit-messages.mdmodifykinds preferred for stereotyped messages
plugin/scripts/tests/sdlc_commit.test.tsmodifykind-mode coverage
  • AC-1: echo '{"action":"start","basename":"T-0001"}' | sdlc_commit --kind task-lifecycle produces a commit whose subject is exactly chore(tasks): start T-0001; each enum action reproduces its historical subject shape byte-for-byte (snapshot test).
  • AC-2: invalid payload or unknown kind exits 2 with the Zod issues / the registered-kind list; no commit is created.
  • AC-3: raw --message - mode behaves exactly as shipped by PR #281 (existing tests untouched and green).
  • AC-4: the three pilot SKILL.mds contain no composed commit-subject prose for lifecycle actions; both invariants.yaml pinned phrases still match rendered output.
  • AC-5: bun test and bunx tsc --noEmit clean.
  • Kinds for non-task commit families (backlog capture, entities-migrate stamps, index regen) — same recipe once lifecycle proves out.
  • A sdlc commit registry noun — the helper script stays the single door until a second non-CLI consumer appears.
  • PR-body templating — that’s T-UZ7T-pr-bodies-as-report-style-eta-kinds.

User direction (2026-06-04 session): “I actually wanted to parameterize and templatize all commit messages anyway,” upon rediscovering in-flight T-SPBO-commit-helper-routes-through-tempfile (PR #281) while designing a commit helper from scratch. Third leg of the schema-contracted rendering family: reports (PR #277/#278), PR bodies (T-UZ7T-pr-bodies-as-report-style-eta-kinds), commit messages (this). Corpus shape data: 10× verify … implementation-ready, 10× start …, 7× record PR for … in the last 40 commits.

T-SPBO-commit-helper-routes-through-tempfile


← Back to Tasks