docs: fleet_handover is shipped — update the charter table and the handover skill
fleetd #480 merged in #483, #484 and #485, so the instruction surface has to catch up. Per CLAUDE.md's own rule, a code change that silently invalidates the canonical block is an incomplete change. CLAUDE.md + wiki/7-Use-Cases.md: one new intent-to-tool row for fleet_handover. Both copies edited identically; the byte-identical sync check prints True. The row carries the trap rather than just the call: open FIRST, then write the file, then confirm — because confirm refuses the file as stale unless its modified time is later than the open request, so the obvious order fails. .claude/skills/handover/SKILL.md: the skill said "do not call a fleet_handover tool: it does not exist." That was true when it was written this morning and is now false, which is the worst state for an instruction file to be in. Replaced with the two real paths (by hand, or with the tool when leadRollover: is configured), plus a new section 11 giving the three-step order and the five things that surprise a caller — chiefly that `accepted` does not mean the pane has been cleared, and that operatorConfirmed is a report of what a human said, not a confidence level. Section 11 also states plainly that the bootstrap prompt landing in a freshly cleared pane is not yet proven end-to-end, and that the recovery is the manual path — which is why the file is written before confirm, never after.
This commit is contained in:
@@ -8,13 +8,16 @@ description: Procedure for an outgoing lead to write the handover file that a fr
|
||||
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.
|
||||
|
||||
**Today this is a manual handoff.** You write the file, then tell the operator where it is. The
|
||||
operator starts the new session and points it at the file.
|
||||
**There are two ways to hand off, and the file is the same either way.**
|
||||
|
||||
**fleetd #480 will automate the same cycle** — the lead asks, fleetd checks the file, clears the
|
||||
pane, and tells the fresh session to read it. That work is not shipped yet, so **do not call a
|
||||
`fleet_handover` tool: it does not exist.** Check the live tool list before assuming otherwise.
|
||||
Nothing else in this procedure changes when it does ship; only who performs the swap changes.
|
||||
- **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 needs
|
||||
`leadRollover:` in `fleetd.yaml`; without it every action answers a clean refusal naming
|
||||
`NOT_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
|
||||
@@ -125,6 +128,41 @@ Do not include:
|
||||
|
||||
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.**
|
||||
|
||||
1. **`fleet_handover{action: "open", reason: "<why now>"}`.** It returns a `token` and the
|
||||
`handoverPath` you must write to. Nothing has happened to your pane yet.
|
||||
2. **Write the handover file at that path**, following sections 1–10 above.
|
||||
3. **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:**
|
||||
|
||||
- **`accepted` does 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.
|
||||
- **`operatorConfirmed` is your report of what a human told you.** Do not pass `true` because you
|
||||
are confident. Ask, wait for the answer, then pass what they said. `requireOperatorConfirm`
|
||||
defaults to `true` and this is the only thing standing between a judgement call and a wiped
|
||||
session.
|
||||
- **The roll can still refuse after `confirm` returns**, and by then there is no caller to tell.
|
||||
Those outcomes are logged only, as `lead-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
|
||||
|
||||
@@ -142,6 +142,7 @@ the merge — and merging on a reviewer's word is delegating it by proxy.
|
||||
| Read your own held lead-to-lead mail (no ack) | `fleet_poll{coordId: <your own coord-id, from fleet_list's coordinator.selfId>}` — primary-only; never acks, so `fleet_list`'s `held[]` still shows it after. `fleet_list`'s `held[]` gives only a truncated preview — this is the only way to read the full body |
|
||||
| Collect a held reply | `fleet_poll{target}` · then `fleet_ack{target, msgId}` |
|
||||
| Tear down a member | `fleet_stop{paneId}` |
|
||||
| Replace your OWN lead session when its context is full | `fleet_handover{action:"open", reason?}` → write the handover file it names → `fleet_handover{action:"confirm", token, operatorConfirmed}`. Primary-only. **In that order**: the file must be modified *after* `open`, or `confirm` refuses it as stale. There is no terminal parameter — the pane is always your own, so you can never roll another lead. `{action:"cancel", token}` drops a pending request |
|
||||
|
||||
### Lead ↔ lead — coordinate, never delegate
|
||||
|
||||
|
||||
Reference in New Issue
Block a user