finish
Integrates reviewed work by testing it against the branch it will merge into, opening or updating the pull request, and merging or discarding only on the use...
npx skills
npx skills add JoshuaOliphant/compost --skill finish
Works for Codex, Cursor and other harnesses. Use -a <agent> to target one, e.g. -a codex.
Claude Code
/plugin marketplace add JoshuaOliphant/compost
/plugin install compost@compost
Finish
Review is addressed and the branch is ready to leave its worktree. This skill proves the work still holds once it meets the branch it merges into, writes the pull request so a reviewer can audit every decision made without them, and cleans up after the merge. Merging into the default branch is the user's decision, and so is throwing work away. Everything before that point is yours to do without asking.
Steps
-
Confirm the branch is ready. A
compost:reviewoutcome is recorded for every issue in the work: its step 6 comment on the issue, or its result in this session for work with no issue. If one is missing, stop and runcompost:reviewnow, whatever the size of the diff. A skipped review is a gap to close here, never a line under Risk, and so is one recorded as incomplete on an axis. Everycompost:reviewfinding is fixed or answered on the issue with afile:linereason.compost:verifyran on the current HEAD. If commits landed after its last run, run it again first. -
Record where you are. Capture these now, because cleanup runs from another directory:
bash
worktree_path=$(git rev-parse --show-toplevel)
branch=$(git branch --show-current) # empty on a detached HEAD
main_root=$(git worktree list --porcelain | sed -n '1s/^worktree //p') # the main checkout
A detached HEAD means the harness manages this workspace. Push it as a named branch
(git push origin HEAD:refs/heads/<issue>-<slug>) and leave the workspace in place.
-
Name the merge target. It is the branch this work forked from. Take it from the issue: a parent issue may name an integration branch for its sub-issues. Otherwise it is the default branch (the tracker doc's Default branch command; with none,
git symbolic-ref --short refs/remotes/origin/HEADminus itsorigin/). State it in the PR. -
Run the suite on the actual merge result. A green branch proves only the branch. Start with the cheap check:
bash
git fetch origin
git merge-base --is-ancestor origin/<target> HEAD
Exit 0 means the target hasn't moved past your branch, so the merge result is HEAD's own
tree and compost:verify's run on HEAD is the merge-result run. Exit 1 means the target
moved: merge it into your branch (git merge --no-ff --no-edit origin/<target>), resolve any
conflict, and go back through compost:verify; the branch then holds the merge result. This needs no second
worktree and no git -C, which a worktree-isolated session refuses. Do not rebase a branch
you have already pushed. A red merge result stops here and goes to compost:diagnose. Any
other exit is an error (a wrong target name, a failed fetch): fix it before going on.
Where your branch must not take the target's commits yet, build the merge in a throwaway
worktree instead: git worktree add --detach "$main_root/.worktrees/merge-check-<issue>"
origin/<target>, cd into it, git merge --no-ff --no-edit <head> (your branch's
git rev-parse HEAD, taken before the cd, so a detached HEAD works too), install
dependencies, run the full suite and the recorded gates, then cd back and
git worktree remove it. A worktree-isolated session may refuse to work outside its own
worktree; then take the merge-into-the-branch path above.
-
Open or update the pull request. Push the branch and open a draft PR against the target, or update the open one. The body has five sections: Goal, Decisions, Changes, Validation, Risk. Decisions summarizes the rulings posted as issue comments. Validation carries
compost:verify's evidence and the merge-result run. Risk calls the change a one-way or two-way door with its blast radius, and names whatever is unproven, deferred, or excluded from coverage. End the body with aClosesline for the issue, or for the parent and every sub-issue (Closes #12, closes #13, closes #14), keeping any the PR already has; leave outready-for-humansub-issues, and the parent while any is open. Use the PR body template and the Pull requests commands indocs/agents/issue-tracker.md(create as a draft, edit the body). Use those, not the forge CLI's porcelain:gh pr creategoes through GraphQL, which Claude Code cloud sessions block. With a local tracker, post the same body on the issue file instead. -
Stop for the merge decision when the target is the default branch. Report the PR link, the merge-result run, and three options: merge, keep the branch open for more feedback, or discard. Wait for the answer. A merge into an integration branch that is not the default branch does not need to wait; go on to step 7.
-
Merge with a merge commit, then clean up. When told to merge:
- With an open PR, mark it ready (the forge refuses to merge a draft), then merge through the
forge with a merge commit, using the tracker doc's Ready for review and Merge commands (in a cloud session, the
cloud variant of ready).
That is the forge's
--no-ff. Never squash or rebase unless the user asks for it. -
Without a PR, build the merge commit in this worktree on a detached HEAD, so the main checkout is never touched and no
git -Cis needed:bash git status --porcelain -uall # must be empty: commit or ask about anything listed git fetch origin git switch --detach origin/<target> git merge --no-ff -m "Merge branch '$branch' into <target>" "$branch"If the merge stops on conflicts, the target moved after step 4:
git merge --abort,git switch "$branch", and go back to step 4. Otherwise run the suite on the result. If it is red,git switch "$branch"and stop with the branch and worktree in place and nothing pushed. If it is green,git push origin HEAD:<target>. A rejected push also means the target moved:git switch "$branch"and go back to step 4. Then close the issue, or the parent, the way the tracker says (for a local tracker,Status: closedand a closing note naming the merge commit).With no remote there is nothing to push to, and the target branch has to move in the checkout that has it. That needs a session that is not worktree-isolated:
cd "$main_root",git switch <target>,git merge --no-ff "$branch", then run the suite there.
Then clean up. Fetch, and check git merge-base --is-ancestor "$branch" origin/<target> (or
<target> with no remote): any exit but 0 means the branch isn't merged, so stop and say so.
On 0, remove the worktree first, since git won't delete a branch a worktree has checked out.
Use the harness's exit tool (such as ExitWorktree with remove) if it created the worktree;
otherwise cd "$main_root" and git worktree remove "$worktree_path". If removal is refused
because of uncommitted files, list them (git status --porcelain -uall inside the worktree)
and ask the user whether to commit, move, or delete them. Never add --force on your own.
Then delete the branch with git branch -D "$branch" (the ancestry check already proved it
merged, which -d can't see when the local target is stale) and delete the remote branch (git push origin --delete "$branch").
-
Discard only on request, and only after the user types "discard". When the user asks to throw the work away, show what will be lost: the branch, the commits (
git log --oneline <target>..$branch), the worktree path, and any open PR. Ask them to typediscard. A paraphrase such as "yeah, get rid of it" is not the confirmation. Once the word arrives, close the PR (the tracker doc's Close without merging), remove the worktree as in step 7, delete the branch withgit branch -D "$branch", and delete the remote branch (git push origin --delete "$branch"). -
Capture what was learned. If the work turned up a gotcha, a non-obvious root cause, or a verdict reached from measurement, and the compound-knowledge plugin is installed, use
compound-knowledge:compound-captureto record it. When nocompost:implementrun worked this parent, tick the issue in its parent's checklist; otherwise implement already did.
Next moves
compost:implementwhen the parent issue still has unblocked issues.compost:buildwhen review or the user asks for changes on the open PR.compost:diagnosewhen the merge result is red.compost:pausewhen stopping with the PR open and work still in flight.compost:retroonce the PR is merged, to file what in the environment would have made the run go better.