feat: add opt-in Claude away supervision host - #5488
Conversation
Add the supervision host (bin/fm-supervision-host.sh): beside a Claude primary it owns the watcher cycle for the Stop auto-arm and, while the away-posture record exists, hands each wake to a bounded headless Claude engine session that runs the supervision branch's contract - the same generated prompt, row eligibility, wake grant, per-actor drain, outcome store, leases, and away relocation the Pi branch uses. Attended wakes pass straight to main. Every path that cannot finish a wake hands it to main with a supervision-host line; the park ends itself before the Stop hook timeout with a cycle-boundary wake. - bin/fm-supervision-engine-lib.sh: opt-in parse, verified engines (claude, default sonnet), one bounded engine turn, and a reap of engine tool processes that sit in their own process groups. - bin/fm-branch-report.sh: the command twin of fm_branch_report, scoped to the tasks the current host turn claimed. - bin/fm-branch-dispatch.mjs: command entry to the Pi dispatch module, so eligibility and the wake prompt have one owner. - bin/fm-claude-stop-autoarm.sh runs the host in the arm's place when config/supervision-host exists; nothing changes without the file. - bin/fm-watch-arm.sh --stop: home-scoped stop without a re-arm. - bin/fm-lease-lib.sh: an opted-in home takes the lease-command lock for unmarked main too, closing the first-claim race; the refusal tells the caller to leave the lease alone and retry. - /afk launches no away daemon on an opted-in Claude home; /quiet still does. Session start renders the host's main-side protocol there.
…urn, and log per-turn engine cost Live validation found two supervision host gaps. A captain who returns while an engine turn is running gets a return brief rendered before that turn's outcomes exist, so the host now hands the close to main with those outcomes. Claude reports a resumed conversation's running cost, so the engine lib now derives each turn's cost from the total the host records, and the host log records every close's destination.
The dated live results behind docs/supervision-host.md: the Claude engine's live guard, the away-wake cases against real workers, the engine's cost reporting, and the flag-off before-and-after regression.
…, and late outcomes
…t incomplete results
|
Dated live validation for this PR (2026-09-23, Claude Code 2.1.281, Pi 0.87.0, Herdr 0.9.0, macOS 26.6.2) is recorded in docs/verification/supervision.md, "Supervision host". It covers:
Two bugs that live validation found are fixed on this branch: outcomes from a turn during which the captain returned were lost, and the engine's cumulative conversation cost was logged as per-turn cost. |
* feat(bin): supervision host core behind config/supervision-host Add the supervision host (bin/fm-supervision-host.sh): beside a Claude primary it owns the watcher cycle for the Stop auto-arm and, while the away-posture record exists, hands each wake to a bounded headless Claude engine session that runs the supervision branch's contract - the same generated prompt, row eligibility, wake grant, per-actor drain, outcome store, leases, and away relocation the Pi branch uses. Attended wakes pass straight to main. Every path that cannot finish a wake hands it to main with a supervision-host line; the park ends itself before the Stop hook timeout with a cycle-boundary wake. - bin/fm-supervision-engine-lib.sh: opt-in parse, verified engines (claude, default sonnet), one bounded engine turn, and a reap of engine tool processes that sit in their own process groups. - bin/fm-branch-report.sh: the command twin of fm_branch_report, scoped to the tasks the current host turn claimed. - bin/fm-branch-dispatch.mjs: command entry to the Pi dispatch module, so eligibility and the wake prompt have one owner. - bin/fm-claude-stop-autoarm.sh runs the host in the arm's place when config/supervision-host exists; nothing changes without the file. - bin/fm-watch-arm.sh --stop: home-scoped stop without a re-arm. - bin/fm-lease-lib.sh: an opted-in home takes the lease-command lock for unmarked main too, closing the first-claim race; the refusal tells the caller to leave the lease alone and retry. - /afk launches no away daemon on an opted-in Claude home; /quiet still does. Session start renders the host's main-side protocol there. * fix(bin): relay a host turn's outcomes when the captain returns mid-turn, and log per-turn engine cost Live validation found two supervision host gaps. A captain who returns while an engine turn is running gets a return brief rendered before that turn's outcomes exist, so the host now hands the close to main with those outcomes. Claude reports a resumed conversation's running cost, so the engine lib now derives each turn's cost from the total the host records, and the host log records every close's destination. * docs(verification): record the supervision host's live evidence The dated live results behind docs/supervision-host.md: the Claude engine's live guard, the away-wake cases against real workers, the engine's cost reporting, and the flag-off before-and-after regression. * docs: describe the supervision host ledger as covering every close * no-mistakes(review): Harden supervision host ownership, boundary, ack, and late outcomes * no-mistakes(review): Recheck park boundary just before starting an engine turn * no-mistakes(review): Cap park boundary, deliver all host lines, reject incomplete results * no-mistakes(document): Correct supervision host documentation and stale pointers
* feat(bin): supervision host core behind config/supervision-host Add the supervision host (bin/fm-supervision-host.sh): beside a Claude primary it owns the watcher cycle for the Stop auto-arm and, while the away-posture record exists, hands each wake to a bounded headless Claude engine session that runs the supervision branch's contract - the same generated prompt, row eligibility, wake grant, per-actor drain, outcome store, leases, and away relocation the Pi branch uses. Attended wakes pass straight to main. Every path that cannot finish a wake hands it to main with a supervision-host line; the park ends itself before the Stop hook timeout with a cycle-boundary wake. - bin/fm-supervision-engine-lib.sh: opt-in parse, verified engines (claude, default sonnet), one bounded engine turn, and a reap of engine tool processes that sit in their own process groups. - bin/fm-branch-report.sh: the command twin of fm_branch_report, scoped to the tasks the current host turn claimed. - bin/fm-branch-dispatch.mjs: command entry to the Pi dispatch module, so eligibility and the wake prompt have one owner. - bin/fm-claude-stop-autoarm.sh runs the host in the arm's place when config/supervision-host exists; nothing changes without the file. - bin/fm-watch-arm.sh --stop: home-scoped stop without a re-arm. - bin/fm-lease-lib.sh: an opted-in home takes the lease-command lock for unmarked main too, closing the first-claim race; the refusal tells the caller to leave the lease alone and retry. - /afk launches no away daemon on an opted-in Claude home; /quiet still does. Session start renders the host's main-side protocol there. * fix(bin): relay a host turn's outcomes when the captain returns mid-turn, and log per-turn engine cost Live validation found two supervision host gaps. A captain who returns while an engine turn is running gets a return brief rendered before that turn's outcomes exist, so the host now hands the close to main with those outcomes. Claude reports a resumed conversation's running cost, so the engine lib now derives each turn's cost from the total the host records, and the host log records every close's destination. * docs(verification): record the supervision host's live evidence The dated live results behind docs/supervision-host.md: the Claude engine's live guard, the away-wake cases against real workers, the engine's cost reporting, and the flag-off before-and-after regression. * docs: describe the supervision host ledger as covering every close * no-mistakes(review): Harden supervision host ownership, boundary, ack, and late outcomes * no-mistakes(review): Recheck park boundary just before starting an engine turn * no-mistakes(review): Cap park boundary, deliver all host lines, reject incomplete results * no-mistakes(document): Correct supervision host documentation and stale pointers
* feat(bin): supervision host core behind config/supervision-host Add the supervision host (bin/fm-supervision-host.sh): beside a Claude primary it owns the watcher cycle for the Stop auto-arm and, while the away-posture record exists, hands each wake to a bounded headless Claude engine session that runs the supervision branch's contract - the same generated prompt, row eligibility, wake grant, per-actor drain, outcome store, leases, and away relocation the Pi branch uses. Attended wakes pass straight to main. Every path that cannot finish a wake hands it to main with a supervision-host line; the park ends itself before the Stop hook timeout with a cycle-boundary wake. - bin/fm-supervision-engine-lib.sh: opt-in parse, verified engines (claude, default sonnet), one bounded engine turn, and a reap of engine tool processes that sit in their own process groups. - bin/fm-branch-report.sh: the command twin of fm_branch_report, scoped to the tasks the current host turn claimed. - bin/fm-branch-dispatch.mjs: command entry to the Pi dispatch module, so eligibility and the wake prompt have one owner. - bin/fm-claude-stop-autoarm.sh runs the host in the arm's place when config/supervision-host exists; nothing changes without the file. - bin/fm-watch-arm.sh --stop: home-scoped stop without a re-arm. - bin/fm-lease-lib.sh: an opted-in home takes the lease-command lock for unmarked main too, closing the first-claim race; the refusal tells the caller to leave the lease alone and retry. - /afk launches no away daemon on an opted-in Claude home; /quiet still does. Session start renders the host's main-side protocol there. * fix(bin): relay a host turn's outcomes when the captain returns mid-turn, and log per-turn engine cost Live validation found two supervision host gaps. A captain who returns while an engine turn is running gets a return brief rendered before that turn's outcomes exist, so the host now hands the close to main with those outcomes. Claude reports a resumed conversation's running cost, so the engine lib now derives each turn's cost from the total the host records, and the host log records every close's destination. * docs(verification): record the supervision host's live evidence The dated live results behind docs/supervision-host.md: the Claude engine's live guard, the away-wake cases against real workers, the engine's cost reporting, and the flag-off before-and-after regression. * docs: describe the supervision host ledger as covering every close * no-mistakes(review): Harden supervision host ownership, boundary, ack, and late outcomes * no-mistakes(review): Recheck park boundary just before starting an engine turn * no-mistakes(review): Cap park boundary, deliver all host lines, reject incomplete results * no-mistakes(document): Correct supervision host documentation and stale pointers
* feat(bin): supervision host core behind config/supervision-host Add the supervision host (bin/fm-supervision-host.sh): beside a Claude primary it owns the watcher cycle for the Stop auto-arm and, while the away-posture record exists, hands each wake to a bounded headless Claude engine session that runs the supervision branch's contract - the same generated prompt, row eligibility, wake grant, per-actor drain, outcome store, leases, and away relocation the Pi branch uses. Attended wakes pass straight to main. Every path that cannot finish a wake hands it to main with a supervision-host line; the park ends itself before the Stop hook timeout with a cycle-boundary wake. - bin/fm-supervision-engine-lib.sh: opt-in parse, verified engines (claude, default sonnet), one bounded engine turn, and a reap of engine tool processes that sit in their own process groups. - bin/fm-branch-report.sh: the command twin of fm_branch_report, scoped to the tasks the current host turn claimed. - bin/fm-branch-dispatch.mjs: command entry to the Pi dispatch module, so eligibility and the wake prompt have one owner. - bin/fm-claude-stop-autoarm.sh runs the host in the arm's place when config/supervision-host exists; nothing changes without the file. - bin/fm-watch-arm.sh --stop: home-scoped stop without a re-arm. - bin/fm-lease-lib.sh: an opted-in home takes the lease-command lock for unmarked main too, closing the first-claim race; the refusal tells the caller to leave the lease alone and retry. - /afk launches no away daemon on an opted-in Claude home; /quiet still does. Session start renders the host's main-side protocol there. * fix(bin): relay a host turn's outcomes when the captain returns mid-turn, and log per-turn engine cost Live validation found two supervision host gaps. A captain who returns while an engine turn is running gets a return brief rendered before that turn's outcomes exist, so the host now hands the close to main with those outcomes. Claude reports a resumed conversation's running cost, so the engine lib now derives each turn's cost from the total the host records, and the host log records every close's destination. * docs(verification): record the supervision host's live evidence The dated live results behind docs/supervision-host.md: the Claude engine's live guard, the away-wake cases against real workers, the engine's cost reporting, and the flag-off before-and-after regression. * docs: describe the supervision host ledger as covering every close * no-mistakes(review): Harden supervision host ownership, boundary, ack, and late outcomes * no-mistakes(review): Recheck park boundary just before starting an engine turn * no-mistakes(review): Cap park boundary, deliver all host lines, reject incomplete results * no-mistakes(document): Correct supervision host documentation and stale pointers
* feat(bin): supervision host core behind config/supervision-host Add the supervision host (bin/fm-supervision-host.sh): beside a Claude primary it owns the watcher cycle for the Stop auto-arm and, while the away-posture record exists, hands each wake to a bounded headless Claude engine session that runs the supervision branch's contract - the same generated prompt, row eligibility, wake grant, per-actor drain, outcome store, leases, and away relocation the Pi branch uses. Attended wakes pass straight to main. Every path that cannot finish a wake hands it to main with a supervision-host line; the park ends itself before the Stop hook timeout with a cycle-boundary wake. - bin/fm-supervision-engine-lib.sh: opt-in parse, verified engines (claude, default sonnet), one bounded engine turn, and a reap of engine tool processes that sit in their own process groups. - bin/fm-branch-report.sh: the command twin of fm_branch_report, scoped to the tasks the current host turn claimed. - bin/fm-branch-dispatch.mjs: command entry to the Pi dispatch module, so eligibility and the wake prompt have one owner. - bin/fm-claude-stop-autoarm.sh runs the host in the arm's place when config/supervision-host exists; nothing changes without the file. - bin/fm-watch-arm.sh --stop: home-scoped stop without a re-arm. - bin/fm-lease-lib.sh: an opted-in home takes the lease-command lock for unmarked main too, closing the first-claim race; the refusal tells the caller to leave the lease alone and retry. - /afk launches no away daemon on an opted-in Claude home; /quiet still does. Session start renders the host's main-side protocol there. * fix(bin): relay a host turn's outcomes when the captain returns mid-turn, and log per-turn engine cost Live validation found two supervision host gaps. A captain who returns while an engine turn is running gets a return brief rendered before that turn's outcomes exist, so the host now hands the close to main with those outcomes. Claude reports a resumed conversation's running cost, so the engine lib now derives each turn's cost from the total the host records, and the host log records every close's destination. * docs(verification): record the supervision host's live evidence The dated live results behind docs/supervision-host.md: the Claude engine's live guard, the away-wake cases against real workers, the engine's cost reporting, and the flag-off before-and-after regression. * docs: describe the supervision host ledger as covering every close * no-mistakes(review): Harden supervision host ownership, boundary, ack, and late outcomes * no-mistakes(review): Recheck park boundary just before starting an engine turn * no-mistakes(review): Cap park boundary, deliver all host lines, reject incomplete results * no-mistakes(document): Correct supervision host documentation and stale pointers
* feat(bin): supervision host core behind config/supervision-host Add the supervision host (bin/fm-supervision-host.sh): beside a Claude primary it owns the watcher cycle for the Stop auto-arm and, while the away-posture record exists, hands each wake to a bounded headless Claude engine session that runs the supervision branch's contract - the same generated prompt, row eligibility, wake grant, per-actor drain, outcome store, leases, and away relocation the Pi branch uses. Attended wakes pass straight to main. Every path that cannot finish a wake hands it to main with a supervision-host line; the park ends itself before the Stop hook timeout with a cycle-boundary wake. - bin/fm-supervision-engine-lib.sh: opt-in parse, verified engines (claude, default sonnet), one bounded engine turn, and a reap of engine tool processes that sit in their own process groups. - bin/fm-branch-report.sh: the command twin of fm_branch_report, scoped to the tasks the current host turn claimed. - bin/fm-branch-dispatch.mjs: command entry to the Pi dispatch module, so eligibility and the wake prompt have one owner. - bin/fm-claude-stop-autoarm.sh runs the host in the arm's place when config/supervision-host exists; nothing changes without the file. - bin/fm-watch-arm.sh --stop: home-scoped stop without a re-arm. - bin/fm-lease-lib.sh: an opted-in home takes the lease-command lock for unmarked main too, closing the first-claim race; the refusal tells the caller to leave the lease alone and retry. - /afk launches no away daemon on an opted-in Claude home; /quiet still does. Session start renders the host's main-side protocol there. * fix(bin): relay a host turn's outcomes when the captain returns mid-turn, and log per-turn engine cost Live validation found two supervision host gaps. A captain who returns while an engine turn is running gets a return brief rendered before that turn's outcomes exist, so the host now hands the close to main with those outcomes. Claude reports a resumed conversation's running cost, so the engine lib now derives each turn's cost from the total the host records, and the host log records every close's destination. * docs(verification): record the supervision host's live evidence The dated live results behind docs/supervision-host.md: the Claude engine's live guard, the away-wake cases against real workers, the engine's cost reporting, and the flag-off before-and-after regression. * docs: describe the supervision host ledger as covering every close * no-mistakes(review): Harden supervision host ownership, boundary, ack, and late outcomes * no-mistakes(review): Recheck park boundary just before starting an engine turn * no-mistakes(review): Cap park boundary, deliver all host lines, reject incomplete results * no-mistakes(document): Correct supervision host documentation and stale pointers
* feat(bin): supervision host core behind config/supervision-host Add the supervision host (bin/fm-supervision-host.sh): beside a Claude primary it owns the watcher cycle for the Stop auto-arm and, while the away-posture record exists, hands each wake to a bounded headless Claude engine session that runs the supervision branch's contract - the same generated prompt, row eligibility, wake grant, per-actor drain, outcome store, leases, and away relocation the Pi branch uses. Attended wakes pass straight to main. Every path that cannot finish a wake hands it to main with a supervision-host line; the park ends itself before the Stop hook timeout with a cycle-boundary wake. - bin/fm-supervision-engine-lib.sh: opt-in parse, verified engines (claude, default sonnet), one bounded engine turn, and a reap of engine tool processes that sit in their own process groups. - bin/fm-branch-report.sh: the command twin of fm_branch_report, scoped to the tasks the current host turn claimed. - bin/fm-branch-dispatch.mjs: command entry to the Pi dispatch module, so eligibility and the wake prompt have one owner. - bin/fm-claude-stop-autoarm.sh runs the host in the arm's place when config/supervision-host exists; nothing changes without the file. - bin/fm-watch-arm.sh --stop: home-scoped stop without a re-arm. - bin/fm-lease-lib.sh: an opted-in home takes the lease-command lock for unmarked main too, closing the first-claim race; the refusal tells the caller to leave the lease alone and retry. - /afk launches no away daemon on an opted-in Claude home; /quiet still does. Session start renders the host's main-side protocol there. * fix(bin): relay a host turn's outcomes when the captain returns mid-turn, and log per-turn engine cost Live validation found two supervision host gaps. A captain who returns while an engine turn is running gets a return brief rendered before that turn's outcomes exist, so the host now hands the close to main with those outcomes. Claude reports a resumed conversation's running cost, so the engine lib now derives each turn's cost from the total the host records, and the host log records every close's destination. * docs(verification): record the supervision host's live evidence The dated live results behind docs/supervision-host.md: the Claude engine's live guard, the away-wake cases against real workers, the engine's cost reporting, and the flag-off before-and-after regression. * docs: describe the supervision host ledger as covering every close * no-mistakes(review): Harden supervision host ownership, boundary, ack, and late outcomes * no-mistakes(review): Recheck park boundary just before starting an engine turn * no-mistakes(review): Cap park boundary, deliver all host lines, reject incomplete results * no-mistakes(document): Correct supervision host documentation and stale pointers
Intent
start on opus now - i will reset my quota if it gets close to running out. this is a major architectural revamp so i want it to do very careful live validation including regression in isolated live environments with some real complex sessions before calling it done. it's ok to use my real llm tokens here
This continues step 2 of the AFK revamp: rung 3 of the ladder in the AFK slices 2-3 implementation plan, the shared non-Pi supervision host, which lands as two PRs. The guards-first PR merged as #5471 (cross-process lease honoring, the supervision-branch primary-harness pin, the shared bounded-exec helper fm_exec_timed, and the Claude timeout-terminated Stop hook note). This is the second PR, the host core. The design was decided on the 2026-09-20 review board, including "Slice 3: go, starting with the spike", taking slice 3 "All the way (3a to 3e): away, attended, /quiet on the host, daemon deleted", and choosing "The primary harness's own headless mode where verified, configurable per home, starting with Claude and Pi engines" as the engine. The spike returned GO for the Claude engine and sized 3a-core as: the host loop, the Claude engine lib, fm-branch-report.sh, the dispatch CLI entry, and the Claude arm swap behind a default-off config/supervision-host flag, plus docs. The captain ruled Pi keeps its in-process supervision and no Pi engine is built now. The host runs a headless engine session beside a non-Pi primary under the same contract as Pi's in-process supervision branch - the same branch prompt, the same records (away-posture record, outcome store, per-task leases, wake queue), and FM_SUPERVISION_ACTOR=branch in its environment.
Substance of the plan and spike referenced above:
bin/fm-supervision-host.sh parkis a loop that owns watcher cycles through bin/fm-watch-arm.sh and, on each actionable close, computes the rows the branch may claim with the same rules as .pi/extensions/lib/fm-branch-dispatch.ts through a thin CLI entry (one owner of eligibility), starts the successor watcher cycle so supervision stays live, runs one bounded headless engine turn with the generated branch prompt (bin/fm-branch-prompt.sh, made host-neutral) plus the away tail carrying the away record's verbatim read-back, and counts the wake handled only when that turn appended a durable report through bin/fm-branch-report.sh, a command twin of the Pi fm_branch_report tool with the same task scoping. Every path that cannot finish a wake falls back to exiting with the close's reason line so the harness's existing wake path delivers it to main; the host adds no delivery machinery, no pane injection, and no authority the Pi branch lacks. In this PR the host runs only on a Claude primary and only takes away-posture wakes, launched by the Stop asyncRewake hook (bin/fm-claude-stop-autoarm.sh) in place of bin/fm-watch-arm.sh for a home opted in with config/supervision-host; bin/fm-afk-launch.sh start-native refuses the away daemon on such a home; docs/supervision-host.md owns the design and docs/configuration.md the opt-in; homes without the file behave exactly as before. Later PRs: 3b (the other harnesses), 3c (attended posture and /quiet on the host), 3d (default on), 3e (the away daemon deleted).claude -pwith --safe-mode (not --bare, which never reads the captain's OAuth) loads none of the home's hooks, CLAUDE.md, or session-start nudge and never touches state/.lock; --permission-mode dontAsk with --allowedTools Bash Read never prompts; FM_SUPERVISION_ACTOR reaches the engine's shell; each turn needs a TERM-grace-KILL wall-clock bound; a resumed conversation with a byte-stable prompt reuses about 98% of its reads from cache, and per-wake cost grows with conversation length, so the conversation rotates at every main session start and after a number of wakes; the default engine model is sonnet; a home outside the code root needs --add-dir; stdin must be /dev/null; and a Claude Stop hook terminated at its configured timeout never delivers its exit 2, so the host must end its own park below the hook timeout with a cycle-boundary wake.What Changed
Risk Assessment
Testing
Targeted host, hook, launcher, watcher, and branch checks passed after correcting the test harness identity; real Claude handled and resumed both single-task and two-task away wakes. The launcher check used an isolated named Herdr lab. Real-hook failure and 27,000-second boundary behavior were not driven live; no UI surface was changed, so no visual artifact was captured.
Evidence: Real Claude engine handles and resumes an away wake
Source: Real Claude engine handles and resumes an away wake
Evidence: Real Claude engine reports two task outcomes in one wake
Source: Real Claude engine reports two task outcomes in one wake
Evidence: Launcher behavior in an isolated Herdr lab
Source: Launcher behavior in an isolated Herdr lab
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 5 issues found → auto-fixed (3) ✅
bin/fm-supervision-host.sh:587- The host calls activate before checking session-lock and auto-arm generation ownership at line 591. A superseded host invocation can therefore stop the current host and watcher and release its branch leases (lines 216–237) before standing down. Check ownership before predecessor cleanup or any shared-state mutation.bin/fm-supervision-host.sh:329- The park deadline is checked only while an arm remains alive. If actionable closes are already ready on successive iterations, await_close skips that check and the host can continue past the Claude Stop-hook timeout, whose exit-2 wake is then lost. The loop at line 598 and successor/turn path at lines 644–653 must honor the deadline even when an arm has closed.bin/fm-supervision-host.sh:563- A clean engine exit and one report receipt count the wake handled without checking whether its claimed rows were acknowledged. An engine can report successfully but fail its printed --ack-through command; the host then parks silently at lines 664–669 while those rows remain queued for repeat handling. Verify consumption of the granted rows before suppressing the handback.bin/fm-supervision-host.sh:653- If the captain returns during a turn, the engine writes an outcome, then exits unsuccessfully, this failure branch hands back only the close and error. The late-outcome relay at lines 658–661 runs only after a successful turn, so that outcome misses both the already-rendered return brief and the handback. Include recorded turn outcomes on failure handbacks too.bin/fm-supervision-engine-lib.sh:246- A tool can spawn a long-lived process in its own process group and the engine can exit between the initial descendant snapshot and the next one-second poll. That process never enters the ledger and survives the turn despite the claimed reap bound. The remedy needs a reliable way to retain descendant identity across that interval; authorize that extension rather than merely shortening the polling interval.🔧 Fix applied.
5 issues (3 errors, 2 warnings) still open:
bin/fm-supervision-host.sh:587- The host calls activate before checking session-lock and auto-arm generation ownership at line 591. A superseded host invocation can therefore stop the current host and watcher and release its branch leases (lines 216–237) before standing down. Check ownership before predecessor cleanup or any shared-state mutation.bin/fm-supervision-host.sh:329- The park deadline is checked only while an arm remains alive. If actionable closes are already ready on successive iterations, await_close skips that check and the host can continue past the Claude Stop-hook timeout, whose exit-2 wake is then lost. The loop at line 598 and successor/turn path at lines 644–653 must honor the deadline even when an arm has closed.bin/fm-supervision-host.sh:563- A clean engine exit and one report receipt count the wake handled without checking whether its claimed rows were acknowledged. An engine can report successfully but fail its printed --ack-through command; the host then parks silently at lines 664–669 while those rows remain queued for repeat handling. Verify consumption of the granted rows before suppressing the handback.bin/fm-supervision-host.sh:653- If the captain returns during a turn, the engine writes an outcome, then exits unsuccessfully, this failure branch hands back only the close and error. The late-outcome relay at lines 658–661 runs only after a successful turn, so that outcome misses both the already-rendered return brief and the handback. Include recorded turn outcomes on failure handbacks too.bin/fm-supervision-host.sh:680- Round 1's boundary fix left a sibling gap: the check runs before start_successor, which may take up to READY_TIMEOUT (line 681), and prompt preparation (lines 503–564). A close arriving with exactly TURN_TIMEOUT + ENGINE_GRACE remaining can therefore start its engine turn at line 567 after that time has elapsed and run past the park boundary without a boundary handback. Recheck immediately before starting the turn and hand the already-read close to main if insufficient time remains.🔧 Fix applied.
8 issues (4 errors, 4 warnings) still open:
bin/fm-supervision-host.sh:587- The host calls activate before checking session-lock and auto-arm generation ownership at line 591. A superseded host invocation can therefore stop the current host and watcher and release its branch leases (lines 216–237) before standing down. Check ownership before predecessor cleanup or any shared-state mutation.bin/fm-supervision-host.sh:329- The park deadline is checked only while an arm remains alive. If actionable closes are already ready on successive iterations, await_close skips that check and the host can continue past the Claude Stop-hook timeout, whose exit-2 wake is then lost. The loop at line 598 and successor/turn path at lines 644–653 must honor the deadline even when an arm has closed.bin/fm-supervision-host.sh:563- A clean engine exit and one report receipt count the wake handled without checking whether its claimed rows were acknowledged. An engine can report successfully but fail its printed --ack-through command; the host then parks silently at lines 664–669 while those rows remain queued for repeat handling. Verify consumption of the granted rows before suppressing the handback.bin/fm-supervision-host.sh:653- If the captain returns during a turn, the engine writes an outcome, then exits unsuccessfully, this failure branch hands back only the close and error. The late-outcome relay at lines 658–661 runs only after a successful turn, so that outcome misses both the already-rendered return brief and the handback. Include recorded turn outcomes on failure handbacks too.bin/fm-supervision-host.sh:680- Round 1's boundary fix left a sibling gap: the check runs before start_successor, which may take up to READY_TIMEOUT (line 681), and prompt preparation (lines 503–564). A close arriving with exactly TURN_TIMEOUT + ENGINE_GRACE remaining can therefore start its engine turn at line 567 after that time has elapsed and run past the park boundary without a boundary handback. Recheck immediately before starting the turn and hand the already-read close to main if insufficient time remains.bin/fm-supervision-host.sh:116- The required design says the host must end its park below Claude's 28,800-second Stop-hook timeout. This hunk accepts any positive FM_SUPERVISION_HOST_PARK_SECONDS; setting it to 28,800 or more makes boundary_reached (line 327) wait until the hook has terminated and dropped its exit-2 wake. The remedy is a policy decision about whether to reject or cap an explicitly supplied value below that timeout.bin/fm-claude-stop-autoarm.sh:398- A turn can record several outcomes, which the host prints at bin/fm-supervision-host.sh:403 and :705–711, but this head -8 drops later outcome lines from the Stop-hook feedback. For example, a close followed by a handback and eight recorded outcomes cannot deliver them all for main to relay. Preserve the host's outcome lines when translating its close.bin/fm-supervision-engine-lib.sh:290- A clean-exiting engine that reports and acknowledges can emit{}; this parser calls it error=0 with zero cost, and the host counts the wake handled at bin/fm-supervision-host.sh:613. Reject an incomplete result rather than defaulting missing fields to success. Validate the consumed type, subtype, is_error, total_cost_usd, num_turns, and usage token fields (input, cache-read, cache-creation, output).🔧 Fix applied.
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
tests/fm-supervision-host.test.shtests/fm-claude-stop-autoarm.test.shtests/fm-afk-launch.test.shunder a Claude-named harness; an initial run inherited Pi ancestry and was rerun with the correct harnessbash tests/fm-branch-supervision.test.shbash tests/fm-watch-arm.test.shbash tests/fm-supervision-instructions.test.shFM_SUPERVISION_HOST_LIVE_E2E=1 tests/fm-supervision-host-live-e2e.test.shwith real Claude CodeA temporary two-task variant of the live Claude test, removed after execution✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.