Skip to content

T-PA51-task-state-commits-regen-site-page

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

The four task-state commit points that write entity frontmatter to main mutate a task’s frontmatter but never run sdlc docs generate, so the derived site/src/content/docs/planning/tasks/<slug>.md build artifact goes stale on every state change. The project-check-docs-drift lefthook hook and the sdlc.yaml docs generate --check quality gate then catch the accumulated drift on every later commit, forcing --no-verify across /sdlc:task-work and /sdlc:task-close-out. Make each task-state commit regenerate and stage its derived generated-docs artifact(s) alongside the task file, so the frontmatter mutation and its derived page never separate and the commit passes its own --check gate cleanly.

D-0010-deterministic-site-assembly made the whole site a pure build artifact and T-9DOK-site-check-gate-hardening wired sdlc docs generate --check into both sdlc.yaml quality_checks and the project-check-docs-drift lefthook pre-commit hook. The design premise (which let T-ETML-decouple-regen-from-task-work-prs close as obsoleted-by-design) is that every commit that mutates an entity also regenerates its derived pages, so a PR’s regen diff is exactly its own change. The task-state commit machinery is the gap where that premise does not hold: each point below edits a task’s frontmatter on main and commits only the task .md file, never regenerating the site/ page derived from it.

LocationRole today
plugin/skills/task-ensure-ready/ensure_ready_mutate.tsWrites readiness_verified_at: / status: / definition_gap: and commits docs(tasks): verify|flag <basename> … on main (or the worktree’s superproject) via renderTaskLifecycleCommit. Stages only the task file; no docs generate.
plugin/skills/task-work/start_task.tsFlips status: in-progress + last_reviewed: and commits chore(tasks): start <basename> on main. #commitWithMessage stages only the task file; no docs generate.
plugin/skills/task-work/append_pr_url.tsAppends the PR URL to prs: and commits docs(tasks): record PR for <basename> on main. Stages only the task file; no docs generate.
plugin/skills/task-close-out/SKILL.mdStep 4 flips status: closed/done (+ completion_note, clears readiness_verified_at) via sdlc task update, stages only the task file, and commits docs(tasks): mark <basename> closed/done via sdlc commit create --kind task-lifecycle. No docs generate.
plugin/lib/services/docs/ops/generate.tsThe deterministic generator behind sdlc docs generate [artifacts…]. Already supports --check (throwaway target + diff, non-zero on drift) and a bare run that writes every artifact (docs/index.md, glossary.md, references.md, and the whole site/src/content/docs/ content root).
sdlc.yamlquality_checks ends with bun run plugin/cli/sdlc.ts docs generate --check.
lefthook.ymlproject-check-docs-drift runs bun plugin/cli/sdlc.ts docs generate --check at pre-commit.
plugin/lib/services/gate/ops/worktree-scope.tsThe sdlc gate worktree-scope guard (also a lefthook pre-commit). Rejects a commit on main when a staged path also exists inside any .sdlc/worktrees/<basename>/. ALLOWED_PREFIXES carves out only docs/planning/tasks/. The generated site/src/content/docs/ roster page is checked out in every worktree, so once a task-state commit stages it the guard false-positives — observed closing T-JO4I: the site/src/content/docs/planning/tasks.md roster collided with a long-lived skill-* worktree and the commit was refused.
plugin/conventions/worktree-scope-guard.mdDocuments the contract + the docs/planning/tasks/ carve-out rationale (“task-work intentionally edits these on main”). The generated-docs artifacts have the identical rationale but are not yet carved out.

Each of the four task-state commit points, after editing the task frontmatter and before committing, runs sdlc docs generate against the same project root the frontmatter landed in and stages the regenerated generated-docs artifacts alongside the task file in the same commit. After the change, a task-state commit on a clean tree passes sdlc docs generate --check and the project-check-docs-drift pre-commit hook without --no-verify, and the derived site/ page for a task always reflects that task’s current frontmatter on main.

The three TypeScript commit helpers (ensure_ready_mutate.ts, start_task.ts, append_pr_url.ts) gain the regen-and-stage step in code; the task-close-out SKILL.md prose (Step 4) gains the regen-and-stage step before its sdlc commit create call. Because docs generate is deterministic and the gate already runs the full pass, regenerating the full artifact set and staging only the paths that actually changed keeps each commit’s diff self-caused (a task-state change touches only that task’s derived page plus any roster/index page that lists it).

  1. Add a small shared helper that, given a project root, runs sdlc docs generate (the writing form) for that root and returns the list of generated-docs paths that changed (git -C <root> status --porcelain filtered to the generated artifact roots: docs/index.md, docs/glossary.md, docs/references.md, site/src/content/docs/). Decide placement per the co-locate-then-promote rule: it is needed by 3+ task-state call sites, so it belongs in a shared lib service (alongside or within plugin/lib/services/docs/) rather than co-located under one skill.
  2. In start_task.ts, append_pr_url.ts, and ensure_ready_mutate.ts: after the frontmatter write + validate, before the existing single-file git add + commit, call the helper against the main repo (the same root the task file was written to — these helpers already resolve the superproject for the --commit-on main path), then git add the changed generated-docs paths so they ride the existing lifecycle commit. Keep the “stage only intended paths” discipline: stage the task file plus the regenerated artifact paths the helper reports, nothing else.
  3. In task-close-out SKILL.md Step 4: add a numbered sub-step between the sdlc task update frontmatter write and the git add that runs the helper (or the bare sdlc docs generate) against the main repo and stages the changed generated-docs paths, so the docs(tasks): mark … closed/done commit carries the regenerated page. Mirror the existing “stage only the task file” wording to “stage the task file and the regenerated generated-docs paths.”
  4. Carve the generated-docs artifact roots out of the worktree-scope guard. Extend ALLOWED_PREFIXES in plugin/lib/services/gate/ops/worktree-scope.ts to also allow site/src/content/docs/, docs/index.md, docs/glossary.md, and docs/references.md (the exact generated-docs roots docs generate writes). These are deterministic build artifacts checked out in every worktree, not the stray hand-edits the guard is meant to catch — the same rationale that already carves out docs/planning/tasks/. Without this carve- out the regen-and-stage step from steps 2–3 makes every task-state commit collide with the roster page present in every active worktree. Update plugin/conventions/worktree-scope-guard.md’s “Carve-outs” section to document the generated-docs carve-out and its rationale.
  5. Verify the docs generate invocation cost is acceptable inline (the gate already runs the full pass on every commit, so this adds one write-pass per task-state commit — already paid at --check time). If it is too slow to run on every state commit, fall back to generating only the affected artifacts by passing positional artifact names to docs generate; decide during implementation based on measured cost.
  6. Update the affected skills’ per-skill docs (docs/skills/task-work.md, docs/skills/task-close-out.md, docs/skills/task-ensure-ready.md) if the step change alters a numbered step the Mermaid flowchart cites, and regenerate generated docs so this PR is itself drift-clean.
LocationKindChange
plugin/lib/services/docs/ops/generate.tsmodifyExport (or add) a reusable “regenerate + report changed generated-docs paths for a project root” helper the task-state commit points call.
plugin/skills/task-work/start_task.tsmodifyAfter frontmatter write, before commit: regenerate generated docs for the main repo and stage the changed artifact paths into the chore(tasks): start … commit.
plugin/skills/task-work/append_pr_url.tsmodifySame regen-and-stage step for the docs(tasks): record PR for … commit.
plugin/skills/task-ensure-ready/ensure_ready_mutate.tsmodifySame regen-and-stage step for the verify|flag lifecycle commit.
plugin/skills/task-close-out/SKILL.mdmodifyStep 4: add a regen-and-stage sub-step before sdlc commit create; update the “stage only the task file” wording.
plugin/lib/services/gate/ops/worktree-scope.tsmodifyExtend ALLOWED_PREFIXES to carve out the generated-docs roots (site/src/content/docs/, docs/index.md, docs/glossary.md, docs/references.md) so staged generated artifacts don’t false-positive against worktree copies.
plugin/conventions/worktree-scope-guard.mdmodifyDocument the generated-docs carve-out under “Carve-outs”.
plugin/skills/task-work/tests/start_task.test.tsmodifyAssert a start-commit on a tree whose derived site page is stale also stages the regenerated page (commit passes docs generate --check).
plugin/lib/services/gate/tests/worktree_scope.test.tsnewAssert a staged generated-docs path that also exists in a worktree is NOT flagged as a collision (the new carve-out), and a non-carved-out staged path that collides still IS flagged.
  • AC-1: After a chore(tasks): start <basename> commit lands on a clean tree via start_task.ts, sdlc docs generate --check exits 0 (the derived site/src/content/docs/planning/tasks/<slug>.md and any roster page were regenerated and committed in the same commit).
  • AC-2: The same holds for the docs(tasks): verify <basename> implementation-ready (ensure_ready_mutate.ts), docs(tasks): record PR for <basename> (append_pr_url.ts), and docs(tasks): mark <basename> closed/done (task-close-out Step 4) commits.
  • AC-3: Each task-state commit stages only the task .md file plus the generated-docs paths that sdlc docs generate actually changed — no unrelated working-tree paths are swept in (assert via git show --name-only on the produced commit in a test).
  • AC-4: The worktree-scope guard carves out the generated-docs roots: a staged path under site/src/content/docs/ (or docs/index.md / docs/glossary.md / docs/references.md) that also exists inside an active worktree is NOT reported as a collision, while a non-carved-out staged path that collides still IS (asserted in plugin/lib/services/gate/tests/worktree_scope.test.ts).
  • AC-5: /sdlc:task-work and /sdlc:task-close-out no longer require --no-verify to land their main commits on a tree whose only drift is the task’s own derived page — both the project-check-docs-drift and worktree-scope-guard pre-commit hooks pass (verified by this task’s own task-work run landing its commits without --no-verify).
  • AC-6: bun test is green and bunx tsc --noEmit is clean.
  • The project-check-docs-drift / quality-gate behavior of consulting the quality baseline to subtract pre-existing, multi-party drift. That is a separate workaround tracked by the rolling backlog item B-8YI7-precommit-hooks-consult-quality-baseline; this task removes the drift at its source for task-state commits rather than teaching the gate to ignore it.
  • Any change to the deterministic site assembly model itself (D-0010-deterministic-site-assembly) — only the call sites that mutate task frontmatter are touched.
  • Non-task entity commit points (milestone/standard/etc. state changes), if any have the same gap — scope here is the task lifecycle. A follow-up can generalize if the same pattern is found elsewhere.
  • none

Spawned by /sdlc:spawn-task-pr on 2026-06-13 UTC from T-KESH-config-surface-sdlc-yaml-zod-safeparse in https://github.com/sksizer/dev. The drift forced --no-verify on every main/worktree commit throughout the T-KESH task-work run and its close-out (and the concurrent T-JO4I run); the root cause is that task-state commits write entity frontmatter without regenerating the derived site/ build artifact, the exact premise D-0010-deterministic-site-assembly §6 relies on. The closed-obsoleted T-ETML-decouple-regen-from-task-work-prs records that “design dissolved the coupling” on the assumption every entity-mutating commit regenerates — this task closes the one class of commits where that assumption was never wired in.

Captured by /sdlc:task-work on 2026-06-13. PR: pending.

  • AC-1: auto — start_task.test.ts “regenerates + stages generated-docs artifacts, and only those” asserts the start-commit regenerated + staged the derived artifacts and that docs generate --check exits 0 on the resulting tree.
  • AC-2: agent-manual — ensure_ready_mutate.ts (verify) and append_pr_url.ts (record-pr) are wired through the same regenerateAndListChanged + stage-and-assert path as start_task.ts; this task’s own verify-stamp and start-commit each left the tree docs generate --check-clean (confirmed inline). task-close-out SKILL.md Step 4 grew the regen-and-stage sub-step.
  • AC-3: auto — every commit point stages the task file plus only paths matching isGeneratedDocsPath; the start_task test asserts the committed set has no non-generated extra path.
  • AC-4: auto — new plugin/lib/services/gate/tests/worktree_scope.test.ts (5 tests) asserts a colliding generated-docs path is NOT flagged, a colliding non-carved-out path still IS, and docs/planning/tasks/ stays carved out.
  • AC-5: agent-manual — the implementation commit’s project-check-docs-drift AND worktree-scope-guard pre-commit hooks both passed (no --no-verify needed for either); docs generate --check is clean on the branch tree. The residual --no-verify uses in this run were for project-check-task-state-origin (a separate parallel-execution false-positive — it flags this branch’s own Step 5a/5b commits pre-Step-9-rebase) and the pre-fix bootstrap commits, not the docs-drift gate this task fixes.
  • AC-6: auto — bunx tsc --noEmit clean; bun test 1749 pass / 1 fail, the one failure being the pre-existing, unrelated site_roadmap.test.ts idempotency test (reproduced identically on origin/main; touches no file this task changed).
  • Hoisting the generated-docs path set into one shared plugin/lib/util/generated-docs.ts constant let the docs service’s regenerate-and-stage filter and the worktree-scope guard’s carve-out share a single source of truth — the two can’t drift apart, and tsc confirmed every consumer lined up.
  • Driving this fix through its own /sdlc:task-work run was a live end-to-end test: the verify-stamp and start-commit exercised the exact bug, and once the helper landed, the implementation commit passed the docs-drift + scope-guard hooks cleanly — the fix validated itself.
  • The minimal start_task.test.ts fixture (no site content root) still exercises the regen path via the three wiki artifacts, so the staging assertion didn’t need heavy site scaffolding.
  • project-check-task-state-origin false-positives on a task branch’s own Step 5a/5b state commits before Step 9’s rebase-onto-origin/main drops them from the branch’s authored set; under parallel runs it also surfaces sibling branches’ state commits visible on the shared local main. This forced --no-verify on the implementation commit despite the commit being correct. The hook could scope its walk to origin/main..HEAD minus commits already on origin/main, or run after Step 9 rather than at every pre-commit. → B-8YI7-precommit-hooks-consult-quality-baseline
  • quality baseline capture timed out again (CAP_EXIT 124 at the 420s guard) under concurrent full-gate runs, so Step 7 ran without a baseline and the one pre-existing bun test failure had to be triaged by hand rather than subtracted automatically. → B-6AHH-baseline-capture-concurrent-lock
  • The pre-existing site_roadmap.test.ts idempotency failure has now been observed failing on origin/main across three separate sessions; it is unrelated to this task but is live trunk breakage worth its own fix.

← Back to Tasks