Skip to content

fix: enforce supervision guards across harnesses - #5471

Merged
kunchenguid merged 3 commits into
mainfrom
fm/fm-afk-host-guards-r1
Sep 23, 2026
Merged

kunchenguid merged 3 commits into
mainfrom
fm/fm-afk-host-guards-r1

Conversation

@kunchenguid

Copy link
Copy Markdown
Owner

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 starts step 2 of the AFK revamp: rung 3 of the ladder in data/fm-afk-slices23-plan-s1/report.md, the shared non-Pi supervision host, which lands as two PRs - this guards-first PR, then the host core. The design was decided on the 2026-09-20 review board (report section 11), including "Slice 3: go, starting with the spike" and taking slice 3 "All the way (3a to 3e): away, attended, /quiet on the host, daemon deleted". The spike (data/fm-supervision-host-spike-s1/report.md) returned GO for the Claude engine. The captain ruled Pi keeps its in-process supervision and no Pi engine is built now (plan section 15). The live validation of the words model it builds on passed, with the abort step cleared by a real-worker replay (data/fm-afk-words-live-validation-s1/report.md, data/fm-afk-abort-run-replay-s1/report.md).

Substance of the referenced plan and reports. Rung 3 is a supervision host for non-Pi primary harnesses: a headless engine session (starting with the Claude engine) running beside the 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 so every guarded script applies the same main/branch partition - replacing the away daemon over rungs 3a to 3e; the review board also chose "The primary harness's own headless mode where verified, configurable per home, starting with Claude and Pi engines" as the engine. The spike resized 3a so it must land as two PRs: this small guards-first PR, then the host core (host loop, engine lib, report and dispatch CLIs, Claude arm swap behind a default-off config/supervision-host flag). The guards-first PR, as the spike report defines it, is: (1) generalize fm_lease_live / fm_lease_guard activation to the pure record test, because off Pi the partition is one-sided today - a branch lease reads live and refuses main only when main's process also carries an actor variable, while an unmarked main reads it as stale and overwrites it; (2) honor a primary-harness pin in bin/fm-harness.sh while FM_SUPERVISION_ACTOR=branch, because an engine detects its own harness (a Pi engine detects as pi) and would otherwise dispatch crewmates on its own harness instead of the primary's; (3) a shared bounded-exec helper (a wall-clock watchdog with TERM, a grace, then KILL, because both engines keep running after TERM mid-tool and macOS has no timeout binary), extracted from fm_tasks_axi; (4) one sentence in bin/fm-claude-stop-autoarm.sh's header that a timeout-terminated hook's exit 2 is not delivered as a rewake (measured: Claude sends SIGTERM to the hook tree at exactly the configured timeout, and an exit 2 from a hook it terminated does not wake main, while an exit 2 before the timeout does). No behavior change for any home without lease files; the host itself is the next PR.

What Changed

  • Honor live per-task leases for unmarked main processes on non-Pi harnesses, while leaving homes without lease files untouched.
  • Let supervision branches pin the primary harness for crew resolution and reject unknown pins.
  • Extract a shared bounded-exec helper for tasks-axi, add guard and timeout regression tests, and document Claude’s timeout-terminated Stop hook behavior.

Risk Assessment

🚨 High: The guards-first partition has a reachable first-claim race during the intended non-Pi supervision flow, and its remedy must be reconciled with the no-behavior-change constraint before merge.

Testing

Live CLI checks confirmed cross-process lease refusal and staleness, branch harness pinning, bounded termination, and the no-lease fast path. A named Herdr lab completed its multi-home spawn, restart, send, and cleanup smoke checks. Targeted tests passed; the broader backlog test command reached its 240-second limit. The Claude timeout statement is documentation, not a runtime change.

  • Live validation: ✅ go - 5 of 5 scenarios driven live against the product
Scenario Result Live Evidence
An unmarked main tries to claim a task leased by a branch and is refused; a previous session's lease reads stale ✅ pass live Live guards-first CLI transcript; bash tests/fm-branch-supervision.test.sh
A branch running with Pi identity routes own, crew, and secondmate to its pinned Claude primary, while main ignores the pin and an invalid branch pin refuses ✅ pass live Live guards-first CLI transcript; tests/fm-harness-precedence.test.sh
A TERM-ignoring command is killed after the wall-clock bound and grace ✅ pass live Live guards-first CLI transcript; tests/fm-timeout-lib.test.sh
An unmarked main with no lease file proceeds without creating a lease-command lock ✅ pass live Live guards-first CLI transcript; bash tests/fm-branch-supervision.test.sh
An isolated Herdr operator spawns tasks in separate homes, sends commands, restarts the session, and cleans up ✅ pass live bash tests/fm-backend-herdr-smoke.test.sh
Evidence: Live guards-first CLI transcript

Source: Live guards-first CLI transcript

Branch lease in isolated home:
branch 65695 1790202166 live
unmarked main claim rc=6: error: claim refused - task 'task-live' is leased to the branch supervision actor (state/.lease-task-live)
Previous session lock becomes stale:
branch 65695 1790202166 stale
main 65695 1790202166 live
Harness under Pi marker and Claude primary pin:
branch own=claude crew=claude secondmate=claude
main own=pi
invalid pin rc=2: error: FM_SUPERVISION_PRIMARY_HARNESS='invalid' names no known harness; refusing to resolve the supervision branch's harness
Bound TERM-ignoring shell:
rc=124 elapsed=2s
Unmarked main without a lease:
no lease command lock created
Evidence: Isolated Herdr multi-home smoke
A non-default fm-lab session created primary and secondmate workspaces, spawned task panes in each, preserved both across a server restart, sent and captured commands, and removed a pane. The real-Claude busy-state check was not enabled.

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 warning
  • ⚠️ bin/fm-lease-lib.sh:197 - An unmarked non-Pi main skips the command lock when a task has no lease file. It can begin an fm-send steer; the branch can then claim that task through bin/fm-lease.sh:115 and mutate it while the steer is still running. The same window affects the guarded lifecycle operation at bin/fm-control.sh:304 and teardown at bin/fm-teardown.sh:354. The comment acknowledges this window, but it leaves the intended cross-process task partition unenforced at the first claim. Closing it requires deciding how to serialize an unmarked main before any lease exists without violating the stated requirement of no behavior change for homes without lease files; that remedy needs authorization.
✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 5 of 5 scenarios driven live against the product
Scenario Result Live Evidence
An unmarked main tries to claim a task leased by a branch and is refused; a previous session's lease reads stale ✅ pass live Live guards-first CLI transcript; bash tests/fm-branch-supervision.test.sh
A branch running with Pi identity routes own, crew, and secondmate to its pinned Claude primary, while main ignores the pin and an invalid branch pin refuses ✅ pass live Live guards-first CLI transcript; tests/fm-harness-precedence.test.sh
A TERM-ignoring command is killed after the wall-clock bound and grace ✅ pass live Live guards-first CLI transcript; tests/fm-timeout-lib.test.sh
An unmarked main with no lease file proceeds without creating a lease-command lock ✅ pass live Live guards-first CLI transcript; bash tests/fm-branch-supervision.test.sh
An isolated Herdr operator spawns tasks in separate homes, sends commands, restarts the session, and cleans up ✅ pass live bash tests/fm-backend-herdr-smoke.test.sh
  • tests/fm-timeout-lib.test.sh
  • tests/fm-harness-precedence.test.sh
  • bash tests/fm-branch-supervision.test.sh
  • bash tests/fm-backlog-atomicity.test.sh (stopped at the 240-second command limit after the relevant bounded-execution checks passed)
  • Manual fm-lease.sh, fm-harness.sh, and fm_exec_timed checks in a disposable home
  • bash tests/fm-backend-herdr-smoke.test.sh in a guarded, non-default Herdr lab
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

…on-Pi supervision host

Lease liveness is now the pure record test in every calling context, so an
unmarked main honors a live branch lease held by a separate process, and a
lease file engages the guard's claim serialization for any caller; a home
with no lease files still takes no lock.

bin/fm-harness.sh honors FM_SUPERVISION_PRIMARY_HARNESS while
FM_SUPERVISION_ACTOR=branch, so a supervision branch running under another
harness resolves own, crew, and secondmate to the primary's harness.

fm_tasks_axi's watchdog moves into bin/fm-timeout-lib.sh as fm_exec_timed with
a separate grace: the perl watchdog is preferred, runs the command in its own
process group against wall-clock deadlines, forwards TERM/INT/HUP, and reaps
the group, so a descendant holding captured output can no longer keep the
caller waiting past the bound on a host without timeout.

The Claude Stop auto-arm header records that Claude drops the exit 2 of a hook
it terminated at the configured timeout, re-measured on Claude Code 2.1.281.
…wn process group

Live runs of real Claude and Pi engine turns under the bound showed both CLIs
start every tool command in a process group of its own, so those processes end
through the engine's own TERM handling rather than the group signal or reap.
Also clears the new timeout test's ShellCheck findings.
@kunchenguid

Copy link
Copy Markdown
Owner Author

fm-afk-host-guards-r1: live validation, 2026-09-23

Scope under test: step 2 rung 3a-guards of the AFK revamp.
The pure-record lease partition off Pi, the supervision-branch primary-harness pin, the shared bounded-exec helper, and the Claude Stop-hook timeout header fact.

  • Before: fdd36879 (main tip the branch forked from).
  • After: e2b0fc60 (the guards), plus 7651fbac (fm_exec_timed header states its process-group limit found live below; new test's ShellCheck findings cleared; no behavior change).
  • Tools: Claude Code 2.1.281, Pi 0.87.0 (real Pi SDK 0.80.10 inside the branch guard), Herdr 0.9.0, tmux 3.6a, macOS (Darwin 25.6.0).
  • Workers: pi harness, openai-codex/gpt-5.6-sol, effort low. Claude lab primaries: sonnet. Branch-engine stand-ins: claude -p --safe-mode (haiku) and pi -p (isolation flags).

Isolation

  • Every lab home is a disposable plain clone of this worktree at the ref under test, with no remote, and its own state/, data/, config/, projects/.
  • The disposable project fmhg-calc is cloned per lab from a lab-private bare origin and registered local-only +yolo.
  • Claude primaries ran on private tmux sockets (fmhg-claude-before, fmhg-claude-after); Pi primaries ran inside two guarded Herdr lab sessions provisioned, driven, and torn down only through fm-herdr-lab.sh (every adapter call routed through the helper by a PATH shim).
  • Every lab process started from a scrubbed environment (env -i plus a fixed PATH), launched from a detached tmux or Herdr server so no Claude Code process is in its ancestry.
  • Teardown: both Herdr labs teardown ok with the default-session tripwire record unchanged (default session still running); all fmhg-* tmux servers gone; the four lab worktree pools verified clean and landed in their lab project's main, then destroyed; test temp directories from these runs removed.
  • Nothing touched the captain's main home, any secondmate home, the live Herdr default session, the shared no-mistakes daemon, or any real repository.

1. Complex sessions, before vs after

A. Claude primary with its Stop-hook watcher cycle (no lease files anywhere)

Captain request: dispatch a worker to add divide(a, b) to fmhg-calc, supervise through landing and cleanup.

before after
request to worker done 14:22:44 to 14:26:06 14:33:08 to 14:36:03
Stop-hook arm rewake after done +33s (outcome=rewake) +12s (outcome=rewake)
landed on lab main 633c6f6 Add division operation 638aaed Add divide operation
cleanup (task records removed, backlog Done) 14:27:44 14:37:13

Same shape in both: dispatch, tokenless auto-arm, one rewake on the worker's ready signal, independent test run, local landing, cleanup.
Both primaries also hit and cleared the same pre-existing teardown refusal on untracked __pycache__ directories.

B. Claude primary against a live branch lease (the change)

The primary dispatched power(a, b) held for review; a real headless Claude engine acting as the supervision branch (FM_SUPERVISION_ACTOR=branch) then ran bin/fm-lease.sh claim fmhg-calc-power in the same home; the captain then asked the primary to steer the worker.

Before (fdd36879):

  • Lease recorded branch 95809 ... with state/.lock = 95809 (the live session), yet an unmarked fm-lease.sh check read it stale.
  • The primary's steer was delivered straight through the branch's live lease (inbox handled/001.msg), and main's own guard then deleted the branch's live lease file as stale (14:30:32).
  • The partition does not exist off Pi.

After (e2b0fc60):

  • Lease branch 8214 ... with state/.lock = 8214; unmarked fm-lease.sh check reads live.
  • The primary's steer was refused and nothing was queued (no inbox directory created); the lease stayed intact. The same refusal, reproduced directly for the record:
    error: steer (fm-send) refused - task 'fmhg-calc-power' is leased to the branch supervision actor (state/.lease-fmhg-calc-power); retry after that actor releases it with exit 6.
  • The primary did not touch the lease; it escalated to the captain with two options and a recommendation.
  • After the branch engine ran fm-lease.sh release, the primary's retry was delivered, the worker added the negative-exponent check, and the primary landed (251fd4a Add integer power operation) and cleaned up on the captain's word.

C. Pi primary on a guarded Herdr lab, attended

Same divide request.
Both trees: dispatch into the Herdr lab, the supervision branch takes the worker's ready wake under its lease, reports a captain outcome, main takes the lease and lands, cleanup completes.
Before additionally had the branch send the worker back once to drop stray instruction files from its commit, then land e0d891b; after landed 739725f directly.

D. Pi away posture with only queued work

Both trees: queue a modulo task, then /afk with words asking to dispatch it and take it through landing.
Both recorded the away posture with the verbatim words and held local landing for the return.
Both absorbed the next heartbeat as no captain-relevant change (before 14:59:24, after 14:46:25) and dispatched nothing; both returns ran fm-afk-return.sh with catch-up clear and archived the record.
Identical, and pre-existing (see finding 5).

E. Pi away posture with a live worker

Both trees: dispatch the queued modulo task attended, then /afk with words to supervise it and land it when ready.
Both branches took the worker's ready wake under away posture, recorded per your away instructions: ... captain outcomes, and held the local-only landing for the return as the posture requires.
On return both mains claimed the lease, landed (before 930f854, after 072863f), and cleaned up.

2. After-only live demonstrations

  • Harness pin: a real pi -p engine run as the branch (FM_SUPERVISION_ACTOR=branch, FM_SUPERVISION_PRIMARY_HARNESS=claude, no crew config) ran fm-harness.sh, crew, secondmate.
    Before: pi, pi, pi (a branch hosted on Pi for a Claude primary would dispatch Pi crew).
    After: claude, claude, claude.
  • Bounded real engine turns: fm_exec_timed 20 5 around a real claude -p (haiku) and a real pi -p turn whose tool ran (sleep 431; ...) & sleep 432.
    Both returned 124 at 20s and 21s with no surviving process.
    Both CLIs ran the tool's processes in a process group of their own (groups 12021 and 35756, each led by the tool's shell rather than the engine the helper bounded), so the cleanup came from each engine's own TERM handling, not the group signal; 7651fbac states that limit in the helper header.
  • Claude Stop-hook timeout: re-measured on 2.1.281 (recorded in docs/verification/supervision.md): a hook that exits 2 before its configured timeout rewakes; a hook Claude terminates at the timeout exits 2 on TERM and no rewake arrives.

3. Standard live guards, before vs after

Run from each tree in a detached tmux server with a scrubbed environment.

guard before after
FM_CLAUDE_LIVE_E2E=1 tests/fm-claude-stop-autoarm-live-e2e.test.sh ok ok
FM_PI_BRANCH_LIVE_E2E=1 tests/fm-pi-branch-live-e2e.test.sh ok, then 5 of 5 ok 1 timeout (pinned watcher-owned main delivery), then 5 of 5 ok
tests/fm-pi-branch-responsiveness-live-e2e.test.sh ok (floor 19.1 ms, idle 18.3, delivering 33.5) ok (floor 10.1 ms, idle 17.5, delivering 30.8)
FM_AFK_PI_HERDR_E2E=1 tests/fm-afk-pi-herdr-return-e2e.test.sh ok (4 of 4) ok (4 of 4)
FM_PI_LIVE_E2E=1 tests/fm-pi-primary-live-e2e.test.sh fails in its model-judgment Ahoy cases fails in its model-judgment Ahoy cases
same with FM_PI_LIVE_WATCH_ONLY=1 (continuity cases only) ok ok

The Pi branch timeout is a flake on a path this change does not alter (1 of 12 runs across both trees).
The Pi primary guard fails in both trees with a different model-compliance miss each run (PRIOR_BOUNDARY_ACK paraphrased, an ASCII near miss treated as operational, a legacy-start case refused), so it is pre-existing and model-dependent.
A first pass run under this worker's own Claude Code ancestry failed fm-afk-pi-herdr-return in both trees because the ancestry outranks the test's PI_CODING_AGENT marker; the detached rerun above is the valid result.

Deterministic suites on the after tree: fm-timeout-lib, fm-branch-supervision, fm-harness-precedence, fm-claude-stop-autoarm, fm-backlog-atomicity (99 ok), fm-backlog-read-bound, fm-tasks-axi pass; bin/fm-lint.sh, bin/fm-doc-audience-check.sh, and bin/fm-test-run.sh --check-coverage are clean.

4. Findings and follow-ups

  1. Quiet attended Claude parks: no-change heartbeats are absorbed while attended, so nothing shortens a park, and a park that outlives the Stop hook's configured timeout ends without a rewake (the drop is now documented). The self-bounded park in 3a-core addresses it.
  2. A non-Pi primary refused with exit 6 has no instructions about a branch or host lease; this one behaved safely but called the lease a stray record and offered to clear it. 3a-core should tell main what a host-held lease means and that it waits for release.
  3. Lease liveness is the session-lock pid, so an engine turn inside the same session keeps its lease live until it releases it; the host must release at turn end or generation activation (planned for 3a-core).
  4. Claude and Pi put tool processes in their own process groups, so fm_exec_timed depends on the engine's TERM handling for them; if the grace KILL ever fires, those processes could outlive the turn. The 3a-core engine lib should reap the engine's tool processes explicitly or prove the engines' TERM cleanup.
  5. Pi away posture never dispatches queued work the words ask for when nothing is in flight: the heartbeat is absorbed as no change. Pre-existing in both trees.
  6. Pre-existing test issues seen along the way: the Pi primary live guard's Ahoy model assertions fail on main; the Pi branch live guard has a rare timing flake; tests/fm-backlog-atomicity.test.sh leaves /tmp/fm-atomic-* task temp directories on every run; tests/fm-afk-pi-herdr-return-e2e.test.sh fails when run under a Claude Code ancestor.

Raw evidence (panes, state listings, backlogs, project logs, branch outcome stores, away records, guard outputs, demo outputs) is kept with the private task record in the operating home (data/fm-afk-host-guards-r1/live-evidence/).

@kunchenguid
kunchenguid merged commit ac2ed3b into main Sep 23, 2026
19 checks passed
@kunchenguid
kunchenguid deleted the fm/fm-afk-host-guards-r1 branch September 23, 2026 22:58
mituso89 pushed a commit to mituso89/firstmate that referenced this pull request Sep 26, 2026
* feat(bin): guard the partition, harness pin, and bounded exec for a non-Pi supervision host

Lease liveness is now the pure record test in every calling context, so an
unmarked main honors a live branch lease held by a separate process, and a
lease file engages the guard's claim serialization for any caller; a home
with no lease files still takes no lock.

bin/fm-harness.sh honors FM_SUPERVISION_PRIMARY_HARNESS while
FM_SUPERVISION_ACTOR=branch, so a supervision branch running under another
harness resolves own, crew, and secondmate to the primary's harness.

fm_tasks_axi's watchdog moves into bin/fm-timeout-lib.sh as fm_exec_timed with
a separate grace: the perl watchdog is preferred, runs the command in its own
process group against wall-clock deadlines, forwards TERM/INT/HUP, and reaps
the group, so a descendant holding captured output can no longer keep the
caller waiting past the bound on a host without timeout.

The Claude Stop auto-arm header records that Claude drops the exit 2 of a hook
it terminated at the configured timeout, re-measured on Claude Code 2.1.281.

* fix(bin): state that fm_exec_timed cannot reach a descendant in its own process group

Live runs of real Claude and Pi engine turns under the bound showed both CLIs
start every tool command in a process group of its own, so those processes end
through the engine's own TERM handling rather than the group signal or reap.
Also clears the new timeout test's ShellCheck findings.

* no-mistakes(document): Clarify cross-harness lease documentation
mehulbhagwani pushed a commit to mehulbhagwani/firstmate that referenced this pull request Sep 26, 2026
* feat(bin): guard the partition, harness pin, and bounded exec for a non-Pi supervision host

Lease liveness is now the pure record test in every calling context, so an
unmarked main honors a live branch lease held by a separate process, and a
lease file engages the guard's claim serialization for any caller; a home
with no lease files still takes no lock.

bin/fm-harness.sh honors FM_SUPERVISION_PRIMARY_HARNESS while
FM_SUPERVISION_ACTOR=branch, so a supervision branch running under another
harness resolves own, crew, and secondmate to the primary's harness.

fm_tasks_axi's watchdog moves into bin/fm-timeout-lib.sh as fm_exec_timed with
a separate grace: the perl watchdog is preferred, runs the command in its own
process group against wall-clock deadlines, forwards TERM/INT/HUP, and reaps
the group, so a descendant holding captured output can no longer keep the
caller waiting past the bound on a host without timeout.

The Claude Stop auto-arm header records that Claude drops the exit 2 of a hook
it terminated at the configured timeout, re-measured on Claude Code 2.1.281.

* fix(bin): state that fm_exec_timed cannot reach a descendant in its own process group

Live runs of real Claude and Pi engine turns under the bound showed both CLIs
start every tool command in a process group of its own, so those processes end
through the engine's own TERM handling rather than the group signal or reap.
Also clears the new timeout test's ShellCheck findings.

* no-mistakes(document): Clarify cross-harness lease documentation
mehulbhagwani pushed a commit to mehulbhagwani/firstmate that referenced this pull request Sep 26, 2026
* feat(bin): guard the partition, harness pin, and bounded exec for a non-Pi supervision host

Lease liveness is now the pure record test in every calling context, so an
unmarked main honors a live branch lease held by a separate process, and a
lease file engages the guard's claim serialization for any caller; a home
with no lease files still takes no lock.

bin/fm-harness.sh honors FM_SUPERVISION_PRIMARY_HARNESS while
FM_SUPERVISION_ACTOR=branch, so a supervision branch running under another
harness resolves own, crew, and secondmate to the primary's harness.

fm_tasks_axi's watchdog moves into bin/fm-timeout-lib.sh as fm_exec_timed with
a separate grace: the perl watchdog is preferred, runs the command in its own
process group against wall-clock deadlines, forwards TERM/INT/HUP, and reaps
the group, so a descendant holding captured output can no longer keep the
caller waiting past the bound on a host without timeout.

The Claude Stop auto-arm header records that Claude drops the exit 2 of a hook
it terminated at the configured timeout, re-measured on Claude Code 2.1.281.

* fix(bin): state that fm_exec_timed cannot reach a descendant in its own process group

Live runs of real Claude and Pi engine turns under the bound showed both CLIs
start every tool command in a process group of its own, so those processes end
through the engine's own TERM handling rather than the group signal or reap.
Also clears the new timeout test's ShellCheck findings.

* no-mistakes(document): Clarify cross-harness lease documentation
mehulbhagwani pushed a commit to mehulbhagwani/firstmate that referenced this pull request Sep 27, 2026
* feat(bin): guard the partition, harness pin, and bounded exec for a non-Pi supervision host

Lease liveness is now the pure record test in every calling context, so an
unmarked main honors a live branch lease held by a separate process, and a
lease file engages the guard's claim serialization for any caller; a home
with no lease files still takes no lock.

bin/fm-harness.sh honors FM_SUPERVISION_PRIMARY_HARNESS while
FM_SUPERVISION_ACTOR=branch, so a supervision branch running under another
harness resolves own, crew, and secondmate to the primary's harness.

fm_tasks_axi's watchdog moves into bin/fm-timeout-lib.sh as fm_exec_timed with
a separate grace: the perl watchdog is preferred, runs the command in its own
process group against wall-clock deadlines, forwards TERM/INT/HUP, and reaps
the group, so a descendant holding captured output can no longer keep the
caller waiting past the bound on a host without timeout.

The Claude Stop auto-arm header records that Claude drops the exit 2 of a hook
it terminated at the configured timeout, re-measured on Claude Code 2.1.281.

* fix(bin): state that fm_exec_timed cannot reach a descendant in its own process group

Live runs of real Claude and Pi engine turns under the bound showed both CLIs
start every tool command in a process group of its own, so those processes end
through the engine's own TERM handling rather than the group signal or reap.
Also clears the new timeout test's ShellCheck findings.

* no-mistakes(document): Clarify cross-harness lease documentation
mehulbhagwani pushed a commit to mehulbhagwani/firstmate that referenced this pull request Sep 27, 2026
* feat(bin): guard the partition, harness pin, and bounded exec for a non-Pi supervision host

Lease liveness is now the pure record test in every calling context, so an
unmarked main honors a live branch lease held by a separate process, and a
lease file engages the guard's claim serialization for any caller; a home
with no lease files still takes no lock.

bin/fm-harness.sh honors FM_SUPERVISION_PRIMARY_HARNESS while
FM_SUPERVISION_ACTOR=branch, so a supervision branch running under another
harness resolves own, crew, and secondmate to the primary's harness.

fm_tasks_axi's watchdog moves into bin/fm-timeout-lib.sh as fm_exec_timed with
a separate grace: the perl watchdog is preferred, runs the command in its own
process group against wall-clock deadlines, forwards TERM/INT/HUP, and reaps
the group, so a descendant holding captured output can no longer keep the
caller waiting past the bound on a host without timeout.

The Claude Stop auto-arm header records that Claude drops the exit 2 of a hook
it terminated at the configured timeout, re-measured on Claude Code 2.1.281.

* fix(bin): state that fm_exec_timed cannot reach a descendant in its own process group

Live runs of real Claude and Pi engine turns under the bound showed both CLIs
start every tool command in a process group of its own, so those processes end
through the engine's own TERM handling rather than the group signal or reap.
Also clears the new timeout test's ShellCheck findings.

* no-mistakes(document): Clarify cross-harness lease documentation
mehulbhagwani pushed a commit to mehulbhagwani/firstmate that referenced this pull request Sep 27, 2026
* feat(bin): guard the partition, harness pin, and bounded exec for a non-Pi supervision host

Lease liveness is now the pure record test in every calling context, so an
unmarked main honors a live branch lease held by a separate process, and a
lease file engages the guard's claim serialization for any caller; a home
with no lease files still takes no lock.

bin/fm-harness.sh honors FM_SUPERVISION_PRIMARY_HARNESS while
FM_SUPERVISION_ACTOR=branch, so a supervision branch running under another
harness resolves own, crew, and secondmate to the primary's harness.

fm_tasks_axi's watchdog moves into bin/fm-timeout-lib.sh as fm_exec_timed with
a separate grace: the perl watchdog is preferred, runs the command in its own
process group against wall-clock deadlines, forwards TERM/INT/HUP, and reaps
the group, so a descendant holding captured output can no longer keep the
caller waiting past the bound on a host without timeout.

The Claude Stop auto-arm header records that Claude drops the exit 2 of a hook
it terminated at the configured timeout, re-measured on Claude Code 2.1.281.

* fix(bin): state that fm_exec_timed cannot reach a descendant in its own process group

Live runs of real Claude and Pi engine turns under the bound showed both CLIs
start every tool command in a process group of its own, so those processes end
through the engine's own TERM handling rather than the group signal or reap.
Also clears the new timeout test's ShellCheck findings.

* no-mistakes(document): Clarify cross-harness lease documentation
RooseveltAdvisors pushed a commit to RooseveltAdvisors/firstmate that referenced this pull request Sep 29, 2026
* feat(bin): guard the partition, harness pin, and bounded exec for a non-Pi supervision host

Lease liveness is now the pure record test in every calling context, so an
unmarked main honors a live branch lease held by a separate process, and a
lease file engages the guard's claim serialization for any caller; a home
with no lease files still takes no lock.

bin/fm-harness.sh honors FM_SUPERVISION_PRIMARY_HARNESS while
FM_SUPERVISION_ACTOR=branch, so a supervision branch running under another
harness resolves own, crew, and secondmate to the primary's harness.

fm_tasks_axi's watchdog moves into bin/fm-timeout-lib.sh as fm_exec_timed with
a separate grace: the perl watchdog is preferred, runs the command in its own
process group against wall-clock deadlines, forwards TERM/INT/HUP, and reaps
the group, so a descendant holding captured output can no longer keep the
caller waiting past the bound on a host without timeout.

The Claude Stop auto-arm header records that Claude drops the exit 2 of a hook
it terminated at the configured timeout, re-measured on Claude Code 2.1.281.

* fix(bin): state that fm_exec_timed cannot reach a descendant in its own process group

Live runs of real Claude and Pi engine turns under the bound showed both CLIs
start every tool command in a process group of its own, so those processes end
through the engine's own TERM handling rather than the group signal or reap.
Also clears the new timeout test's ShellCheck findings.

* no-mistakes(document): Clarify cross-harness lease documentation
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