---
name: done
description: Lands finished Git worktrees or jj workspaces, publishes, proves remote equality, and cleans up.
---

# Done

Close the current repository task completely. Preserve unrelated work.

## Completion invariant

A task is done only when:

1. Task changes are shaped into reviewable, green Git commits or jj changes.
2. The task revision is an ancestor of the repository's actual default branch/bookmark.
3. When a writable remote exists, the authoritative remote default tip equals the local default tip.
4. After proof, remove a task worktree/workspace and feature branch/bookmark. Use the owning launcher for its active worktree; never delete the agent's original directory directly.

Commit/change shaping, integration, publication, proof, and cleanup are separate states. Never report `done` from a clean feature workspace alone.

## Select the backend

Run `jj root --ignore-working-copy`. If it succeeds, use the jj path. Otherwise use Git. Never initialize jj during closeout.

Honor an explicit PR, local-only, no-push, squash, merge, or fast-forward request. Honor repository rules that require review or forbid direct pushes. Otherwise, **Default to direct landing** on the actual default branch/bookmark and publish it. A bare `done` authorizes this closeout; do not ask PR versus direct merge again.

Discover the remote and default destination from live state. Prefer remote symbolic HEAD, hosting metadata, or jj's `trunk()`/tracked bookmarks. Fall back to `main` or `master` only when the ref exists. Do not assume the remote is `origin`.

## Git closeout

### Dirty default checkout integration

Unrelated dirt in the default branch's existing checkout is not itself a
blocker after task changes are committed. Preserve it and first try the normal
safe Git path:

1. Fetch the remote and create a clean temporary integration worktree at the
   remote default tip.
2. Rebase or cherry-pick only the explicit task commits there. Never absorb
   other commits merely because they are ancestors of the task tip.
3. Run `git -C "$default_checkout" merge --ff-only "$integration_tip"`.
   Git will refuse before overwriting overlapping tracked or untracked work.
4. When it succeeds, push the default branch without force, fetch again, and
   prove local/remote equality.

Report `Blocked:` only when Git refuses the fast-forward because the task
overlaps dirty paths, or when the default histories cannot be reconciled
without changing user-owned work. Include Git's refusal as evidence. Never move
that checkout to a preservation branch, create a preservation branch to free
the default branch, stash, reset, commit its unrelated dirt, or update its
checked-out branch ref directly. A bare `done` does not authorize changing the
branch meaning of another checkout.

Unrelated dirt in a non-default task worktree does not block landing its already
committed task revision through a clean default checkout. Preserve the dirt and
defer that worktree's cleanup.

1. **Snapshot.** Before any `cd`, record `active_directory=$(pwd -P)` and never recompute it. Record root, path, branch/detached state, worktrees, task tip, default branch, remotes, status, and ahead/behind counts. Identify unrelated files.
2. **Commit task work.** Split distinct intents. Leave unrelated dirt unstaged. Run focused checks.
3. **Refresh.** Fetch the chosen remote. Reconcile destination local-only commits and remote tip. Rebase the explicit task commits when safe; rerun checks after changed content or commit IDs.
4. **Integrate.** Prefer a fast-forward on the default branch. Use its clean checkout directly, or use a temporary integration worktree and then attempt `merge --ff-only` in a dirty default checkout as described above. Never reset, overwrite, or blindly stash unrelated dirt.
5. **Publish.** Push the default branch, not merely the feature branch. Do not bypass hooks.
6. **Prove.** After a final fetch, run:

   ```bash
   bash "${HOME}/.agents/skills/done/scripts/verify-landing.sh" \
     "$integration_tip" "$default_branch" "$remote"
   ```

   If the installed verifier is unavailable, require both `git merge-base --is-ancestor "$integration_tip" "$default_branch"` and equality between `git rev-parse "$default_branch"` and `git ls-remote "$remote" "refs/heads/$default_branch"`.

7. **Clean up last.** Recheck tracked/untracked files. Remove no dirty worktree.
   Do not remove the worktree before the cleanup verifier passes. Before
   removing any candidate worktree, require:

   ```bash
   if bash "${HOME}/.agents/skills/done/scripts/verify-workspace-cleanup.sh" \
     "$active_directory" "$candidate_worktree"
   then
     git worktree remove "$candidate_worktree"
   fi
   ```

   Never delete a candidate that contains `active_directory`, even after `cd`
   elsewhere. `cd`-ing out first only leaves the returning shell in a deleted
   CWD. Route a recognized launcher-owned current worktree through **Launcher
   teardown** after completing any receipt. Otherwise report cleanup deferred,
   not failed. Delete a feature branch only after Git proves containment and no
   preserved worktree still checks it out.

## jj closeout

`@` is workspace-local. Other workspaces share the repository and operation log, not the same working-copy commit. Never use repository-wide `jj undo` or `jj op restore` as routine recovery while other agents may be active.

1. **Snapshot.** Before any `cd`, record `active_directory=$(pwd -P)` and never recompute it. Record `jj root`, `jj workspace root`, `jj workspace list`, `jj status`, `jj op log -n 1`, `jj git remote list`, and `jj log -r '@-::@ | trunk()'`. Identify unrelated files, the task's stable change ID, and each cleanup candidate's workspace name and physical root separately.
2. **Shape task work.** Resolve conflicts, describe each meaningful change, run focused checks, then create an empty successor with `jj new`. Record the completed task change ID explicitly; do not blindly assume every `@-` belongs to this task.
3. **Refresh.** Run `jj git fetch --remote "$remote"`. Determine the actual default bookmark and tracked remote bookmark. If the remote advanced, rebase only the task's explicit change range onto the fresh remote destination and rerun checks.
4. **Integrate.** Move the local default bookmark to the rebased task tip only after proving the intended task range. Do not move unrelated bookmarks.

   ```bash
   jj bookmark set "$default_bookmark" -r "$task_change_id"
   ```

5. **Publish.** Push that bookmark through jj. Raw `git push` and `jj_vcs align_push` bypass this proof contract.

   ```bash
   jj git push --remote "$remote" --bookmark "$default_bookmark"
   jj git fetch --remote "$remote"
   ```

6. **Prove.** Use the stable task change ID; sign-on-push may rewrite its commit ID.

   ```bash
   bash "${HOME}/.agents/skills/done/scripts/verify-jj-landing.sh" \
     "$task_change_id" "$default_bookmark" "$remote"
   ```

   The verifier requires no task conflict, task containment by the local default bookmark, and equality among local, tracked-remote, and authoritative Git remote tips.

7. **Clean up last.** Confirm the task workspace is empty and contains no unrelated files. Before forgetting or removing a workspace, require:

   ```bash
   bash "${HOME}/.agents/skills/done/scripts/verify-workspace-cleanup.sh" \
     "$active_directory" "$candidate_workspace_path"
   ```

   Forget by workspace name and remove only the corresponding physical path
   after the verifier succeeds. Never remove the workspace containing
   `active_directory`, even after `cd` elsewhere. Route a recognized
   launcher-owned current workspace through **Launcher teardown** after
   completing any receipt. Otherwise preserve it and report cleanup deferred.
   Preserve other workspaces and bookmarks.

## Receipt

If `agent-start` produced a receipt, finish it only after proof. Record the proved local and authoritative remote tips:

```bash
hey agent-complete "$receipt" \
  --revision "$task_revision" \
  --local-tip "$local_tip" \
  --remote-tip "$remote_tip"
```

A mismatch records `false_done` and fails. Do not edit the receipt to turn it green.

## Launcher teardown

Launcher teardown applies only after landing proof, receipt completion, and a
fresh check that the recorded task checkout has no tracked, untracked, or
conflicted files. Prepare the final report first. Do not manually remove the
active directory.

### Herdr

When `HERDR_ENV=1`, invoke the teardown script with the recorded task root as
the last tool action:

```bash
bash "${HOME}/.agents/skills/done/scripts/teardown-herdr-worktree.sh" \
  "$task_root"
```

The script requires `HERDR_WORKSPACE_ID`, proves the active directory and clean
Git root match the recorded task root, parses Herdr's structured worktree
provenance, and executes native worktree removal without `--force`. It never
closes a normal or parent Herdr workspace. Treat any refusal as deferred cleanup
and report its exact reason.

### Codex Desktop

When `CODEX_THREAD_ID` is set, first confirm the recorded task root is the
calling thread's Codex-owned worktree under
`${CODEX_HOME:-$HOME/.codex}/worktrees/`. Do not archive a same-directory or
local-checkout thread merely because the variable exists.

Use the current-thread `set_thread_archived` tool with `{ "archived": true }`,
omitting `threadId` and `hostId`. The background archive lets Codex Desktop own
thread and worktree cleanup. Never run `git worktree remove` against the active
Codex worktree. If the tool is unavailable or ownership cannot be proved,
report cleanup deferred and tell the user to archive the thread in Codex
Desktop.

## Final report

Lead with the user-visible result in plain language: what changed, whether it works, and whether the user needs to act. For a bare `done` after an earlier completion report, say explicitly that no additional product change was made; only closeout was re-verified.

Follow with one compact proof line: `Landed on <destination>; remote verified; <checks> passed.` Include hashes, local/remote tip equality, worktree, branch, receipt, and unrelated-file details only when the user asks or they require user action. Otherwise say only that unrelated work was preserved. Never lead with backend, branch cleanup, raw hashes, or other repository mechanics.

If any landing invariant is unproved, lead with `Blocked:` or `Local only:` and give the exact next action.
