Skip to content

feat: extend opt-in away supervision to non-Pi primaries - #5503

Merged
kunchenguid merged 11 commits into
mainfrom
fm/fm-afk-host-harnesses-r1
Sep 24, 2026
Merged

kunchenguid merged 11 commits into
mainfrom
fm/fm-afk-host-harnesses-r1

Conversation

@kunchenguid

@kunchenguid kunchenguid commented Sep 24, 2026 •

Copy link
Copy Markdown
Owner

Intent

yes
(to: merge #5488 and start 3b on Opus)

Standing words for this AFK revamp: "2 should be done by opus crewmates" and "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"

The end state the captain expects, in his words: "non-afk mode gains pi-like supervision session which offloads main session work. in afk mode it activates a different posture where everything goes to supervision session". The approved plan (data/fm-afk-slices23-plan-s1/report.md) gets there in rungs: 3a (merged as 5488) built the supervision host with the Claude engine handling away wakes behind a default-off config/supervision-host flag; this task is rung 3b: the other five non-Pi harness arm owners (Cursor, OpenCode, omp, Grok, Codex) run the host under that same flag while away, including the Codex checkpoint bound while away. Attended posture and /quiet (3c), the default-on flip (3d), and daemon retirement (3e) come later.
The approved plan's rung 3b item, by reference: "the other five harnesses under away ... Each arm owner swaps its arm command for the host under the same flag; Codex's checkpoint bound; protocol docs under docs/supervision-protocols/."

What Changed

  • Route Cursor, OpenCode, omp, Grok, and Codex arm owners through the supervision host when config/supervision-host is present, while leaving /quiet and unconfigured homes on their existing paths.
  • Give Codex a longer bounded away checkpoint and update host handoff, ownership recovery, and post-return outcome delivery across the harnesses.
  • Update the away-mode protocols and add harness, host, checkpoint, and wake-queue regression coverage.

Risk Assessment

⚠️ Medium: The opt-in change spans six primary harnesses and asynchronous handoffs, but this review found no substantiated remaining defect or intent contradiction.

Testing

Live validation before this run

Before validation, the flag-on path was driven live in isolated lab homes on private tmux sockets, with real primaries supervising real workers through attended work, an away window, a pre-answered decision, a named steer, a stopped worker, lease contention, the park boundary, and a return during an engine turn.
Codex, Cursor, Grok, OpenCode (on OpenCode's free opencode/big-pickle, because this machine's OpenAI login rejects gpt-5.6-sol and has exhausted gpt-5.5), and Claude primaries all ran through the host while away, and Codex, Cursor, and Grok also ran flag-off regression pairs against the tree before this change with identical results.
omp was not live-tested: it is not installed on the measuring machine, so its routing rests on tests/fm-omp-harness.test.sh alone.
The dated record, with versions, per-case results, costs, and the before/after live guard table, is docs/verification/supervision.md, "Non-Pi primaries".
The AFK-launch and omp suites the pipeline's test run could not credit under Pi ancestry pass from a non-Pi shell at 1bf367f4 (tests/fm-omp-harness.test.sh 14 of 14, tests/fm-afk-launch.test.sh 77 of 77).

Pipeline test run

The focused host, checkpoint, Cursor, instruction, and wake checks passed; a real Claude engine handled and resumed two away wakes, and real Codex completed a foreground checkpoint. AFK-launch and omp fixture suites were confounded by the invoking Pi ancestry. No real opt-in session was driven for the five new primary arm owners; the working tree is clean.

  • Live validation: ⚠️ inconclusive - 2 of 5 scenarios driven live against the product
Scenario Result Live Evidence
An away host handles successive real wakes through its Claude engine without waking main ✅ pass live host-live.log records two handled turns, a persisted branch outcome, and the resumed conversation
A real Codex primary runs a bounded foreground checkpoint rather than switching to a background arm ✅ pass live FM_CODEX_LIVE_E2E=1 tests/fm-codex-continuity-live-e2e.test.sh completed its real Codex checkpoint
Opted-in Cursor, OpenCode, Grok, Codex, and omp primaries route away arm cycles through the host and leave attended behavior unchanged ⏸️ untested no This run did not provision real opted-in primary sessions for those five paths. Provide isolated harness sessions and credentials for Cursor, OpenCode, Grok, and Codex to drive them; omp is not on PAT…
A split omp host close delivers its signal and later outcome in one follow-up, including after a host-only boundary handoff ⏸️ untested no Run the focused suite from a non-Pi parent shell, then provide an omp binary on PATH and an isolated real omp session for live confirmation.
Opted-in away entry avoids the daemon while quiet and non-opted-in entries retain their existing lifecycle ⏸️ untested no A non-Pi parent shell is needed to rerun the harness-dependent AFK lifecycle checks without the invoking Pi process overriding the intended primary identity.
Evidence: Real host-engine turns and recorded branch outcome

Source: Real host-engine turns and recorded branch outcome

# first turn: handled	turn=host-64262-1790241664.1	rc=0	reports=1
# outcome: {"seq":1,"epoch":1790241686,"task":"demo","wake":"signal: /private/var/folders/0k/bf8mwt2n5qddzk24r20gfk0c0000gn/T/fm-supervision-host-live.ch7z2w/fm/state/demo.status","verdict":"routine","summary":"Demo task's status already shows its cleanup finished with nothing else needed; no live work or worktree remains, so no action taken.","silent":false,"statusEndpoint":72,"statusIdent":"strong:16777232:301567924:1790241665.739385543"}
# second turn: handled	turn=host-64262-1790241664.2	rc=0	reports=1
ok - supervision host live (2.1.281 (Claude Code)): a real engine handles and resumes away wakes under the branch contract without waking main
Evidence: Focused checks and Pi-ancestry failures

Source: Focused checks and Pi-ancestry failures

=== fm-supervision-host ===
ok - report surface: only the branch actor's current turn may report, and only on the tasks its wake names
ok - report surface: an outcome recorded after the captain returned is queued durably for main
ok - dispatch entry: the host reads branch eligibility and the wake prompt from the Pi branch's own owner
ok - host: an attended close reaches main exactly as the plain arm delivers it
ok - host: an away wake is handled on the engine through the branch contract and never reaches main
ok - host: an engine turn that records no outcome hands its durable wake to main
ok - host: a captain return during an engine turn hands that turn's outcomes to main
ok - host: an outcome recorded after the return reaches main even when its host dies at the turn's end
ok - host: the next host stops the engine a killed predecessor left mid-turn and removes that turn's files
ok - host: a turn that reports but leaves its granted rows queued hands the wake to main
ok - host: a captain return during a failed engine turn still hands that turn's outcomes to main
ok - host: an engine turn whose result is incomplete hands its wake to main
ok - host: an engine turn is bounded, and tool processes outside its process group are reaped
ok - host: a restarted host stops, by recorded identity, the cycle a killed predecessor left running
ok - host: the park ends itself with a boundary wake and a stopped watcher
ok - host: waiting closes cannot carry the park past its boundary
ok - host: a close whose margin runs out while the successor starts reaches main at the boundary without a turn
ok - host: a park at or beyond the Stop-hook registration falls back to the default boundary
ok - host: an owner's later park limit lets a turn outlive the boundary, and no other limit does
ok - host: the first cycle's status streams once, --restart replaces a stale watcher, and an owner predecessor makes a handling successor
ok - host: a home naming an unverified engine hands every away wake to main with the reason
ok - host: a host outside the session-lock owner stands down without arming
ok - host: a host under a superseded auto-arm generation stands down without touching the owner
exit=0
=== fm-afk-launch ===
ok - clear-stale: removes escalations buffer, sidecar, and wedge marker
ok - clear-stale: leaves the durable wake-queue intact (no pending work dropped)
ok - enter: one call writes the record with the words, expected return, and spend cap, reads it back without asking for a go, and launches no daemon
ok - enter: the retired --grant flag is refused by name and leaves the standing record alone
ok - enter: refuses while the prior return catch-up is pending
ok - propose: the retired wait-for-go step is refused by name, writes nothing, and releases the launcher lock
ok - confirm: the retired wait-for-go step is refused by name, writes nothing, and releases the launcher lock
ok - pi: start refuses to launch the daemon and writes no state
ok - pi: start-native refuses to prepare a daemon
ok - pi-signed: start refuses to launch the daemon and writes no state
ok - pi-signed: start-native refuses to prepare a daemon
ok - pi enter stop: reports that no daemon terminal was running
not ok - daemon entry: started without a record or the refusal was unclear (rc=1): fm-afk-launch: the away daemon is no longer launched on pi; the away-posture record is the posture there (run bin/fm-afk-launch.sh enter and stop)
not ok - daemon entry: the record enter wrote did not permit lifecycle preparation
ok - failed start: preserves the posture record enter wrote
not ok - stop archive: native entry failed
ok - stop: clears the away flag and archives the posture record under its entry time
ok - launcher paths: relative home and state ignore CDPATH before daemon command construction
ok - launcher paths: absolute symlink spellings are preserved
ok - launcher paths: unresolved relative FM_HOME fails loudly
ok - launcher paths: unresolved relative FM_STATE_OVERRIDE fails loudly
ok - refresh: daemon already alive - stale artifacts preserved (current session's buffer kept)
ok - mode: a fresh entry with FM_AFK_MODE=quiet writes quiet
ok - mode: a fresh entry with FM_AFK_MODE unset defaults to away
ok - mode: a bare refresh (FM_AFK_MODE unset) of an already-running quiet daemon preserves quiet, never resets to away
ok - mode: an empty (legacy pre-mode) flag reads as away
ok - mode: a bare-epoch-timestamp (legacy pre-mode) flag reads as away
ok - mode: unrecognized content falls back to away
ok - mode: a missing flag reads as away
ok - stop-ordering: daemon SIGTERM'd while .afk still present (flush is not a no-op)
ok - stop-ordering: .afk cleared last
ok - stop-ordering: daemon-terminal record removed
ok - stop identity: stale lock cannot signal an unrelated live process
ok - failed start: away flag and delivery artifacts roll back
not ok - concurrent start: leaked or lost daemon terminal (count 0, record )
ok - launcher lock: incomplete publication receives initialization grace
ok - launcher signal: TERM exits and releases the lifecycle lock
fm-afk-launch: daemon launched in non-visible herdr workspace ws-partial (pane lab:pane-exact), supervising lab:captain
ok - herdr create: malformed response recovers durable exact ownership
fm-afk-launch: herdr create failed after returning exact ids; closing lab:pane-exact
fm-afk-launch: recorded terminal teardown is unconfirmed; preserving exact id
ok - herdr create error: unconfirmed exact id is persisted for reconciliation
fm-afk-launch: failed to run daemon in herdr pane lab:pane-exact; closing it
fm-afk-launch: recorded terminal teardown is unconfirmed; preserving exact id
ok - herdr run failure: unconfirmed exact id remains reconcilable
fm-afk-launch: failed to persist daemon terminal record; closing tmux:exact-session
ok - record failure: newly created terminal is closed by exact id
fm-afk-launch: daemon did not become ready; closing tmux:exact-session
ok - readiness failure: exact terminal and durable record roll back
fm-afk-launch: daemon did not become ready; closing tmux:exact-session
fm-afk-launch: recorded terminal teardown is unconfirmed; preserving exact id
ok - readiness failure: unconfirmed terminal retains its reconciliation id
ok - tmux absence: clean missing differs from transport probe failure
not ok - native lifecycle: state preparation or no-terminal record failed
ok - native lifecycle: uniform stop clears state without closing a terminal
not ok - supervision host: away start-native did not refuse cleanly (rc=1): fm-afk-launch: the away daemon is no longer launched on pi; the away-posture record is the posture there (run bin/fm-afk-launch.sh enter and stop)
not ok - supervision host: quiet mode was refused or lost its mode on a claude host home
ok - supervision host: away mode on an opted-in cursor, opencode, omp, grok, or codex home launches no daemon
ok - supervision host: enter names a missing engine on an opted-in home and says nothing otherwise
ok - native entry: launcher-prepared lifecycle state is not rewritten
fm-afk-launch: reconciling leaked daemon terminal tmux:exact-session
fm-afk-launch: recorded terminal teardown is unconfirmed; preserving exact id
ok - teardown failure: exact terminal record is preserved
ok - record publication: failed atomic rename preserves the complete prior record
fm-afk-launch: daemon terminal record is malformed; refusing to act on it
ok - record read: malformed record fails closed without acting on a partial id
fm-afk-launch: daemon terminal record is malformed; refusing to act on it
fm-afk-launch: malformed daemon terminal record; refusing to stop away mode
ok - stop: malformed terminal record preserves away state and fails closed
fm-afk-launch: failed to create detached tmux daemon session 'fm-afk-daemon-2972420433-55268-17007-1790241372'
ok - tmux launch: planned exact target is recorded before creation and removed on failure
fm-afk-launch: failed to create detached tmux daemon session 'fm-afk-daemon-2363188043-55382-8940-1790241372'
ok - tmux launch: unique names eliminate collision teardown
ok - stop validation: malformed record causes no daemon or state side effects
ok - launcher lock: inc

... [7879 bytes truncated] ...

arker crash window
ok - empty prefix mate cleanup preserves another mate's stall receipt
ok - self-announced appends suppress only their own bytes and fail toward waking
ok - separate self-announced answers after a fold stay owned; worker decisions and later lines still wake
ok - an unreadable status still reads as unreported, with or without owned growth
ok - a worker resolved in fold lag still wakes after this home's close
ok - owned growth suppresses the wake without hiding the turn-ended annotation
ok - historical annotations replay nothing already announced and keep everything new
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 2 --recovery-generation 67014.1790241525.rYpert
ok - concurrent append plus drain preserves durable records through acknowledgement
ok - signal written while no watcher runs is caught on next run
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 97939.1790241541.dWdvz6
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  1 task(s) in flight, but no live watcher process holds this home lock (last beat: 2s ago).
●  Trust the emitted supervision protocol for this harness; do not use shell & for watcher repair.
●  This is a supervision warning only; the guarded operation WILL still run.
●  After draining queued wakes, repair a missing or failed watcher cycle with the Pi tool fm_watch_arm_pi, or restart Pi with -e /private/var/folders/0k/bf8mwt2n5qddzk24r20gfk0c0000gn/T/fm-wake-tangle-root.aXOvDy/.pi/extensions/fm-primary-turnend-guard.ts -e /private/var/folders/0k/bf8mwt2n5qddzk24r20gfk0c0000gn/T/fm-wake-tangle-root.aXOvDy/.pi/extensions/fm-primary-pi-watch.ts if the extensions are not loaded.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
WARNING: queued wakes pending - drain them with bin/fm-wake-drain.sh before anything else.
ok - stale wake is queued before suppressor state is advanced
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 2926.1790241544.mFPWZ9
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  1 task(s) in flight, but no live watcher process holds this home lock (last beat: 2s ago).
●  Trust the emitted supervision protocol for this harness; do not use shell & for watcher repair.
●  This is a supervision warning only; the guarded operation WILL still run.
●  After draining queued wakes, repair a missing or failed watcher cycle with the Pi tool fm_watch_arm_pi, or restart Pi with -e /private/var/folders/0k/bf8mwt2n5qddzk24r20gfk0c0000gn/T/fm-wake-tangle-root.aXOvDy/.pi/extensions/fm-primary-turnend-guard.ts -e /private/var/folders/0k/bf8mwt2n5qddzk24r20gfk0c0000gn/T/fm-wake-tangle-root.aXOvDy/.pi/extensions/fm-primary-pi-watch.ts if the extensions are not loaded.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
WARNING: queued wakes pending - drain them with bin/fm-wake-drain.sh before anything else.
ok - a not-provably-working stale wake is queued before its suppressor is advanced
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 6544.1790241546.mFtm9J
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  1 registered custom check(s), but no live watcher process holds this home lock (last beat: 1s ago).
●  Trust the emitted supervision protocol for this harness; do not use shell & for watcher repair.
●  This is a supervision warning only; the guarded operation WILL still run.
●  After draining queued wakes, repair a missing or failed watcher cycle with the Pi tool fm_watch_arm_pi, or restart Pi with -e /private/var/folders/0k/bf8mwt2n5qddzk24r20gfk0c0000gn/T/fm-wake-tangle-root.aXOvDy/.pi/extensions/fm-primary-turnend-guard.ts -e /private/var/folders/0k/bf8mwt2n5qddzk24r20gfk0c0000gn/T/fm-wake-tangle-root.aXOvDy/.pi/extensions/fm-primary-pi-watch.ts if the extensions are not loaded.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
WARNING: queued wakes pending - drain them with bin/fm-wake-drain.sh before anything else.
ok - registered custom check output is queued before cadence suppression
ok - concurrent drains replay until one post-handling acknowledgement consumes records
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 4 --recovery-generation 10680.1790241549.BTQ1AZ
ok - drain collapses obvious duplicate heartbeat and signal records
ok - drain asserts watcher liveness: warns on a lapse, stays silent for a live watcher with a fresh beacon
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 8 --recovery-generation 13492.1790241550.36rQlV
ok - structural signal enrichment is separate, deduped, home-local, and tier-zero for other wakes
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 13 --recovery-generation 16203.1790241552.wsy6f8
ok - every readable unread status line is annotated in full while invalid status files preserve their raw wakes
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 20699.1790241555.MsMu7V
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 2 --recovery-generation 20699.1790241555.MsMu7V
ok - slow annotation releases the append lock and a deleted status file fails open
ok - a branch-actor scoped ack never swallows an unacked main-owned row, and main's later drain sees exactly what remains
ok - main drain and acknowledgement exclude an active branch grant
ok - a branch-held row raises no queued-wake warning for main, and the same row is presented and acknowledged once the grant clears
ok - a queue that cannot be counted keeps the queued-wake alarm up
ok - structurally unusable rows are retired by main alone, leaving every remaining row presentable and acknowledgeable
ok - branch grant cannot take a row already claimed by main
ok - main's acknowledgement leaves a row that arrived after its drain for whichever actor takes it next
ok - actor ownership filtering precedes same-key deduplication
ok - main reclaims rows granted to an exited branch owner
ok - a branch-actor drain with no eligible-row snapshot refuses loudly instead of draining nothing
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation existing
ok - wake append publishes atomic recovery evidence before durable rows
ok - wake drain: generation-less legacy wakes are adopted and acknowledged
ok - wake drain: a stale acknowledgement cannot retire or consume a newer recovery episode
ok - wake drain: an acknowledgement that consumes nothing says so and names the exact command for the current wake
ok - wake drain: a branch acknowledgement that consumes nothing names the exact command for its granted wake
ok - wake drain: recovery acknowledgement failures are explicit and retryable
ok - interruptions preserve durable rows until post-handling acknowledgement
ok - fm_wake_queue_prune_task: prunes wakes for target task without touching other tasks
exit=0
Evidence: Repeat with Pi environment variables removed

Source: Repeat with Pi environment variables removed

=== fm-afk-launch (clean harness environment) ===
ok - clear-stale: removes escalations buffer, sidecar, and wedge marker
ok - clear-stale: leaves the durable wake-queue intact (no pending work dropped)
ok - enter: one call writes the record with the words, expected return, and spend cap, reads it back without asking for a go, and launches no daemon
ok - enter: the retired --grant flag is refused by name and leaves the standing record alone
ok - enter: refuses while the prior return catch-up is pending
ok - propose: the retired wait-for-go step is refused by name, writes nothing, and releases the launcher lock
ok - confirm: the retired wait-for-go step is refused by name, writes nothing, and releases the launcher lock
ok - pi: start refuses to launch the daemon and writes no state
ok - pi: start-native refuses to prepare a daemon
ok - pi-signed: start refuses to launch the daemon and writes no state
ok - pi-signed: start-native refuses to prepare a daemon
ok - pi enter stop: reports that no daemon terminal was running
not ok - daemon entry: started without a record or the refusal was unclear (rc=1): fm-afk-launch: the away daemon is no longer launched on pi; the away-posture record is the posture there (run bin/fm-afk-launch.sh enter and stop)
not ok - daemon entry: the record enter wrote did not permit lifecycle preparation
ok - failed start: preserves the posture record enter wrote
not ok - stop archive: native entry failed
ok - stop: clears the away flag and archives the posture record under its entry time
ok - launcher paths: relative home and state ignore CDPATH before daemon command construction
ok - launcher paths: absolute symlink spellings are preserved
ok - launcher paths: unresolved relative FM_HOME fails loudly
ok - launcher paths: unresolved relative FM_STATE_OVERRIDE fails loudly
ok - refresh: daemon already alive - stale artifacts preserved (current session's buffer kept)
ok - mode: a fresh entry with FM_AFK_MODE=quiet writes quiet
ok - mode: a fresh entry with FM_AFK_MODE unset defaults to away
ok - mode: a bare refresh (FM_AFK_MODE unset) of an already-running quiet daemon preserves quiet, never resets to away
ok - mode: an empty (legacy pre-mode) flag reads as away
ok - mode: a bare-epoch-timestamp (legacy pre-mode) flag reads as away
ok - mode: unrecognized content falls back to away
ok - mode: a missing flag reads as away
ok - stop-ordering: daemon SIGTERM'd while .afk still present (flush is not a no-op)
ok - stop-ordering: .afk cleared last
ok - stop-ordering: daemon-terminal record removed
ok - stop identity: stale lock cannot signal an unrelated live process
ok - failed start: away flag and delivery artifacts roll back
not ok - concurrent start: leaked or lost daemon terminal (count 0, record )
ok - launcher lock: incomplete publication receives initialization grace
ok - launcher signal: TERM exits and releases the lifecycle lock
fm-afk-launch: daemon launched in non-visible herdr workspace ws-partial (pane lab:pane-exact), supervising lab:captain
ok - herdr create: malformed response recovers durable exact ownership
fm-afk-launch: herdr create failed after returning exact ids; closing lab:pane-exact
fm-afk-launch: recorded terminal teardown is unconfirmed; preserving exact id
ok - herdr create error: unconfirmed exact id is persisted for reconciliation
fm-afk-launch: failed to run daemon in herdr pane lab:pane-exact; closing it
fm-afk-launch: recorded terminal teardown is unconfirmed; preserving exact id
ok - herdr run failure: unconfirmed exact id remains reconcilable
fm-afk-launch: failed to persist daemon terminal record; closing tmux:exact-session
ok - record failure: newly created terminal is closed by exact id
fm-afk-launch: daemon did not become ready; closing tmux:exact-session
ok - readiness failure: exact terminal and durable record roll back
fm-afk-launch: daemon did not become ready; closing tmux:exact-session
fm-afk-launch: recorded terminal teardown is unconfirmed; preserving exact id
ok - readiness failure: unconfirmed terminal retains its reconciliation id
ok - tmux absence: clean missing differs from transport probe failure
not ok - native lifecycle: state preparation or no-terminal record failed
ok - native lifecycle: uniform stop clears state without closing a terminal
not ok - supervision host: away start-native did not refuse cleanly (rc=1): fm-afk-launch: the away daemon is no longer launched on pi; the away-posture record is the posture there (run bin/fm-afk-launch.sh enter and stop)
not ok - supervision host: quiet mode was refused or lost its mode on a claude host home
ok - supervision host: away mode on an opted-in cursor, opencode, omp, grok, or codex home launches no daemon
ok - supervision host: enter names a missing engine on an opted-in home and says nothing otherwise
ok - native entry: launcher-prepared lifecycle state is not rewritten
fm-afk-launch: reconciling leaked daemon terminal tmux:exact-session
fm-afk-launch: recorded terminal teardown is unconfirmed; preserving exact id
ok - teardown failure: exact terminal record is preserved
ok - record publication: failed atomic rename preserves the complete prior record
fm-afk-launch: daemon terminal record is malformed; refusing to act on it
ok - record read: malformed record fails closed without acting on a partial id
fm-afk-launch: daemon terminal record is malformed; refusing to act on it
fm-afk-launch: malformed daemon terminal record; refusing to stop away mode
ok - stop: malformed terminal record preserves away state and fails closed
fm-afk-launch: failed to create detached tmux daemon session 'fm-afk-daemon-4113373448-41240-7841-1790241646'
ok - tmux launch: planned exact target is recorded before creation and removed on failure
fm-afk-launch: failed to create detached tmux daemon session 'fm-afk-daemon-2129990968-41344-18061-1790241646'
ok - tmux launch: unique names eliminate collision teardown
ok - stop validation: malformed record causes no daemon or state side effects
ok - launcher lock: incomplete metadata fails acquisition and releases lock
fm-afk-launch: failed to clear away-mode flag
fm-afk-launch: away mode stopped; terminal teardown or the record archive remains recorded for retry
ok - stop state: away-flag removal failure is surfaced
fm-afk-launch: away-mode daemon did not exit after SIGTERM; preserving lifecycle state
ok - stop liveness: captured live daemon preserves lifecycle state after lock release
fm-afk-launch: the away daemon is no longer launched on pi; the away-posture record is the posture there (run bin/fm-afk-launch.sh enter and stop)
fm-afk-launch: the away daemon is no longer launched on pi; the away-posture record is the posture there (run bin/fm-afk-launch.sh enter and stop)
ok - refresh record: malformed terminal identity fails closed
fm-afk-launch: the away daemon is no longer launched on pi; the away-posture record is the posture there (run bin/fm-afk-launch.sh enter and stop)
ok - clear failure: native entry aborts and restores prior state
fm-afk-launch: reconciling leaked daemon terminal tmux:exact-session
fm-afk-launch: terminal close command failed, but exact absence was confirmed
ok - confirmed absence: cleanup succeeds and removes the stale record
fm-afk-launch: rollback restoration incomplete; backup retained at /var/folders/0k/bf8mwt2n5qddzk24r20gfk0c0000gn/T//fm-afk-restore-fail.RTINon/state/.afk-launch-backup.5D7kQK
ok - rollback restore: incomplete restoration retains its recovery backup
fm-afk-launch: the away daemon is no longer launched on pi; the away-posture record is the posture there (run bin/fm-afk-launch.sh enter and stop)
ok - flag failure: lifecycle aborts without active state
ok - herdr e2e: captain tab pane count unchanged after start (no split)
not ok - herdr e2e: no separate daemon workspace created
not ok - herdr e2e: daemon pane shares the captain tab ()
not ok - herdr e2e: daemon terminal not in the lab session ()
ok - herdr e2e: captain tab pane count restored after stop
ok - herdr e2e: daemon workspace removed by exact id on stop
ok - herdr e2e: record + .afk cleared on stop
ok - tmux e2e: captain window pane count unchanged after start (no split-window)
not ok - tmux e2e: no separate daemon session ()
ok - tmux e2e: captain window pane count unchanged after stop
not ok - tmux e2e: daemon session leaked ()
ok - tmux e2e: record + .afk cleared on stop
exit=1
=== fm-omp-harness (clean harness environment) ===
not ok - a leaked FM_OMP_HARNESS without an omp ancestor must not relabel a claude worker, got 'pi'
exit=1
- Outcome: ⚠️ 3 warnings across 1 run (11m23s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 1 issue found → auto-fixed (2) ✅
  • ⚠️ .omp/extensions/fm-primary-omp-watch.ts:322 - A host cycle boundary can produce only a supervision-host: line (bin/fm-supervision-host.sh:406). The changed host classifier treats it as actionable (.omp/extensions/fm-primary-omp-watch.ts:422), but replacement-handoff validation here still requires a signal/stale/check/heartbeat line. If omp replaces its session before consuming the boundary follow-up, the persisted handoff fails to load and that follow-up is not replayed. Accept the host-only boundary as a valid pending actionable message.

🔧 Fix applied.
2 warnings still open:

  • ⚠️ .omp/extensions/fm-primary-omp-watch.ts:322 - A host cycle boundary can produce only a supervision-host: line (bin/fm-supervision-host.sh:406). The changed host classifier treats it as actionable (.omp/extensions/fm-primary-omp-watch.ts:422), but replacement-handoff validation here still requires a signal/stale/check/heartbeat line. If omp replaces its session before consuming the boundary follow-up, the persisted handoff fails to load and that follow-up is not replayed. Accept the host-only boundary as a valid pending actionable message.
  • ⚠️ .omp/extensions/fm-primary-omp-watch.ts:990 - The round 1 fix made host-only handoffs replayable, but left a sibling delivery path incomplete. If the host's exit output arrives in multiple stream chunks, a completed signal: line can enqueue and deliver the follow-up before later supervision-host: outcome lines arrive. The close handler at line 1022 updates only an undelivered record, so main can miss those outcomes even though the host completed normally. In host mode, wait for the child close before delivering its actionable message; continue streaming the readiness line.

🔧 Fix applied.
✅ Re-checked - no issues remain.

⚠️ **Test** - 3 warnings
  • ⚠️ The flagged Cursor, OpenCode, Grok, and omp primary-to-host paths were not exercised in real harness sessions. The focused adapter checks use fixtures, so this run cannot demonstrate the full five-harness away-mode intent. Run isolated opt-in primary sessions to establish that evidence.
  • ⚠️ The AFK-launch and omp harness suites failed when their subprocesses identified the invoking Pi process as their harness. Removing Pi environment variables did not remove that process ancestry. Rerun these suites from a non-Pi shell; their Herdr and tmux topology failures cannot be credited as product failures from this run.
  • ⚠️ live validation verdict: inconclusive (2 of 5 scenarios were driven live against the product); untested: Opted-in Cursor, OpenCode, Grok, Codex, and omp primaries route away arm cycles through the host and leave attended behavior unchanged, A split omp host close delivers its signal and later outcome in one follow-up, including after a host-only boundary handoff, Opted-in away entry avoids the daemon while quiet and non-opted-in entries retain their existing lifecycle
  • Live validation: ⚠️ inconclusive - 2 of 5 scenarios driven live against the product
Scenario Result Live Evidence
An away host handles successive real wakes through its Claude engine without waking main ✅ pass live host-live.log records two handled turns, a persisted branch outcome, and the resumed conversation
A real Codex primary runs a bounded foreground checkpoint rather than switching to a background arm ✅ pass live FM_CODEX_LIVE_E2E=1 tests/fm-codex-continuity-live-e2e.test.sh completed its real Codex checkpoint
Opted-in Cursor, OpenCode, Grok, Codex, and omp primaries route away arm cycles through the host and leave attended behavior unchanged ⏸️ untested no This run did not provision real opted-in primary sessions for those five paths. Provide isolated harness sessions and credentials for Cursor, OpenCode, Grok, and Codex to drive them; omp is not on PAT…
A split omp host close delivers its signal and later outcome in one follow-up, including after a host-only boundary handoff ⏸️ untested no Run the focused suite from a non-Pi parent shell, then provide an omp binary on PATH and an isolated real omp session for live confirmation.
Opted-in away entry avoids the daemon while quiet and non-opted-in entries retain their existing lifecycle ⏸️ untested no A non-Pi parent shell is needed to rerun the harness-dependent AFK lifecycle checks without the invoking Pi process overriding the intended primary identity.
  • tests/fm-supervision-host.test.sh, tests/fm-watch-checkpoint.test.sh, tests/fm-cursor-primary.test.sh, tests/fm-supervision-instructions.test.sh, and tests/fm-wake-queue.test.sh
  • tests/fm-afk-launch.test.sh and tests/fm-omp-harness.test.sh, repeated with Pi environment variables removed
  • FM_SUPERVISION_HOST_LIVE_E2E=1 tests/fm-supervision-host-live-e2e.test.sh
  • FM_CODEX_LIVE_E2E=1 tests/fm-codex-continuity-live-e2e.test.sh
  • git status --short after testing
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

… while away

Cursor's stop-hook park, the OpenCode plugin, the omp watch extension, Grok's
model-owned background arm, and Codex's foreground checkpoint now run
bin/fm-supervision-host.sh in the watcher arm's place when the home opted in
with config/supervision-host, so the host's Claude engine takes away-posture
wakes beside those primaries exactly as it does beside Claude. Without the
file nothing changes.

- The host streams its first cycle's status line, accepts --restart and the
  owner's predecessor arm for its first cycle, and prints each exit in one
  write, so owners that wait for arm readiness and restart their own
  successor (OpenCode, omp) keep their handling handoff.
- Codex's checkpoint passes its bound to the host as the park boundary,
  raises it to FM_CODEX_WATCH_CHECKPOINT_AWAY (3600 s) while the away record
  exists, and lets an engine turn that starts before the bound finish after
  it (FM_SUPERVISION_HOST_PARK_LIMIT).
- /afk launches no away daemon on an opted-in home of those harnesses and
  says so at entry when the file selects no engine for that primary.
- Session start renders the host protocol for each arm owner, and Grok's
  arm command becomes the host.
…ch actor

A supervision host's engine turn runs guarded commands after its successor
watcher cycle may already have closed on a newer wake, so the guard showed it
the watcher-down banner with the primary's repair line. Under a Codex primary
pin that line is the checkpoint, and a live Codex lab run showed the away
session running it mid-turn (the nested host stood down on its ownership
check). The branch actor never owns watcher continuity, so the banner, its
reminder, and the episode state now leave that actor out, as the queued-wake
warning already does.

The lint telemetry fixture counts bin/fm-afk-launch.sh's source directives,
which the host engine note raised from four to five.
A Cursor park superseded by the captain's return stops its host as the
engine turn ends, so the host's own handoff of that turn's outcomes was
never printed and the outcomes never reached main. The report surface now
queues every outcome it records after the away record is gone as a durable
check wake; the return owner archives the record before it reads the store,
so each outcome is in the return brief, queued, or both. A host stopped
mid-turn also removes its turn's result and error files.

The stream test now acknowledges its first close and accepts a restarted
cycle that closes on its resurface before the arm confirms it.
A Cursor park superseded mid-turn can kill its host outright, which runs no
cleanup, so the turn's result, error, and descendant files stayed behind and
any tool process the engine started was left running. The next host's
activation now reaps the descendants that turn recorded and removes its
files. The host suite also registers its homes in a file, because
make_home runs in a command substitution, so its cleanup now stops every
host a case leaves running.
…acknowledgement

Main's acknowledgement re-claimed every unreserved queued row, including one
that arrived after the drain above the acknowledged cutoff. That row stayed
main's without ever being shown to it, so while away the supervision host
refused every later wake that included it and handed each back to main until
main drained again. The acknowledgement now claims only unreserved rows at or
below its cutoff.
…OINT_AWAY in the rendered Codex host instructions. The focused instruction and checkpoint suites pass
@kunchenguid
kunchenguid merged commit e1b7f4f into main Sep 24, 2026
20 checks passed
@kunchenguid
kunchenguid deleted the fm/fm-afk-host-harnesses-r1 branch September 24, 2026 16:05
mituso89 pushed a commit to mituso89/firstmate that referenced this pull request Sep 26, 2026
…#5503)

* feat(bin): run the supervision host beside the other non-Pi primaries while away

Cursor's stop-hook park, the OpenCode plugin, the omp watch extension, Grok's
model-owned background arm, and Codex's foreground checkpoint now run
bin/fm-supervision-host.sh in the watcher arm's place when the home opted in
with config/supervision-host, so the host's Claude engine takes away-posture
wakes beside those primaries exactly as it does beside Claude. Without the
file nothing changes.

- The host streams its first cycle's status line, accepts --restart and the
  owner's predecessor arm for its first cycle, and prints each exit in one
  write, so owners that wait for arm readiness and restart their own
  successor (OpenCode, omp) keep their handling handoff.
- Codex's checkpoint passes its bound to the host as the park boundary,
  raises it to FM_CODEX_WATCH_CHECKPOINT_AWAY (3600 s) while the away record
  exists, and lets an engine turn that starts before the bound finish after
  it (FM_SUPERVISION_HOST_PARK_LIMIT).
- /afk launches no away daemon on an opted-in home of those harnesses and
  says so at entry when the file selects no engine for that primary.
- Session start renders the host protocol for each arm owner, and Grok's
  arm command becomes the host.

* fix(bin): keep the watcher-down banner away from the supervision branch actor

A supervision host's engine turn runs guarded commands after its successor
watcher cycle may already have closed on a newer wake, so the guard showed it
the watcher-down banner with the primary's repair line. Under a Codex primary
pin that line is the checkpoint, and a live Codex lab run showed the away
session running it mid-turn (the nested host stood down on its ownership
check). The branch actor never owns watcher continuity, so the banner, its
reminder, and the episode state now leave that actor out, as the queued-wake
warning already does.

The lint telemetry fixture counts bin/fm-afk-launch.sh's source directives,
which the host engine note raised from four to five.

* fix(bin): queue away-session outcomes recorded after the return for main

A Cursor park superseded by the captain's return stops its host as the
engine turn ends, so the host's own handoff of that turn's outcomes was
never printed and the outcomes never reached main. The report surface now
queues every outcome it records after the away record is gone as a durable
check wake; the return owner archives the record before it reads the store,
so each outcome is in the return brief, queued, or both. A host stopped
mid-turn also removes its turn's result and error files.

The stream test now acknowledges its first close and accepts a restarted
cycle that closes on its resurface before the arm confirms it.

* fix(bin): clear a hard-killed host's turn at the next activation

A Cursor park superseded mid-turn can kill its host outright, which runs no
cleanup, so the turn's result, error, and descendant files stayed behind and
any tool process the engine started was left running. The next host's
activation now reaps the descendants that turn recorded and removes its
files. The host suite also registers its homes in a file, because
make_home runs in a command substitution, so its cleanup now stops every
host a case leaves running.

* fix(bin): leave rows that arrive after main's drain unclaimed at its acknowledgement

Main's acknowledgement re-claimed every unreserved queued row, including one
that arrived after the drain above the acknowledged cutoff. That row stayed
main's without ever being shown to it, so while away the supervision host
refused every later wake that included it and handed each back to main until
main drained again. The acknowledgement now claims only unreserved rows at or
below its cutoff.

* docs: name the killed turn's engine and files in the host's failure direction

* docs: record live supervision host runs on the non-Pi primaries

* no-mistakes(review): Replay host-only supervision boundaries across omp session replacement

* no-mistakes(review): Deliver omp supervision-host wakes only at the host's close

* no-mistakes(document): Correct supervision host documentation for non-Pi primaries

* no-mistakes(ci): Fixed the CI failure by naming FM_CODEX_WATCH_CHECKPOINT_AWAY in the rendered Codex host instructions. The focused instruction and checkpoint suites pass
mehulbhagwani pushed a commit to mehulbhagwani/firstmate that referenced this pull request Sep 26, 2026
…#5503)

* feat(bin): run the supervision host beside the other non-Pi primaries while away

Cursor's stop-hook park, the OpenCode plugin, the omp watch extension, Grok's
model-owned background arm, and Codex's foreground checkpoint now run
bin/fm-supervision-host.sh in the watcher arm's place when the home opted in
with config/supervision-host, so the host's Claude engine takes away-posture
wakes beside those primaries exactly as it does beside Claude. Without the
file nothing changes.

- The host streams its first cycle's status line, accepts --restart and the
  owner's predecessor arm for its first cycle, and prints each exit in one
  write, so owners that wait for arm readiness and restart their own
  successor (OpenCode, omp) keep their handling handoff.
- Codex's checkpoint passes its bound to the host as the park boundary,
  raises it to FM_CODEX_WATCH_CHECKPOINT_AWAY (3600 s) while the away record
  exists, and lets an engine turn that starts before the bound finish after
  it (FM_SUPERVISION_HOST_PARK_LIMIT).
- /afk launches no away daemon on an opted-in home of those harnesses and
  says so at entry when the file selects no engine for that primary.
- Session start renders the host protocol for each arm owner, and Grok's
  arm command becomes the host.

* fix(bin): keep the watcher-down banner away from the supervision branch actor

A supervision host's engine turn runs guarded commands after its successor
watcher cycle may already have closed on a newer wake, so the guard showed it
the watcher-down banner with the primary's repair line. Under a Codex primary
pin that line is the checkpoint, and a live Codex lab run showed the away
session running it mid-turn (the nested host stood down on its ownership
check). The branch actor never owns watcher continuity, so the banner, its
reminder, and the episode state now leave that actor out, as the queued-wake
warning already does.

The lint telemetry fixture counts bin/fm-afk-launch.sh's source directives,
which the host engine note raised from four to five.

* fix(bin): queue away-session outcomes recorded after the return for main

A Cursor park superseded by the captain's return stops its host as the
engine turn ends, so the host's own handoff of that turn's outcomes was
never printed and the outcomes never reached main. The report surface now
queues every outcome it records after the away record is gone as a durable
check wake; the return owner archives the record before it reads the store,
so each outcome is in the return brief, queued, or both. A host stopped
mid-turn also removes its turn's result and error files.

The stream test now acknowledges its first close and accepts a restarted
cycle that closes on its resurface before the arm confirms it.

* fix(bin): clear a hard-killed host's turn at the next activation

A Cursor park superseded mid-turn can kill its host outright, which runs no
cleanup, so the turn's result, error, and descendant files stayed behind and
any tool process the engine started was left running. The next host's
activation now reaps the descendants that turn recorded and removes its
files. The host suite also registers its homes in a file, because
make_home runs in a command substitution, so its cleanup now stops every
host a case leaves running.

* fix(bin): leave rows that arrive after main's drain unclaimed at its acknowledgement

Main's acknowledgement re-claimed every unreserved queued row, including one
that arrived after the drain above the acknowledged cutoff. That row stayed
main's without ever being shown to it, so while away the supervision host
refused every later wake that included it and handed each back to main until
main drained again. The acknowledgement now claims only unreserved rows at or
below its cutoff.

* docs: name the killed turn's engine and files in the host's failure direction

* docs: record live supervision host runs on the non-Pi primaries

* no-mistakes(review): Replay host-only supervision boundaries across omp session replacement

* no-mistakes(review): Deliver omp supervision-host wakes only at the host's close

* no-mistakes(document): Correct supervision host documentation for non-Pi primaries

* no-mistakes(ci): Fixed the CI failure by naming FM_CODEX_WATCH_CHECKPOINT_AWAY in the rendered Codex host instructions. The focused instruction and checkpoint suites pass
mehulbhagwani pushed a commit to mehulbhagwani/firstmate that referenced this pull request Sep 26, 2026
…#5503)

* feat(bin): run the supervision host beside the other non-Pi primaries while away

Cursor's stop-hook park, the OpenCode plugin, the omp watch extension, Grok's
model-owned background arm, and Codex's foreground checkpoint now run
bin/fm-supervision-host.sh in the watcher arm's place when the home opted in
with config/supervision-host, so the host's Claude engine takes away-posture
wakes beside those primaries exactly as it does beside Claude. Without the
file nothing changes.

- The host streams its first cycle's status line, accepts --restart and the
  owner's predecessor arm for its first cycle, and prints each exit in one
  write, so owners that wait for arm readiness and restart their own
  successor (OpenCode, omp) keep their handling handoff.
- Codex's checkpoint passes its bound to the host as the park boundary,
  raises it to FM_CODEX_WATCH_CHECKPOINT_AWAY (3600 s) while the away record
  exists, and lets an engine turn that starts before the bound finish after
  it (FM_SUPERVISION_HOST_PARK_LIMIT).
- /afk launches no away daemon on an opted-in home of those harnesses and
  says so at entry when the file selects no engine for that primary.
- Session start renders the host protocol for each arm owner, and Grok's
  arm command becomes the host.

* fix(bin): keep the watcher-down banner away from the supervision branch actor

A supervision host's engine turn runs guarded commands after its successor
watcher cycle may already have closed on a newer wake, so the guard showed it
the watcher-down banner with the primary's repair line. Under a Codex primary
pin that line is the checkpoint, and a live Codex lab run showed the away
session running it mid-turn (the nested host stood down on its ownership
check). The branch actor never owns watcher continuity, so the banner, its
reminder, and the episode state now leave that actor out, as the queued-wake
warning already does.

The lint telemetry fixture counts bin/fm-afk-launch.sh's source directives,
which the host engine note raised from four to five.

* fix(bin): queue away-session outcomes recorded after the return for main

A Cursor park superseded by the captain's return stops its host as the
engine turn ends, so the host's own handoff of that turn's outcomes was
never printed and the outcomes never reached main. The report surface now
queues every outcome it records after the away record is gone as a durable
check wake; the return owner archives the record before it reads the store,
so each outcome is in the return brief, queued, or both. A host stopped
mid-turn also removes its turn's result and error files.

The stream test now acknowledges its first close and accepts a restarted
cycle that closes on its resurface before the arm confirms it.

* fix(bin): clear a hard-killed host's turn at the next activation

A Cursor park superseded mid-turn can kill its host outright, which runs no
cleanup, so the turn's result, error, and descendant files stayed behind and
any tool process the engine started was left running. The next host's
activation now reaps the descendants that turn recorded and removes its
files. The host suite also registers its homes in a file, because
make_home runs in a command substitution, so its cleanup now stops every
host a case leaves running.

* fix(bin): leave rows that arrive after main's drain unclaimed at its acknowledgement

Main's acknowledgement re-claimed every unreserved queued row, including one
that arrived after the drain above the acknowledged cutoff. That row stayed
main's without ever being shown to it, so while away the supervision host
refused every later wake that included it and handed each back to main until
main drained again. The acknowledgement now claims only unreserved rows at or
below its cutoff.

* docs: name the killed turn's engine and files in the host's failure direction

* docs: record live supervision host runs on the non-Pi primaries

* no-mistakes(review): Replay host-only supervision boundaries across omp session replacement

* no-mistakes(review): Deliver omp supervision-host wakes only at the host's close

* no-mistakes(document): Correct supervision host documentation for non-Pi primaries

* no-mistakes(ci): Fixed the CI failure by naming FM_CODEX_WATCH_CHECKPOINT_AWAY in the rendered Codex host instructions. The focused instruction and checkpoint suites pass
mehulbhagwani pushed a commit to mehulbhagwani/firstmate that referenced this pull request Sep 27, 2026
…#5503)

* feat(bin): run the supervision host beside the other non-Pi primaries while away

Cursor's stop-hook park, the OpenCode plugin, the omp watch extension, Grok's
model-owned background arm, and Codex's foreground checkpoint now run
bin/fm-supervision-host.sh in the watcher arm's place when the home opted in
with config/supervision-host, so the host's Claude engine takes away-posture
wakes beside those primaries exactly as it does beside Claude. Without the
file nothing changes.

- The host streams its first cycle's status line, accepts --restart and the
  owner's predecessor arm for its first cycle, and prints each exit in one
  write, so owners that wait for arm readiness and restart their own
  successor (OpenCode, omp) keep their handling handoff.
- Codex's checkpoint passes its bound to the host as the park boundary,
  raises it to FM_CODEX_WATCH_CHECKPOINT_AWAY (3600 s) while the away record
  exists, and lets an engine turn that starts before the bound finish after
  it (FM_SUPERVISION_HOST_PARK_LIMIT).
- /afk launches no away daemon on an opted-in home of those harnesses and
  says so at entry when the file selects no engine for that primary.
- Session start renders the host protocol for each arm owner, and Grok's
  arm command becomes the host.

* fix(bin): keep the watcher-down banner away from the supervision branch actor

A supervision host's engine turn runs guarded commands after its successor
watcher cycle may already have closed on a newer wake, so the guard showed it
the watcher-down banner with the primary's repair line. Under a Codex primary
pin that line is the checkpoint, and a live Codex lab run showed the away
session running it mid-turn (the nested host stood down on its ownership
check). The branch actor never owns watcher continuity, so the banner, its
reminder, and the episode state now leave that actor out, as the queued-wake
warning already does.

The lint telemetry fixture counts bin/fm-afk-launch.sh's source directives,
which the host engine note raised from four to five.

* fix(bin): queue away-session outcomes recorded after the return for main

A Cursor park superseded by the captain's return stops its host as the
engine turn ends, so the host's own handoff of that turn's outcomes was
never printed and the outcomes never reached main. The report surface now
queues every outcome it records after the away record is gone as a durable
check wake; the return owner archives the record before it reads the store,
so each outcome is in the return brief, queued, or both. A host stopped
mid-turn also removes its turn's result and error files.

The stream test now acknowledges its first close and accepts a restarted
cycle that closes on its resurface before the arm confirms it.

* fix(bin): clear a hard-killed host's turn at the next activation

A Cursor park superseded mid-turn can kill its host outright, which runs no
cleanup, so the turn's result, error, and descendant files stayed behind and
any tool process the engine started was left running. The next host's
activation now reaps the descendants that turn recorded and removes its
files. The host suite also registers its homes in a file, because
make_home runs in a command substitution, so its cleanup now stops every
host a case leaves running.

* fix(bin): leave rows that arrive after main's drain unclaimed at its acknowledgement

Main's acknowledgement re-claimed every unreserved queued row, including one
that arrived after the drain above the acknowledged cutoff. That row stayed
main's without ever being shown to it, so while away the supervision host
refused every later wake that included it and handed each back to main until
main drained again. The acknowledgement now claims only unreserved rows at or
below its cutoff.

* docs: name the killed turn's engine and files in the host's failure direction

* docs: record live supervision host runs on the non-Pi primaries

* no-mistakes(review): Replay host-only supervision boundaries across omp session replacement

* no-mistakes(review): Deliver omp supervision-host wakes only at the host's close

* no-mistakes(document): Correct supervision host documentation for non-Pi primaries

* no-mistakes(ci): Fixed the CI failure by naming FM_CODEX_WATCH_CHECKPOINT_AWAY in the rendered Codex host instructions. The focused instruction and checkpoint suites pass
mehulbhagwani pushed a commit to mehulbhagwani/firstmate that referenced this pull request Sep 27, 2026
…#5503)

* feat(bin): run the supervision host beside the other non-Pi primaries while away

Cursor's stop-hook park, the OpenCode plugin, the omp watch extension, Grok's
model-owned background arm, and Codex's foreground checkpoint now run
bin/fm-supervision-host.sh in the watcher arm's place when the home opted in
with config/supervision-host, so the host's Claude engine takes away-posture
wakes beside those primaries exactly as it does beside Claude. Without the
file nothing changes.

- The host streams its first cycle's status line, accepts --restart and the
  owner's predecessor arm for its first cycle, and prints each exit in one
  write, so owners that wait for arm readiness and restart their own
  successor (OpenCode, omp) keep their handling handoff.
- Codex's checkpoint passes its bound to the host as the park boundary,
  raises it to FM_CODEX_WATCH_CHECKPOINT_AWAY (3600 s) while the away record
  exists, and lets an engine turn that starts before the bound finish after
  it (FM_SUPERVISION_HOST_PARK_LIMIT).
- /afk launches no away daemon on an opted-in home of those harnesses and
  says so at entry when the file selects no engine for that primary.
- Session start renders the host protocol for each arm owner, and Grok's
  arm command becomes the host.

* fix(bin): keep the watcher-down banner away from the supervision branch actor

A supervision host's engine turn runs guarded commands after its successor
watcher cycle may already have closed on a newer wake, so the guard showed it
the watcher-down banner with the primary's repair line. Under a Codex primary
pin that line is the checkpoint, and a live Codex lab run showed the away
session running it mid-turn (the nested host stood down on its ownership
check). The branch actor never owns watcher continuity, so the banner, its
reminder, and the episode state now leave that actor out, as the queued-wake
warning already does.

The lint telemetry fixture counts bin/fm-afk-launch.sh's source directives,
which the host engine note raised from four to five.

* fix(bin): queue away-session outcomes recorded after the return for main

A Cursor park superseded by the captain's return stops its host as the
engine turn ends, so the host's own handoff of that turn's outcomes was
never printed and the outcomes never reached main. The report surface now
queues every outcome it records after the away record is gone as a durable
check wake; the return owner archives the record before it reads the store,
so each outcome is in the return brief, queued, or both. A host stopped
mid-turn also removes its turn's result and error files.

The stream test now acknowledges its first close and accepts a restarted
cycle that closes on its resurface before the arm confirms it.

* fix(bin): clear a hard-killed host's turn at the next activation

A Cursor park superseded mid-turn can kill its host outright, which runs no
cleanup, so the turn's result, error, and descendant files stayed behind and
any tool process the engine started was left running. The next host's
activation now reaps the descendants that turn recorded and removes its
files. The host suite also registers its homes in a file, because
make_home runs in a command substitution, so its cleanup now stops every
host a case leaves running.

* fix(bin): leave rows that arrive after main's drain unclaimed at its acknowledgement

Main's acknowledgement re-claimed every unreserved queued row, including one
that arrived after the drain above the acknowledged cutoff. That row stayed
main's without ever being shown to it, so while away the supervision host
refused every later wake that included it and handed each back to main until
main drained again. The acknowledgement now claims only unreserved rows at or
below its cutoff.

* docs: name the killed turn's engine and files in the host's failure direction

* docs: record live supervision host runs on the non-Pi primaries

* no-mistakes(review): Replay host-only supervision boundaries across omp session replacement

* no-mistakes(review): Deliver omp supervision-host wakes only at the host's close

* no-mistakes(document): Correct supervision host documentation for non-Pi primaries

* no-mistakes(ci): Fixed the CI failure by naming FM_CODEX_WATCH_CHECKPOINT_AWAY in the rendered Codex host instructions. The focused instruction and checkpoint suites pass
mehulbhagwani pushed a commit to mehulbhagwani/firstmate that referenced this pull request Sep 27, 2026
…#5503)

* feat(bin): run the supervision host beside the other non-Pi primaries while away

Cursor's stop-hook park, the OpenCode plugin, the omp watch extension, Grok's
model-owned background arm, and Codex's foreground checkpoint now run
bin/fm-supervision-host.sh in the watcher arm's place when the home opted in
with config/supervision-host, so the host's Claude engine takes away-posture
wakes beside those primaries exactly as it does beside Claude. Without the
file nothing changes.

- The host streams its first cycle's status line, accepts --restart and the
  owner's predecessor arm for its first cycle, and prints each exit in one
  write, so owners that wait for arm readiness and restart their own
  successor (OpenCode, omp) keep their handling handoff.
- Codex's checkpoint passes its bound to the host as the park boundary,
  raises it to FM_CODEX_WATCH_CHECKPOINT_AWAY (3600 s) while the away record
  exists, and lets an engine turn that starts before the bound finish after
  it (FM_SUPERVISION_HOST_PARK_LIMIT).
- /afk launches no away daemon on an opted-in home of those harnesses and
  says so at entry when the file selects no engine for that primary.
- Session start renders the host protocol for each arm owner, and Grok's
  arm command becomes the host.

* fix(bin): keep the watcher-down banner away from the supervision branch actor

A supervision host's engine turn runs guarded commands after its successor
watcher cycle may already have closed on a newer wake, so the guard showed it
the watcher-down banner with the primary's repair line. Under a Codex primary
pin that line is the checkpoint, and a live Codex lab run showed the away
session running it mid-turn (the nested host stood down on its ownership
check). The branch actor never owns watcher continuity, so the banner, its
reminder, and the episode state now leave that actor out, as the queued-wake
warning already does.

The lint telemetry fixture counts bin/fm-afk-launch.sh's source directives,
which the host engine note raised from four to five.

* fix(bin): queue away-session outcomes recorded after the return for main

A Cursor park superseded by the captain's return stops its host as the
engine turn ends, so the host's own handoff of that turn's outcomes was
never printed and the outcomes never reached main. The report surface now
queues every outcome it records after the away record is gone as a durable
check wake; the return owner archives the record before it reads the store,
so each outcome is in the return brief, queued, or both. A host stopped
mid-turn also removes its turn's result and error files.

The stream test now acknowledges its first close and accepts a restarted
cycle that closes on its resurface before the arm confirms it.

* fix(bin): clear a hard-killed host's turn at the next activation

A Cursor park superseded mid-turn can kill its host outright, which runs no
cleanup, so the turn's result, error, and descendant files stayed behind and
any tool process the engine started was left running. The next host's
activation now reaps the descendants that turn recorded and removes its
files. The host suite also registers its homes in a file, because
make_home runs in a command substitution, so its cleanup now stops every
host a case leaves running.

* fix(bin): leave rows that arrive after main's drain unclaimed at its acknowledgement

Main's acknowledgement re-claimed every unreserved queued row, including one
that arrived after the drain above the acknowledged cutoff. That row stayed
main's without ever being shown to it, so while away the supervision host
refused every later wake that included it and handed each back to main until
main drained again. The acknowledgement now claims only unreserved rows at or
below its cutoff.

* docs: name the killed turn's engine and files in the host's failure direction

* docs: record live supervision host runs on the non-Pi primaries

* no-mistakes(review): Replay host-only supervision boundaries across omp session replacement

* no-mistakes(review): Deliver omp supervision-host wakes only at the host's close

* no-mistakes(document): Correct supervision host documentation for non-Pi primaries

* no-mistakes(ci): Fixed the CI failure by naming FM_CODEX_WATCH_CHECKPOINT_AWAY in the rendered Codex host instructions. The focused instruction and checkpoint suites pass
RooseveltAdvisors pushed a commit to RooseveltAdvisors/firstmate that referenced this pull request Sep 29, 2026
…#5503)

* feat(bin): run the supervision host beside the other non-Pi primaries while away

Cursor's stop-hook park, the OpenCode plugin, the omp watch extension, Grok's
model-owned background arm, and Codex's foreground checkpoint now run
bin/fm-supervision-host.sh in the watcher arm's place when the home opted in
with config/supervision-host, so the host's Claude engine takes away-posture
wakes beside those primaries exactly as it does beside Claude. Without the
file nothing changes.

- The host streams its first cycle's status line, accepts --restart and the
  owner's predecessor arm for its first cycle, and prints each exit in one
  write, so owners that wait for arm readiness and restart their own
  successor (OpenCode, omp) keep their handling handoff.
- Codex's checkpoint passes its bound to the host as the park boundary,
  raises it to FM_CODEX_WATCH_CHECKPOINT_AWAY (3600 s) while the away record
  exists, and lets an engine turn that starts before the bound finish after
  it (FM_SUPERVISION_HOST_PARK_LIMIT).
- /afk launches no away daemon on an opted-in home of those harnesses and
  says so at entry when the file selects no engine for that primary.
- Session start renders the host protocol for each arm owner, and Grok's
  arm command becomes the host.

* fix(bin): keep the watcher-down banner away from the supervision branch actor

A supervision host's engine turn runs guarded commands after its successor
watcher cycle may already have closed on a newer wake, so the guard showed it
the watcher-down banner with the primary's repair line. Under a Codex primary
pin that line is the checkpoint, and a live Codex lab run showed the away
session running it mid-turn (the nested host stood down on its ownership
check). The branch actor never owns watcher continuity, so the banner, its
reminder, and the episode state now leave that actor out, as the queued-wake
warning already does.

The lint telemetry fixture counts bin/fm-afk-launch.sh's source directives,
which the host engine note raised from four to five.

* fix(bin): queue away-session outcomes recorded after the return for main

A Cursor park superseded by the captain's return stops its host as the
engine turn ends, so the host's own handoff of that turn's outcomes was
never printed and the outcomes never reached main. The report surface now
queues every outcome it records after the away record is gone as a durable
check wake; the return owner archives the record before it reads the store,
so each outcome is in the return brief, queued, or both. A host stopped
mid-turn also removes its turn's result and error files.

The stream test now acknowledges its first close and accepts a restarted
cycle that closes on its resurface before the arm confirms it.

* fix(bin): clear a hard-killed host's turn at the next activation

A Cursor park superseded mid-turn can kill its host outright, which runs no
cleanup, so the turn's result, error, and descendant files stayed behind and
any tool process the engine started was left running. The next host's
activation now reaps the descendants that turn recorded and removes its
files. The host suite also registers its homes in a file, because
make_home runs in a command substitution, so its cleanup now stops every
host a case leaves running.

* fix(bin): leave rows that arrive after main's drain unclaimed at its acknowledgement

Main's acknowledgement re-claimed every unreserved queued row, including one
that arrived after the drain above the acknowledged cutoff. That row stayed
main's without ever being shown to it, so while away the supervision host
refused every later wake that included it and handed each back to main until
main drained again. The acknowledgement now claims only unreserved rows at or
below its cutoff.

* docs: name the killed turn's engine and files in the host's failure direction

* docs: record live supervision host runs on the non-Pi primaries

* no-mistakes(review): Replay host-only supervision boundaries across omp session replacement

* no-mistakes(review): Deliver omp supervision-host wakes only at the host's close

* no-mistakes(document): Correct supervision host documentation for non-Pi primaries

* no-mistakes(ci): Fixed the CI failure by naming FM_CODEX_WATCH_CHECKPOINT_AWAY in the rendered Codex host instructions. The focused instruction and checkpoint suites pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant