# Worktree finalize  -  removing a task's worktree once its PR is open

> **TLDR**  -  Phase 6 step 9. Once the PR exists the worktree is dead weight, so it is removed: artefacts are salvaged into the log dir first, the branch is kept and deliberately NOT checked out, and every destructive path is gated. Gated by `prefs.global.settings.worktreeAutoRemoveOnPr` (default **true**). Script: `worktree-finalize.sh`.

## Why it exists

A finished task's worktree is a full second checkout that nobody needs after the PR is open, and removing it is the step people forget. `.worktrees/` then accumulates copies of the repo until `/multi-agent:kill` or `:garbage-collect` is run by hand.

Removing it at PR-open is only safe because of the salvage, so the two are one step and not two.

## What it will not do

**No `git checkout` of the task branch.** `git worktree remove` leaves the branch as an ordinary local branch: the ref, its commits, and the ability to `git checkout <branch>` later all survive untouched. Checking it out here would move the user's HEAD out from under them and can collide with their own uncommitted work on another branch. Phase 5 removes-then-checks-out on purpose, because it is handing the branch over for manual testing; this step is not.

**No `git branch -D`.** The branch is the deliverable.

**No `--force`, ever.** `git worktree remove` refusing is a safety feature. The clean-tree check runs before it, so a refusal at that point means something unexpected (a lock, a submodule, permissions) and forcing past unexpected dirt is how work gets lost.

## Preconditions  -  each one skips with a reason, none is an error

| Condition | Why it blocks |
|---|---|
| `worktreePath == projectRoot` (`--local` mode) | there is no worktree; removing it would delete the user's checkout |
| cwd is inside the worktree | a shell left on a deleted inode is worse than a leftover directory, and Phase 6 legitimately `cd`s into the worktree earlier |
| not a registered worktree of the project root | a mistyped path must not delete an unrelated directory |
| real uncommitted changes | never discarded; see the artefact carve-out below |
| HEAD not on the remote | removing a worktree whose commits exist nowhere else is data loss, not cleanup |

Exit codes: `0` removed, `3` skipped with a reason (report and continue to Phase 7), `1` usage error.

### The artefact carve-out, and why `--untracked-files=no` is wrong

The pipeline's own artefacts live inside the worktree and are untracked, so a raw `git status --porcelain` is never empty at PR-open. Left unhandled the removal would never fire and the feature would look implemented while doing nothing.

So exactly these paths are forgiven, and nothing else:

```
agent-state.json  phase-tracker.json  triage-output.json
.review-diff.txt  .build.log  .test.log  .pipeline/
```

Suppressing all untracked files instead (`--untracked-files=no`) would have been shorter and wrong: a source file the developer created but never `git add`ed is invisible to it, and that file would be destroyed silently.

## Salvage

Copied into `$HOME/.claude/logs/multi-agent/<project>/<task-id>/artifacts/` before removal, each only if present  -  a task that never reached Phase 4 has no triage output and that is not an error.

This is why the removal is safe:

| Consumer | Reads | Without salvage |
|---|---|---|
| Phase 7 triage-memory ingest | `triage-output.json` | `[ -f ]`-guarded, so it degrades **silently**: the triage corpus and learnings ledger stop being fed and no error appears |
| Phase 7 learnings-ledger distill | same file | same silent degradation |
| `render-work-summary.sh` | `agent-state.json`, `phase-tracker.json` (falls back to `logs/multi-agent/<task>/tracker-state.json`, which survives removal) | loses the salvaged copies but keeps the tracker via the logs fallback |
| `:resume` | `agent-state.json` | cannot continue a Phase 7 pause |
| `:status`, `:log` | `agent-state.json` | the task becomes invisible |

`state.worktreeRemovedAt` and `state.artifactsPath` record the outcome. The timestamp is what tells a reader that a worktree-less task was finished-and-tidied rather than killed  -  without it, a missing worktree is indistinguishable from a broken run.

## Multi-repo

Run serially per repo, and only **after** `update_sibling_links`: that function issues an update per PR and the loop `cd`s per repo, so removing repo 1's worktree mid-loop breaks repos 2..N.

## Interaction with the existing removal sites

`gc-worktrees.sh` and `/multi-agent:garbage-collect` never touch a registered, healthy worktree  -  they sweep orphans. Both name this step as the owner of finishing-a-task removal, which is now true rather than aspirational.
