How to write the end-of-session handoff — the residue this session neither owned nor could fix. Activate when the user asks for a handoff, when a session is ending with a known failure still standing, or when you are about to call something "pre-existing", "unrelated" or "not mine". The load-bearing rule is that a handoff is what remains AFTER you fixed what root CLAUDE.md's scope rule makes yours and handed each shared failure to its one owner, never an alternative to either.
Install
npx skillscat add alexandermattturner/agent-glovebox/handoff Install via the SkillsCat registry.
Writing the end-of-session handoff
The doctrine
Fix what is yours, route what is shared, hand off the remainder. Root CLAUDE.md § Autonomy's scope rule decides which of the three a failure is. A handoff entry is one its arm 3 leaves: not caused by your change, and not blocking your outcome. A failure your own diff caused is yours. A shared one goes to its owner, is named here in one line with that owner, and is never re-fixed here. A clean session writes nothing and says nothing — never "no handoff needed".
An intermittent test failure is not a handoff item. The `ci-triage` skill owns it, including the rule that hardware you lack is never an exit. Two flakes still hand back, one line each: one an OPEN PR already covers (name that PR), and one blocked by a credential you lack that stops you from committing a runnable reproduction at all (name the credential).
The four steps
- Fix what the scope rule's arm 1 makes yours. Work every such item on this branch until only the unreachable remain. Needing another PR is not a reason to defer — open it.
- Earn "pre-existing". Reproduce the failure on the unmodified base before you call it unrelated:
Then run the exact failing command there and quote what it printed. A claim you did not run is a hypothesis, and it says so. Reproducing it on the base earns "pre-existing", which makes it a SHARED failure rather than "not mine": step 3 finds its one owner, and you reuse that owner's change instead of writing a second one. With no owner, follow `ci-triage`'s ownership rule and take the repair. Mergeumask 002 && git worktree add --detach /tmp/claude-worktrees/base origin/mainorigin/maininto your branch and run it again before you hand anything back — a failure only that merge produces is yours however green both sides are alone. - Deduplicate. Search issues and pull requests, because a multi-PR effort is tracked by an issue that no PR search finds:
mcp__github__search_issuesandmcp__github__search_pull_requests, both withowner/repoandfields: ["number","state","title"].gh pr list --searchis GraphQL and answers 403 in a web session, so the MCP tools are the only route that works on both surfaces.- An
OPENtracking issue owns the item: add your row by EDITING that issue, and file no second one. - Read the PR state:
OPENgets one line naming it and no fresh investigation;MERGEDmeansmainalready carries the fix, so drop the item;CLOSEDcovers nothing, so the item stands.
- Paste it in chat, in full. Never a bare file path: on the host the reader has no shell to open one, and inside the sandbox
/tmpdies at teardown. Commit a copy only when it is worth keeping.
The shape
Lead with one line per item — what is broken, where, whether a PR already covers it. Then the handoff itself, written for a session that shares none of this one's context.
Write it self-contained: the next session cannot open a file you name. It starts in a fresh container, so a scratch file, a plan file and a worktree path are all gone, and a committed path it can read still costs it a turn to find. Carry the content in the prompt — the failing lines, the verdict text, the assertion, the command's output — and cite a path only beside that content. A committed path is a pointer for a reader who already has the substance, never a substitute for it.
- the full path and the exact command,
- the reproduction, and what it printed,
- each claim labelled observed or hypothesised,
- the PR or issue number that already covers it, when one does.
Size each handoff to one session
A handoff a session cannot finish comes back as another handoff, so estimate it before you write it. The bounds are the ones root CLAUDE.md's Delegation and Testing sections and the `babysit-prs` skill's dispatch band already set:
- Count the units. A unit is work one sub-agent finishes on its ~100k tokens with ONE verification command: a verb to home, a donor diff to take and rename, a check script to give a second arm, a test file to write. A row that flips with no code edit is not a unit.
- Find the width. The writing fleet caps at four agents, lower under load. Each agent runs its tests through
scripts/test-slot.sh, which holds the host to halfnproctest runs at once. A read-only agent spends no CPU and does not count against the width. - Find the depth. Count the longest chain of units where each needs the one before it landed. No width shortens a chain.
- Count the CI rounds. Root
CLAUDE.md§ Testing bounds local testing per PR and per session, and each worktree agent provisions its own ~1 GB toolchain. A unit whose proof needs a live runner (a microVM shard, a KVM leg) is proved by a CI round instead, and rounds on one branch are serial: a push cancels the run before it. - Cut at the seam. Start from about two fleet rounds of units (2 x width), a chain of about three, and about three CI rounds. No cap in the tree sources those three figures, so revise them against what this session observed. Past any of them, cut where the next unit needs the previous unit landed. Name that seam in both handoffs, and have the second cut its branch from the first's.
Write the unit count, the width you assumed, the seam, and which units are independent into the handoff. A handoff that does not say which units are independent is run serially, which is the schedule the estimate exists to prevent.
Inside the sandbox, add the guardrail friction you hit
_GLOVEBOX_PROTECTION_TIER set in the environment means you are in the microVM (glovebox never sets IS_SANDBOX). On the host, skip this half.
List only what you empirically ran into, one line each with the command and its observed output: a Bash-sandbox denial and its error text, a tool that exited 0 on a blocked read, a guardrail whose message did not name its remedy. Never speculate — an invented one sends someone to fix a boundary that works.
Not a handoff item
- A rule you obeyed, a bug in the code under test, an edge you fixed.
- Friction from this repo's own tooling that blocked your outcome — you fix that. An
## Also noticedline in chat carries friction that blocked nothing, as a code location or reproducer and its consequence. - Anything the scope rule's arm 1 makes yours. Naming a piece of work is not delivering it.
- A shared failure you owned and fixed. One you handed to another owner is still a line here, naming that owner, so the next session finds the fix instead of writing a second.