T-902K-task-transition-op-owns-commit-side-effect
Status: closed/done · Impact: medium · Complexity: medium
PR #286 review (2026-06-04): “should this instead be an operation on the task entity — as in we reach some point where the task is transitioning and one of the (desired) side effects is a commit attempt?” Today a task lifecycle transition is three separately-orchestrated actions — mutate frontmatter, stage, commit with the canonical kind message — composed per call site (scripts compose all three in TS; skills compose them as sequential shell steps). One entity op per transition would make the commit a declared side effect of the state change instead of a step a caller can forget, reorder, or interleave with unrelated staging.
| Location | Role today |
|---|---|
plugin/skills/task-work/start_task.ts | start transition: flips frontmatter, stages, commits (via renderTaskLifecycleCommit), CAS-REPLACEs the lease — all hand-composed in one script |
plugin/skills/task-ensure-ready/ensure_ready_mutate.ts | verify-ready / flag-needs-definition transitions: same hand-composed shape |
plugin/skills/task-work/append_pr_url.ts | record-pr transition: same |
plugin/lib/model/entities/task/ops/update.ts | sdlc task update — schema-gated frontmatter mutation, deliberately commit-free |
plugin/lib/services/commit/ops/create.ts | sdlc commit create — commit, deliberately mutation-free |
plugin/skills/task-close-out/SKILL.md | close-done transition: LLM orchestrates update → validate → stage → kind-commit as sequential prose steps 3–6 |
plugin/lib/model/entities/task/commits/lifecycle/schema.ts | the action enum a transition op would key on |
Proposed
Section titled “Proposed”sdlc task transition <basename> --action <lifecycle-action> [--detail …]
(an entity op at entities/task/ops/transition.ts): performs the
action’s frontmatter mutation (status flip, stamps) via the task update
core, stages exactly the task file, and commits via the task-lifecycle
kind — one atomic verb per transition, with --dry-run reporting the
whole plan. The three lifecycle scripts and task-close-out’s steps 3–6
converge on it; task update and commit create remain as the
lower-level primitives it composes.
Approach
Section titled “Approach”- Decide the op surface: one
transitionverb keyed on the lifecycle action enum vs per-action verbs. Decide which transitions are in scope v1 (close-done/close-obsoleted are the cleanest; start is entangled with lease CAS + worktree reset, likely later). - Map each in-scope action to its frontmatter mutation (status, dates, completion_note) — single-sourced beside the action enum in the lifecycle kind package.
- Implement the op composing update-core → stage (ctx.git) → kind-commit; surface a structured result (changed fields, sha).
- Re-point task-close-out steps 3–6 at the verb; converge append_pr_url.ts if its record-pr fits cleanly.
- Defer start/verify transitions if lease coupling makes them awkward — record what blocks them.
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
plugin/lib/model/entities/task/ops/transition.ts | new | the transition op |
plugin/lib/model/entities/task/commits/lifecycle/schema.ts | modify | co-locate per-action frontmatter-mutation map |
plugin/lib/model/entities/task/ops/update.ts | modify | export the merge/validate core for composition |
plugin/skills/task-close-out/SKILL.md | modify | steps 3–6 → one transition call |
plugin/skills/task-work/append_pr_url.ts | modify | converge on the op if clean |
plugin/lib/model/entities/task/ops/tests/transition.test.ts | new | acceptance suite |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
sdlc task transition <basename> --action close-done --detail "Shipped via #N."flips status, stamps required fields, and lands exactly one commit whose subject is the canonical kind shape — with only the task file staged. - AC-2: a schema-invalid resulting frontmatter aborts before any stage/commit; the file is byte-untouched.
- AC-3:
--dry-runreports mutation + commit plan, writes nothing. - AC-4: task-close-out SKILL.md no longer sequences update/stage/commit as separate steps for the close transition.
- AC-5:
bun testandbunx tsc --noEmitclean.
Out of scope
Section titled “Out of scope”- The start transition’s lease CAS / worktree-reset coupling (start_task.ts keeps its composition until the lease seam is settled).
- Generalizing to non-task entities.
Dependencies
Section titled “Dependencies”- T-D7PW-templatized-commit-kinds (PR #286) — the lifecycle kind and both primitive verbs this op composes.
Discovery context
Section titled “Discovery context”Inline review comment on PR #286 (plugin/skills/task-work/SKILL.md:772, the post-mortem commit block): transitions should be entity operations whose desired side effect is the commit attempt, rather than skill-side sequencing of mutation, staging, and commit.