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-pluginsafter the worktree’splugin/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-runflag 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.
Proposed
Section titled “Proposed”/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.
Approach
Section titled “Approach”- Add
--dry-runto the skill’s argument contract inplugin/skills/task-close-out/SKILL.mdStep 1. - In Steps 4–6, branch on the flag: when set, print-only; otherwise existing behavior.
- Add invariants under
plugin/skills/task-close-out/invariants.yamlfor the three properties above (forbidden phrases inside the--dry-runbranch, requiredDRY-RUNmarker prefix on the emitted line).
Files to touch
Section titled “Files to touch”plugin/skills/task-close-out/SKILL.md— add--dry-runto the argument contract and branch Steps 4–6.plugin/skills/task-close-out/invariants.yaml— pin the three load-bearing properties.
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: Invoking
/sdlc:task-close-out <slug> --dry-runagainst anin-progresstask with a merged PR prints the frontmatter diff, the commit/push commands, the worktree teardown command, and the branch-deletion commands — and emitsDRY-RUN ok pr=#<N>. No files are modified, no commits, no branch deletions. (agent-manual; verifiable viagit status+git logbefore/after) - AC-2: Invoking without
--dry-runis 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.pypasses for the new invariants. (auto)
Out of scope
Section titled “Out of scope”- A wider rehearsal mode that covers
/sdlc:task-workas 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.
Dependencies
Section titled “Dependencies”- none
Discovery context
Section titled “Discovery context”Spawned by /sdlc:task-work post-mortem of T-RDKI-extract-task-close-out-skill on 2026-05-20.
Post-mortem
Section titled “Post-mortem”Captured by /sdlc:task-work on 2026-05-20. PR: pending.
Acceptance criteria coverage
Section titled “Acceptance criteria coverage”- AC-1: agent-manual — prose contract verified by reading the new
### --dry-run rehearsal modeH3 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 theDRY-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.mdshows 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.pypasses against the newinvariants.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.
What worked
Section titled “What worked”- 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_phrasesmap 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.
Friction and automation gaps
Section titled “Friction and automation gaps”just full-checkandjust ciare referenced in this skill’s Step 7 contract, but the repo has nojustfile. The skill should detect a missingjusttoolchain 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 modeH3 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 dispatchwould read more naturally; deferred because promoting heading levels mid-skill is a structural change with broader review surface. (skipped — judged non-actionable; cosmetic-only)
Spawned follow-up tasks
Section titled “Spawned follow-up tasks”2026-05-21-investigate-skill-prompt-freshness— linked existing (covers the stale-prompt root cause of thejust full-checksymptom)