pi.md
September 3, 2026 · View on GitHub
Mode: Pi extension background wake.
When this session owns supervision and away mode is not active:
- Drain first with
bin/fm-wake-drain.sh. After handling all emitted wakes and reconciling open decisions and unread status lines, run the exact--ack-throughcommand printed asWAKE_ACK_REQUIRED; until then the work remains durable for idempotent re-handling after interruption. - Confirm the Pi primary auto-loaded both project extensions (plain
piorpi-signed, after approving project trust once per clone); if not, restart the selected executable with-e __FM_PI_TURNEND_EXT__ -e __FM_PI_EXT__as a trust-free fallback. - Initial process cycle only: make the one required
fm_watch_arm_picall; if startup already owned the fleet lock, this is an ownership-based no-op. Use/fm-watch-arm-pionly as a human-entered fallback. Never runbin/fm-watch-arm.shthrough Pi's bash tool because that foreground arm can wedge the agent and bypasses extension-owned cleanup. - If the extension says no live session holds the lock, run
bin/fm-session-start.shto reclaim the session lock, then callfm_watch_arm_piagain. - The extension starts
bin/fm-watch-arm.sh --restart, keeps the child attached to the live Pi process, and owns every later successor launch. - Ordinary same-process session replacement (
/new,/resume,/fork, reload) retires only the prior generation; when the replacement owns the fleet lock, itssession_startarms the new generation without a model turn or anotherfm_watch_arm_picall. The generation-owner contract and in-flight actionable-close handoff live in.pi/extensions/fm-primary-pi-watch.ts. - After an actionable child close, the extension rechecks session-lock ownership and verifies one successor before it delivers the follow-up wake; its bounded fallback is defined in
docs/watcher-continuity.md. - Ordinary work, turn completion, and ordinary signal, stale, check, heartbeat, or other wake handling: do not call
fm_watch_arm_piagain because continuity is extension-owned rather than model-memory-owned. - An unexpected child close enters bounded exponential retry, and an exhausted retry or lost session lock is surfaced as a watcher failure instead of disappearing.
- Missing, failed, or unhealthy cycle only: if a later notification explicitly reports one of those repair conditions, drain queued wakes, inspect the failure text, call
fm_watch_arm_pi, and restart the selected Pi-family executable with both extensions loaded if needed. A redundant call while the extension owns an arm child or scheduled retry is an ownership-basedwatcher: unchangedno-op, not an independent health claim. - Never use shell
&for watcher supervision. The arm mechanism above is extension-owned, not a model tool call, but a manual recovery probe that backgrounds, pipes, or bundles the arm is denied automatically by the PreToolUse seatbelt (bin/fm-arm-pretool-check.sh, wired into the turn-end guard extension at__FM_PI_TURNEND_EXT__).
The supervision branch is default-on (docs/pi-supervision-branch.md): whenever this session owns the fleet lock and away mode is not active, the watcher extension hands eligible task-local rows from ordinary actionable wakes, plus selected fleet-wide heartbeat reviews, to the in-process supervision branch while main-only rows remain queued for this conversation.
A no-change heartbeat outcome explicitly reported with task=fleet and silent=true is delivered silently with no rendered note, while every other routine outcome returns as an appended, rendered note that leads with ⛵ then the dim outcome text.
A captain-facing outcome instead appears as one exact, sequence-keyed visible transcript entry, and then arrives in this conversation as one hidden supervision processing request listing each [seq N] task: summary it covers.
That request is the one turn in which MAIN processes the outcome: give the captain a visible response where one is due, answer or escalate a decision, act on a blocker or failure, or record that no further action is needed, then call the fm_branch_processed tool with the highest sequence the request listed, exactly once.
Only that call closes the outcome; an unrelated, empty, or paraphrased answer leaves it open, and the current unprocessed sequence set is presented again at the next run boundary and at session start until it is acknowledged.
The persisted entry is already the captain-visible record, so MAIN must not re-emit it verbatim merely because it appeared.
Before MAIN steers, controls lifecycle, or cleans up a task, claim its lease with bin/fm-lease.sh claim <task> and release it afterwards; a refused claim means the branch is acting on that task right now.
This conversation still receives every other fleet-wide or unresolvable wake, the branch's wakes when it is unavailable or away mode is active, and every watcher-failure alarm regardless, so the arm and repair contract above is unchanged.
Treat the merged fleet event as already handled for fleet operations: MAIN must not re-drain, re-run, or acknowledge it.
Separately, MAIN applies judgment about whether and how to surface, summarize, reference, or incorporate a merged sailboat outcome in the captain conversation; event ownership does not decide the conversational treatment.
Read the durable outcome store with the fm_branch_outcomes tool when the captain asks what happened.
The turn-end guard extension lives at __FM_PI_TURNEND_EXT__.
The watcher extension lives at __FM_PI_EXT__.
Both are tracked, project-local .pi/extensions/*.ts files that Pi auto-discovers once the project is trusted; bin/fm-session-start.sh reports when the running Pi session has not loaded both required extensions.