The lead rollover handover file now lives inside the workspace, at the relative path fleetd.yaml's leadRollover.handoverPath names. It is a snapshot of one moment's live state, so it must never enter git history. The handover skill also now says the handoverPath fleetd hands back is always absolute, even when the configured value is relative — a lead that resolves it itself can pick a different file from the one the daemon checks.
9.9 KiB
name, description
| name | description |
|---|---|
| handover | Procedure for an outgoing lead to write the handover file that a fresh lead session inherits. Load this when your context is filling up and you are about to be replaced, whether you hand off by hand or fleetd does it for you. The file is the new lead's only inheritance — follow it exactly. |
Handover — write the file the next lead depends on
A lead session fills up its context and has to be replaced by a fresh one. The outgoing lead writes a handover file, and the new session reads that file and carries on.
There are two ways to hand off, and the file is the same either way.
- By hand. You write the file, then tell the operator where it is. The operator starts the new session and points it at the file. This always works.
- With
fleet_handover(fleetd #480, merged 2026-09-11). You ask fleetd to do the swap: it checks the file, clears your pane, and tells the fresh session to read it. This needsleadRollover:infleetd.yaml; without it every action answers a clean refusal namingNOT_CONFIGURED, and you fall back to the manual path. Section 11 below is the procedure.
Nothing else in this skill changes between the two. Only who performs the swap changes.
The new lead's only inheritance is that file. It does not see your conversation, your plan, or your screen. If the file is thin or wrong, the new lead re-derives what you already knew, and that wastes hours. Writing a good handover file is real work. It is not paperwork you rush through at the end of a session.
This skill is the procedure for writing it. Every rule below earned its place because a past handover got it wrong.
1. Confirm you are the right session to write this
Run fleet_whoami first. It must answer primary. Only a primary (lead) session writes a
handover file. A worker's job ends with its own pull request, not a fleet-wide handoff.
2. Every number needs a command, run in this turn
A number is a claim: a count, a commit hash, a process id, a percentage, a queue depth. Before you write one, run the command that produces it — now, in this turn, against the live state.
Never take a number from:
- earlier in your own conversation — the state has moved since then,
- a peer lead's report — that is their measurement, not yours,
- your own memory of an earlier session.
Put the command, or its real output, next to the number. That lets the next lead re-run it and check it still matches. If you cannot measure something yourself, say so instead of guessing: "the fleet01 lead reports 91 commits behind; I have not checked this myself."
3. Say what you measured and what you did not
Mark every claim as one of two things:
- "I checked this myself, in the code or on this host, at
<time>." - "I did not check this myself;
<who>reported it."
Never present someone else's measurement as your own. This matters most for cross-host claims — a peer lead's daemon, a worker's report, or something the operator said earlier that you cannot re-verify from here.
4. Record open decisions, and who owns them
List three things:
- what the operator actually asked for, in their own words where you have them,
- what is still unanswered,
- any question you decided yourself instead of asking, with your reason.
Write the decision so it cannot be mistaken for the operator's instruction. Say plainly: "the operator never answered X; I decided Y, because Z." Without this, the next lead either silently reopens a closed question or assumes the operator chose something they never did.
5. Record live hazards
List anything that will break if the next lead does the obvious thing next. This includes:
- unpushed commits or unmerged branches,
- a build, a spawn, or a redeploy still running,
- code merged to
mainbut not yet redeployed to the live daemon, - any trap that looks safe and is not — say what goes wrong and why, not only that something is "tricky."
6. Record what is explicitly not owed
List work that is finished, and work that another party has said they do not want touched. Name who said so and when. Without this line, the next lead re-does closed work or reopens a question a peer already declined to revisit.
7. Open the file with three re-measurement commands
The file's own first section must give the next lead three concrete commands to run before acting on anything else in the file:
- confirm role — for example
fleet_whoami, - confirm the state of the working tree — for example
git statusandgit rev-list origin/main..HEAD, - read the live fleet — for example
fleet_list.
Record what each command answered when you wrote the file, and tell the reader to run it again rather than trust your answer. The point of this section is that the reader checks live state before acting on any claim in the rest of the file, including yours.
8. Stamp the file with time and commit
Near the top of the file, write:
- the date and time you wrote it,
- the commit the tree was on (
git rev-parse HEAD), - whether the tree was clean (
git status).
Without this, nobody can tell how old the file is, or which code it describes.
9. State plainly that the file goes stale fast
Say near the top: re-measure anything you act on. The file goes stale the moment anyone merges a branch, spawns a member, or restarts the daemon. Everything in the file is a snapshot of one moment, not a live fact.
10. What to leave out
Do not include:
- narration of how the session felt, or how hard something was,
- anything the repo already records — code structure, git history, or a rule already written in
CLAUDE.md. Point at it instead of repeating it, - advice that is only true for the session that is ending — a half-open terminal, a local variable, a train of thought with no state behind it.
A handover file is a record of state and decisions. It is not a diary.
11. Using fleet_handover (only if leadRollover: is configured)
Run the three steps in this order. The order is not a style choice — the wrong order is refused.
-
fleet_handover{action: "open", reason: "<why now>"}. It returns atokenand thehandoverPathyou must write to. Nothing has happened to your pane yet.Write to exactly that path, and do not resolve it yourself. It is always absolute, even when the operator configured a relative
handoverPath: fleetd resolves a relative one against your own workspace before it hands it to you. The daemon and your pane can run in different directories, so a path you resolve yourself can point at a different file from the one the daemon will check. -
Write the handover file at that path, following sections 1–10 above.
-
Ask the operator, then
fleet_handover{action: "confirm", token, operatorConfirmed: true}.
Why that order: confirm refuses with HANDOVER_STALE unless the file was modified after the
open request. That check stops a leftover file from an earlier session being accepted as this
one's handover. So writing the file first and then calling open — the obvious order — always
fails.
{action: "cancel", token} drops a pending request without rolling.
Things that will surprise you:
accepteddoes not mean your pane has been cleared. It means every gate passed and the roll is scheduled to run once your current turn ends. Say your goodbye in the same turn — you will not get another one.- There is no terminal or session parameter, on purpose. The pane is always your own, resolved from your connection, so you can only ever roll yourself.
operatorConfirmedis your report of what a human told you. Do not passtruebecause you are confident. Ask, wait for the answer, then pass what they said.requireOperatorConfirmdefaults totrueand this is the only thing standing between a judgement call and a wiped session.- The roll can still refuse after
confirmreturns, and by then there is no caller to tell. Those outcomes are logged only, aslead-rollover:lines in the daemon log. - If the bootstrap prompt never lands, your context is gone and no fresh session starts. This has not yet been proven end-to-end (see fleetd #480). The recovery is the manual path: the file is already written, so the operator starts a session and points it at the file. That is why you write the file before you confirm, and never the other way round.
Writing style
Write in plain English. Use everyday words, one idea per sentence, and active voice. Keep every class, method, file, flag, and config key exactly as it appears in the code — replacing a precise term with a vague one makes the sentence wrong, not simpler. Explain an abbreviation the first time you use it.
If a diagram genuinely helps, put it in the .md file as a fenced ```mermaid block with no
hardcoded colors, so it stays readable on light and dark backgrounds. Quote any label that has
brackets, colons, or slashes.
Template
# Handover — <fleet name> lead session, <date and time>
Written at commit `<output of git rev-parse HEAD>`. Tree was <clean, or dirty: `<git status
summary>`>. Re-measure anything you act on — this file goes stale the moment anyone merges,
spawns, or restarts.
## 0. Do these three things first
1. Confirm your role: `fleet_whoami` — must answer `primary`. (Answered `<result>` at `<time>`.)
2. Confirm tree state: `git status`, `git rev-list origin/main..HEAD`. (`<result>` at `<time>`.)
3. Read the live fleet: `fleet_list`. (`<result>` at `<time>`.)
## 1. What the operator asked for
<the live instructions, in their words where you have them; what is still open; any decision
you made yourself, and why>
## 2. Open decisions, and who owns them
<one line per decision: who owns it, what is unanswered>
## 3. Live hazards
<one entry per hazard: what breaks, and why, if the next lead does the obvious thing>
## 4. What is not owed
<finished work, and work another party has declined; name who said so and when>