Skip to content

T-Y81J-task-close-out-dry-run-flag

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

Auto-generated from a /sdlc:task-work post-mortem. Review and promote to open/ready before picking up.

/sdlc:task-close-out performs irreversible side effects (frontmatter edits + commit on main, worktree teardown, local + remote branch deletion). Exercising the skill end-to-end against real state requires a throwaway fixture or a willingness to re-do the close-out by hand if anything goes wrong, which makes testing painful and discourages running the skill from automation (e.g. the future orchestrator) without extra guards. A --dry-run flag that prints every side effect without executing any of them would let close-out be rehearsed against any in-progress task safely — useful for spot-checks, for the orchestrator’s pre-execution dry run, and for harness coverage of the close-out path that doesn’t depend on a synthetic GitHub fixture. Closing this gap reduces the cost of trusting the skill, which is a prerequisite for T-FQCN-self-driving-orchestrator-loop’s orchestrator dispatching it autonomously.

From the originating post-mortem:

The skill cannot be exercised end-to-end without /reload-plugins after the worktree’s plugin/skills/task-close-out/ exists, and even then the skill’s mutations (branch delete, worktree teardown) are destructive enough that real live testing requires throwaway fixtures. A --dry-run flag that printed each side effect without executing would let close-out be exercised against any in-progress task safely.

plugin/skills/task-close-out/SKILL.md has no rehearsal mode. Every invocation runs Steps 4–6 (file edit + commit, worktree teardown, branch deletion) for real if Step 2’s eligibility gate and Step 3’s PR detection pass.

/sdlc:task-close-out <slug> --dry-run runs the full procedure through Step 3 unchanged, then for Steps 4–6 prints each side effect (the unified diff for the frontmatter edit, the exact git commit / git push / git worktree remove / git branch -d / git push origin --delete commands) and exits with a DRY-RUN ok pr=#<N> marker instead of DONE. No file is modified, no commit is created, no branch is touched.

The invariants linter pins three properties: (a) --dry-run never calls git commit, git push, git worktree remove, or git branch -d; (b) the diff that would be applied is printed in unified-diff form so a human can eyeball it; (c) the emitted marker uses the literal DRY-RUN prefix so callers (orchestrator, harness) can branch on it.

  1. Add --dry-run to the skill’s argument contract in plugin/skills/task-close-out/SKILL.md Step 1.
  2. In Steps 4–6, branch on the flag: when set, print-only; otherwise existing behavior.
  3. Add invariants under plugin/skills/task-close-out/invariants.yaml for the three properties above (forbidden phrases inside the --dry-run branch, required DRY-RUN marker prefix on the emitted line).
  • plugin/skills/task-close-out/SKILL.md — add --dry-run to the argument contract and branch Steps 4–6.
  • plugin/skills/task-close-out/invariants.yaml — pin the three load-bearing properties.
  • AC-1: Invoking /sdlc:task-close-out <slug> --dry-run against an in-progress task with a merged PR prints the frontmatter diff, the commit/push commands, the worktree teardown command, and the branch-deletion commands — and emits DRY-RUN ok pr=#<N>. No files are modified, no commits, no branch deletions. (agent-manual; verifiable via git status + git log before/after)
  • AC-2: Invoking without --dry-run is unchanged from today’s behavior. (auto via lint that the non-dry-run path is byte-identical aside from the flag branch)
  • AC-3: lint_skill_prose.py passes for the new invariants. (auto)
  • A wider rehearsal mode that covers /sdlc:task-work as a whole. Start with close-out; extend later if useful.
  • Persisting the dry-run output to a file. Stdout is enough for rehearsal; harness can capture as needed.
  • none

Spawned by /sdlc:task-work post-mortem of T-RDKI-extract-task-close-out-skill on 2026-05-20.

Captured by /sdlc:task-work on 2026-05-20. PR: pending.

  • AC-1: agent-manual — prose contract verified by reading the new ### --dry-run rehearsal mode H3 between Step 3 and Step 4: it documents the print-only behavior (unified diff for the frontmatter edit, verbatim commands for the destructive ops), forbids the destructive command set, and prescribes the DRY-RUN ok pr=#<N> exit marker. Full end-to-end execution against a real in-progress merged PR was not exercised this run because the only in-flight task (this one) hasn’t merged; the rehearsal contract is invocation-shaped (LLM interprets SKILL.md at call time), so the prose-level coverage is the artifact under review.
  • AC-2: auto — git diff main -- plugin/skills/task-close-out/SKILL.md shows the changes are pure additions: a new Usage bullet, a new Output-contract bullet, a new H3 between Step 3 and Step 4, and a one-paragraph skip-note prepended to Step 7. No existing prose in Steps 4-6 was modified.
  • AC-3: auto — lint_skill_prose.py passes against the new invariants.yaml (three new required_phrases for the DRY-RUN marker, the MUST-NOT-execute guard, and the unified-diff requirement); all 20 skills pass with no violations.
  • The invariants-linter shape pinned exactly the load-bearing prose the task spec called out (marker text, MUST-NOT guard, unified-diff requirement). The three new required_phrases map 1:1 to the spec’s three invariants.
  • Placing the rehearsal-mode guard as an H3 between Step 3 and Step 4 let Steps 4-6’s existing prose stay byte-stable, which keeps AC-2’s “byte-identical aside from the flag branch” promise structurally rather than per-line.
  • just full-check and just ci are referenced in this skill’s Step 7 contract, but the repo has no justfile. The skill should detect a missing just toolchain and fall back to project-native gates (here: lint_skill_prose.py + validate_frontmatter.py) with a recorded note, instead of attempting to invoke commands that aren’t there. Today the agent has to notice and work around it. → 2026-05-21-investigate-skill-prompt-freshness (the per-project quality_checks fix already shipped in T-T879-task-work-uses-per-project-quality-checks; the live skill prose loaded for this run was stale, which is exactly the freshness investigation’s scope).
  • The ### --dry-run rehearsal mode H3 lives under the Step 3 H2 but logically gates Steps 4-6. The lint section-matching is H3-aware, so the invariants resolve correctly, but a future reader may find the placement non-obvious. An explicit H2 like ## 3.5. Rehearsal mode dispatch would read more naturally; deferred because promoting heading levels mid-skill is a structural change with broader review surface. (skipped — judged non-actionable; cosmetic-only)
  • 2026-05-21-investigate-skill-prompt-freshness — linked existing (covers the stale-prompt root cause of the just full-check symptom)

← Back to Tasks