Skip to content

T-QOL7-gap-report-runs-before-lease-acquire

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

AUTO-DEFINED: this spec was best-effort machine-authored by /sdlc:task-auto-define on 2026-07-19 because the task is autonomy: autonomous/pr. Review the Goal, Approach, Today, Files-to-touch, and Acceptance-criteria carefully before trusting it.

/sdlc:task-work runs its touchpoint-drift check too late: sdlc task gap-report fires only after Step 2a has taken the lifecycle lease and Step 4 has built the worktree, so drift is caught after irreversible side effects already landed. Moving the call into Step 2’s relevance check — before the lease acquire — makes drift a cheap, side-effect-free early exit.

Reported from T-J5DI-remove-clear-duplicate-augmented-packages in https://github.com/sksizer/dev:

/sdlc:task-work runs sdlc task gap-report after Step 2a acquires the lifecycle lease and Step 4 builds the worktree. During T-J5DI the task cited packages/ts/obsidian-bases-augment/ in both ## Today and ## Files to touch, but commit 09d8f775 had already deleted it. gap-report flagged it directory-missing correctly — but only after the lease and worktree existed, so a scope-narrowing commit had to land on origin/main mid-flow. Move the gap-report call into Step 2’s relevance check, before Step 2a’s lease acquire, so touchpoint drift is caught before any side effect. Touchpoints: apps/sdlc/skills/task-work/SKILL.md (Step 2 / Step 2a ordering) and apps/sdlc/lib/model/entities/task/ops/gap-report.ts.

LocationRole today
apps/sdlc/skills/task-work/SKILL.mdStep 2’s “Relevance check (the important one)” is a hand-rolled LLM sweep — regex-extract paths, ls/Read each, command grep cited symbols. It never calls sdlc task gap-report. Step 2a then acquires the lifecycle lease and Step 4 creates the worktree.
apps/sdlc/lib/model/entities/task/ops/gap-report.tsThe deterministic, read-only, no-LLM readiness-gap composite. Its touchpoints.unresolved[] and touchpoints.parse_errors[] are exactly the touchpoint-drift facts Step 2’s relevance check is trying to derive by hand, and it mutates nothing, so it is safe to run before any side effect.
apps/sdlc/skills/task-ensure-ready/SKILL.mdThe only gap-report caller reachable from the task-work flow — invoked at task-work Step 5a, i.e. after the lease (2a) and the worktree (4) already exist.
apps/sdlc/lib/services/gate/ops/_skill_prose_contract.ts#SKILL_PROSE_REGISTRYPins task-work’s prose invariants per section. The "2. Pre-flight: verify task is workable" entry pins only the verify-stamp commit subject (skill-prose/task-work/s9); nothing pins a pre-lease drift check, so the ordering can silently regress.
docs/skills/task-work.mdPer-skill doc. Its Mermaid flowchart labels the relevance node Relevance check: ls cited paths; command grep cited symbols and arrows into 2a. Acquire the lease.

Step 2’s relevance check opens with one deterministic sdlc task gap-report <path> --output json call. A non-empty touchpoints.unresolved[] or touchpoints.parse_errors[] feeds the step’s existing “materially out of date” AskUserQuestion branch, which already returns before Step 2a. Touchpoint drift then costs one read-only op call and zero side effects — no lease ref at refs/sdlc/tasks/<basename>, no worktree, no task branch, and no mid-flow scope-narrowing commit on origin/main.

Step 5a’s /sdlc:task-ensure-ready keeps running the same composite as the authoritative, stamping gate; the Step 2 call is an early advisory read that writes no frontmatter.

  1. In apps/sdlc/skills/task-work/SKILL.md, rewrite the numbered list under “Relevance check (the important one)” so its first item is the deterministic composite, run un-piped so the exit code propagates: ${CLAUDE_PLUGIN_ROOT}cli/sdlc task gap-report <task-path> --output json. Read touchpoints.unresolved[] (cited Location missing from the working tree) and touchpoints.parse_errors[] (malformed row, symbol-on-glob, missing column) as the authoritative drift facts.
  2. Demote the existing ls / Read / command grep items to the interpretive follow-up the op deliberately does not cover — the “currently does X” prose claims and the relevance_note spot-check — rather than re-deriving path existence the op already decided.
  3. Route a non-empty touchpoints.unresolved[] into the step’s existing out-of-date AskUserQuestion (update the doc / close the task with a completion_note / stop), and state in that prose that the branch returns before Step 2a, so no lease or worktree exists yet.
  4. Add one sentence to the new Step 2 text marking the call advisory: it does not stamp readiness_verified_at: and does not replace Step 5a’s /sdlc:task-ensure-ready gate, which re-runs the same composite authoritatively.
  5. Add one sentence to Step 2a’s opening paragraph naming Step 2’s gap-report as the last read-only check before the first side effect, so the ordering invariant is stated on both sides.
  6. In apps/sdlc/lib/services/gate/ops/_skill_prose_contract.ts, add a requires entry to the "2. Pre-flight: verify task is workable" section of SKILL_PROSE_REGISTRY["task-work"]: pattern task gap-report, id skill-prose/task-work/s16 (the next free id — s15 is the highest in use), and a note explaining that the drift check must stay in Step 2 ahead of Step 2a’s lease acquire.
  7. Refresh docs/skills/task-work.md so the Mermaid relevance node names the sdlc task gap-report call upstream of the 2a. Acquire the lease node. /sdlc:update-skill-doc task-work is the canonical way to regenerate it.
  8. Verify: sdlc gate skill-prose --paths apps/sdlc/skills/task-work/SKILL.md exits 0, and bun test apps/sdlc/lib/services/gate/ops/_skill_prose_contract.test.ts passes.
LocationKindChange
apps/sdlc/skills/task-work/SKILL.mdmodifyMove the deterministic touchpoint-drift check into Step 2’s relevance check as its first item; demote the hand-rolled path/symbol sweep to the interpretive follow-up; route drift into the existing out-of-date branch; note the call is advisory; name the ordering invariant in Step 2a.
apps/sdlc/lib/services/gate/ops/_skill_prose_contract.tsmodifyAdd the task gap-report requires pin (skill-prose/task-work/s16) under the task-work entry’s "2. Pre-flight: verify task is workable" section.
docs/skills/task-work.mdmodifyRefresh the Mermaid relevance node to name the gap-report call ahead of the lease-acquire node.
  • AC-1: apps/sdlc/skills/task-work/SKILL.md contains the literal string task gap-report inside the “Relevance check (the important one)” subsection of Step 2, at a lower line number than the ## 2a. Acquire the lease heading.
  • AC-2: That invocation is written un-piped (no | tail, no | head), per the “Don’t pipe commands you gate on” rule in apps/sdlc/skills/CLAUDE.md.
  • AC-3: Step 2’s out-of-date prose names touchpoints.unresolved[] as a trigger for the existing AskUserQuestion branch and states that the branch returns before any lease acquire.
  • AC-4: Step 2’s new text states the call does not stamp readiness_verified_at:, and Step 5a still routes readiness through /sdlc:task-ensure-ready.
  • AC-5: SKILL_PROSE_REGISTRY["task-work"]’s "2. Pre-flight: verify task is workable" section carries a requires entry with pattern task gap-report and id skill-prose/task-work/s16.
  • AC-6: sdlc gate skill-prose --paths apps/sdlc/skills/task-work/SKILL.md exits 0 and reports no violation.
  • AC-7: bun test apps/sdlc/lib/services/gate/ops/_skill_prose_contract.test.ts passes.
  • AC-8: docs/skills/task-work.md’s Mermaid flowchart names the gap-report call in the relevance node, and that node still arrows into the 2a. Acquire the lease node.
  • Changing apps/sdlc/lib/model/entities/task/ops/gap-report.ts — the op already emits the needed facts read-only; only its call site moves.
  • Removing or weakening Step 5a’s /sdlc:task-ensure-ready gate. The Step 2 call is additive and advisory; the stamping gate stays.
  • Folding the drift check into sdlc task probe-state (the Step 2 pre-flight probe). That would be a second op-composition change; the early call stays a separate shell-out.
  • Auto-repairing drifted touchpoint rows. The out-of-date branch stays a human decision via AskUserQuestion.
  • none

Spawned by /sdlc:spawn-task-pr on 2026-07-20 UTC from T-J5DI-remove-clear-duplicate-augmented-packages in https://github.com/sksizer/dev.


← Back to Tasks