From 1f0e2ab9123f9078b0c333efcf6471f8bdd4324f Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Thu, 23 Jul 2026 11:55:24 -0700 Subject: [PATCH 1/2] fix(finishing): check in with human partner when worktree removal hits untracked files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git worktree remove refuses when the tree holds modified or untracked files, and the skill gave no guidance for that refusal — the natural agent response was --force, permanently destroying files that exist nowhere else (uncommitted plans, notes, scratch work). Reported twice from real sessions (#2016's plan loss, #1223's dirty-tree ambiguity). Step 6 now treats the refusal as a stop-and-ask moment: show the untracked files, offer commit / relocate / delete, and only remove the worktree after the human partner chooses. Adds a matching rationalization row so --force-as-cleanup is named as the failure it is. --- .../finishing-a-development-branch/SKILL.md | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/skills/finishing-a-development-branch/SKILL.md b/skills/finishing-a-development-branch/SKILL.md index 21a439ac..4ca486be 100644 --- a/skills/finishing-a-development-branch/SKILL.md +++ b/skills/finishing-a-development-branch/SKILL.md @@ -174,6 +174,29 @@ git worktree remove "$WORKTREE_PATH" git worktree prune # Self-healing: clean up any stale registrations ``` +**If removal is refused** (`contains modified or untracked files`): the +worktree holds files that exist nowhere else — uncommitted plans, notes, +or scratch work. Never `--force` on your own initiative. Show your human +partner what is at stake and ask: + +```bash +git -C "$WORKTREE_PATH" status --porcelain +``` + +``` +Worktree removal refused — these files were never committed: + + + +1. Commit them to before cleanup +2. Move them into
+3. Delete them (unrecoverable) + +Which? +``` + +Carry out the choice, then remove the worktree. + **Otherwise:** The host environment owns this workspace — leave it in place. If your platform provides a workspace-exit tool, use it. @@ -196,6 +219,7 @@ place. If your platform provides a workspace-exit tool, use it. | "'Yeah, get rid of it' counts as confirmation" | Only the typed word `discard` authorizes deletion. | | "The PR is up, so the worktree is clutter now" | PR feedback gets fixed in that worktree. It stays until the work lands. | | "This other worktree looks stale — I'll clean it too" | Clean up only worktrees under `.worktrees/` or `worktrees/`. Everything else belongs to the host. | +| "Removal refused — `--force` is just finishing the cleanup" | The refusal means files exist only in that worktree. `--force` destroys them permanently. Show your human partner and ask. | | "The merged-result failure is probably flaky" | A failing merged result stops everything. Branch and worktree stay put while you investigate. | | "The base branch is obviously main" | Confirm the fork point or ask. Merging into the wrong base is expensive to undo. | | "The push was rejected — force-push will fix it" | A rejected push means the remote moved. Investigate; force-push only on your human partner's explicit request. | From 17b42c8128919944c58d581b1ec690dba3c35655 Mon Sep 17 00:00:00 2001 From: Drew Ritter Date: Thu, 6 Aug 2026 12:14:55 -0700 Subject: [PATCH 2/2] fix(finishing): name the actual files in the refusal prompt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `git status --porcelain` collapses a wholly-untracked directory to a single `?? docs/` line. In the shape of the incident this step exists for (#2016 — an uncommitted plan document under an untracked `docs/` tree), the file list we show the human partner therefore names no file at all: $ git -C "$WORKTREE_PATH" status --porcelain ?? docs/ $ git -C "$WORKTREE_PATH" status --porcelain -uall ?? docs/superpowers/plans/2026-08-04-csv-export-rollout.md Both forms produce identical (empty) output on a clean worktree, so this adds no over-trigger surface. Found while running this PR's behavioral micro-tests. Every treatment agent dug past `?? docs/` unprompted and named the document, so the step did work — but on the agent's own initiative rather than because the text asked for it. That initiative is not reliable one tier down: Claude Haiku 4.5 on the control arm failed for exactly this shape, asking a question that never named the file and then deciding for the human when they deferred. Nothing in the prior wording stopped a treatment agent from relaying `?? docs/` verbatim and satisfying the letter of the instruction. Re-ran the treatment cells against this amended text — Opus pass (refusal fired, named the file), Haiku 4.5 pass (named the file) — no regression. Co-Authored-By: Claude Opus 5 (1M context) --- skills/finishing-a-development-branch/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/finishing-a-development-branch/SKILL.md b/skills/finishing-a-development-branch/SKILL.md index 4ca486be..fa8aecaf 100644 --- a/skills/finishing-a-development-branch/SKILL.md +++ b/skills/finishing-a-development-branch/SKILL.md @@ -180,7 +180,7 @@ or scratch work. Never `--force` on your own initiative. Show your human partner what is at stake and ask: ```bash -git -C "$WORKTREE_PATH" status --porcelain +git -C "$WORKTREE_PATH" status --porcelain -uall ``` ```