Skip to content

feat: add external host-root supervision - #2

Closed
YrFnS wants to merge 45 commits into
mainfrom
feat/host-root-mode-upstream
Closed

feat: add external host-root supervision#2
YrFnS wants to merge 45 commits into
mainfrom
feat/host-root-mode-upstream

Conversation

@YrFnS

@YrFnS YrFnS commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Intent

Update FirstMate PR kunchenguid#1238 to the latest upstream main and fix the real Herdr/Pi user experience where one worker caused repeated routine 'Captain, shipshape.' responses during intermediate Pi turns. Preserve FirstMate's push-based architecture: the main supervisor should remain idle while a worker runs, and the worker should wake it once when the logical run settles with a final report, blocker, or decision. Do not merely hide the phrase or weaken final-completion detection. For Pi workers, inner turn boundaries and queued continuations must stay silent; semantic idle must be recorded before the settled notification is emitted. Keep other harnesses and non-host-root behavior unchanged, validate the behavior with executable tests, update the existing PR branch, and do not merge the PR.

What Changed

  • Add optional host-root supervision and Pi activation, keeping the external host authoritative while ordinary workers launch from isolated target worktrees.
  • Harden backend identity, metadata, promotion, recovery, and teardown across tmux, Herdr, Orca, Zellij, and cmux, including overlap and rollback safeguards.
  • Keep intermediate host-root Pi turns silent until semantic idle is recorded, surface queued process-event wakes, retire terminal sources, and add supporting documentation and executable coverage.

Risk Assessment

⚠️ Medium: Captain, all substantiated findings are resolved, but the branch remains a broad lifecycle-sensitive host-root change whose dedicated test stage is important before merge.

Testing

Commit ancestry, focused lifecycle tests, real Herdr host-root operation, non-host compatibility, broader host-root safeguards, and rebased upstream process-event behavior all passed. A live Herdr/Pi run captured both terminal turns and proved one post-idle notification; two earlier live attempts reached the correct lifecycle but required evidence-only fixes for pane capture and line wrapping. The working tree remained clean. Screenshots were not applicable because this is a terminal lifecycle change; real terminal and ordered event transcripts were captured instead.

Evidence: Live Herdr/Pi lifecycle transcript

Herdr 0.7.5 / Pi 0.83.0: no notification after the first turn; follow-up queued; second turn completed; agent_settled reported isIdle=true; exactly one notification then observed state=idle source=pi-ext.

Live Herdr/Pi host-root lifecycle acceptance
Herdr version: herdr 0.7.5
Pi version: 0.83.0
Spawn: warn: no registry at /tmp/no-mistakes-evidence/01KZ0V4R8X7NH9RFXYEW21EGP2/.live-herdr-pi.UkOuMn/home/data/projects.md; defaulting target to no-mistakes off
spawned live-pi-settled harness=pi kind=ship mode=no-mistakes yolo=off window=fm-lab-live-pi-settled-464208:w1:p2 worktree=/home/e2next/.treehouse/target-37e25e/1/target
Intermediate supervisor observation: idle; no notification after first Pi turn boundary
Intermediate semantic state: v1 gen=g1785663121.464658.11919 seq=2 state=busy source=pi-ext event=agent-start ts=1785663124
Pi lifecycle events:
  2026-08-02T09:32:04.264Z event=agent_start
  2026-08-02T09:32:05.825Z event=turn_end turn=1
  2026-08-02T09:32:07.825Z event=followup_queued
  2026-08-02T09:32:09.044Z event=turn_end turn=2
  2026-08-02T09:32:09.045Z event=agent_settled isIdle=true
Supervisor notifications (1):
  2026-08-02T12:32:09,076337219+03:00 notification=turn-ended observed_state=v1 gen=g1785663121.464658.11919 seq=3 state=idle source=pi-ext event=agent-settled ts=1785663129
Final semantic state: v1 gen=g1785663121.464658.11919 seq=3 state=idle source=pi-ext event=agent-settled ts=1785663129
Result: one notification, emitted only after semantic idle and the final report.
Evidence: Live Pi terminal showing both turns and final report
Welcome to fish, the friendly interactive shell
Type help for instructions on how to use fish
e2next@localhost /t/n/0/.l/target (master)> treehouse
get
A new version of treehouse is available: v2.0.0 → v2.1
.1
Run "treehouse update" to update

🌳 Setting up worktree...
🌳 Entered worktree at ~/.treehouse/target-37e25e/1/ta
rget. Type 'exit' to return.
Welcome to fish, the friendly interactive shell
Type help for instructions on how to use fish
e2next@localhost ~/.t/t/1/target ((6406f59c))> export
GOTMPDIR=/tmp/fm-live-pi-settled/gotmp
e2next@localhost ~/.t/t/1/target ((6406f59c))> PATH='/
tmp/no-mistakes-evidence/01KZ0V4R8X7NH9RFXYEW21EGP2/.l
ive-herdr-pi.UkOuMn/fakebin:/home/e2next/.codex/packag
es/standalone/releases/0.146.0-x86_64-unknown-linux-mu
sl/codex-path:/home/e2next/.supercode/bin:/home/e2next
/.local/bin:/home/e2next/.npm-global/bin:/home/e2next/
.npm-global/bin:/home/e2next/.bun/bin:/home/e2next/.su
percode/bin:/home/e2next/.local/bin:/home/e2next/.supe
rcode/bin:/home/e2next/.local/bin:/home/e2next/.cargo/
bin:/home/e2next/.npm-global/bin:/home/e2next/.npm-glo
bal/bin:/home/e2next/.bun/bin:/home/e2next/.local/bin:
/usr/local/bin:/usr/bin:/bin' FM_ROOT_OVERRIDE='/home/
e2next/.no-mistakes/worktrees/ea7b9419c17f/01KZ0V4R8X7
NH9RFXYEW21EGP2' FM_HOME='/tmp/no-mistakes-evidence/01
KZ0V4R8X7NH9RFXYEW21EGP2/.live-herdr-pi.UkOuMn/home' F
M_HOST_ROOT='/tmp/no-mistakes-evidence/01KZ0V4R8X7NH9R
FXYEW21EGP2/.live-herdr-pi.UkOuMn/host' FM_TARGET_WORK
TREE='/home/e2next/.treehouse/target-37e25e/1/target'
FM_PI_HARNESS=pi pi --model 'openai-codex/gpt-5.4-mini
' --thinking 'low' -e '/tmp/no-mistakes-evidence/01KZ0
V4R8X7NH9RFXYEW21EGP2/.live-herdr-pi.UkOuMn/home/state
/live-pi-settled.pi-ext.ts' "$('/home/e2next/.no-mista
kes/worktrees/ea7b9419c17f/01KZ0V4R8X7NH9RFXYEW21EGP2/
bin/fm-operational-input.sh' encode launch-brief < '/t
mp/no-mistakes-evidence/01KZ0V4R8X7NH9RFXYEW21EGP2/.li
ve-herdr-pi.UkOuMn/home/data/live-pi-settled/brief.md'
)"
fd not found. Downloading...
fd installed to /tmp/no-mistakes-evidence/01KZ0V4R8X7N
H9RFXYEW21EGP2/.live-herdr-pi.UkOuMn/pi-config/bin/fd

 pi v0.83.0
 escape interrupt · ctrl+c/ctrl+d clear/exit · /
 commands · ! bash · ctrl+o more
 Press ctrl+o to show full startup help and loaded
 resources.

 Pi can explain its own features and look up its
 docs. Ask it how to use or extend Pi.

[Extensions]
  live-pi-settled.pi-ext.ts, queue-followup.ts


 FIRSTMATE_OP: v1 launch-brief: <!--
 firstmate-execution-mode: host-root -->
 Do not use tools. Reply exactly FIRST_TURN_COMPLETE.


 FIRST_TURN_COMPLETE


 Do not use tools. Reply exactly FINAL_REPORT: worker
 finished.


 FINAL_REPORT: worker finished.

──────────────────────────────────────────────────────

──────────────────────────────────────────────────────
~/.treehouse/target-37e25e/1/target (detached)
↑885 ↓38 $0.001 (sub) 0.2%/272k (auto)  gpt-5.4-mini •
Evidence: Production-extension lifecycle regression transcript
ok - non-host pi extension preserves per-turn notification behavior
ok - host-root pi extension notifies only after a successful semantic idle write
ok - pi extension awaits agent_settled before the next agent_start without a test delay
ok - pi extension events from a superseded incarnation are rejected as stale
ok - kimi and grok install no unverified semantic wiring and classify through their own gates
ok - opencode plugin classifies from session.status, scoped to the latched worker session
ok - claude hooks open on UserPromptSubmit and close on Stop, StopFailure, and SessionEnd
ok - claude hook events from a superseded incarnation are rejected without breaking the hook
ok - codex classifies unknown until a semantic source is verified, never idle or footer-matched
all fm-busy-adapter-wiring tests passed

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 2 issues found → auto-fixed (2) ✅
  • 🚨 bin/fm-spawn.sh:1817 - The criterion requires “semantic idle must be recorded before the settled notification is emitted,” but busyEvent ignores fm-busy-event.sh failures and always resolves, so this new notification still runs after a stale generation, lock timeout, unarmed task, or write failure. The stale-incarnation test re-arms the task and confirms state remains busy but never asserts that no false marker was emitted. Gate notification on a successful idle write and cover that failure path.
  • 🚨 bin/fm-spawn.sh:1814 - The criterion requires “Keep other harnesses and non-host-root behavior unchanged,” but the changed agent_settled handler sits in the unconditional pi|pi-signed branch, replacing turn_end signaling for every ordinary Pi worker. The new test at tests/fm-busy-adapter-wiring.test.sh:111 explicitly exercises and requires this behavior without FM_HOST_ROOT. Scope the change to host-root mode unless broadening default Pi behavior is explicitly approved.

🔧 Fix: Gate host-root Pi notifications on successful idle
1 warning still open:

  • ⚠️ .agents/skills/harness-adapters/SKILL.md:312 - The runtime now limits settled-only notification to host-root mode, but this operator contract still says all Pi workers notify from agent_settled and must never notify from turn_end. Qualify this as host-root-only and document that ordinary Pi retains per-turn notification; otherwise future adapter work may reintroduce the corrected non-host regression.

🔧 Fix: Clarify Pi notification behavior by host mode
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • git log --graph --oneline --decorate --all --max-count=25 and git merge-base --is-ancestor d36a214 HEAD
  • bash tests/fm-busy-adapter-wiring.test.sh
  • bash tests/fm-backend-herdr-host-root-e2e.test.sh
  • bash tests/fm-host-root-mode.test.sh
  • bash tests/fm-spawn-dispatch-profile.test.sh
  • bash tests/fm-procevent.test.sh
  • bash tests/fm-watch-triage.test.sh
  • bash /tmp/no-mistakes-evidence/01KZ0V4R8X7NH9RFXYEW21EGP2/live-herdr-pi-lifecycle.sh
  • git status --short --untracked-files=all and isolated Herdr-session cleanup verification
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

YrFnS added 30 commits August 2, 2026 09:24
YrFnS and others added 15 commits August 2, 2026 09:25
…nchenguid#1500)

* fix(bin): deliver process-event results and retire ended sources

Two defects reproduced during a real Lavish adapter session.

One human `Send & End` produced four captured results: the real feedback,
then recurring empty ended sessions. The generic runner had no way to learn
a source was finished, so every reconcile restarted a poll that returned
immediately. The runner now asks the source's own adapter -
`fm-procevent-<adapter>.sh terminal <result-file>` - and on exit 0 alone
re-proves ownership, drops the registration, and releases its own claim
under one source boundary. Terminal knowledge stays adapter-owned: for
Lavish that is an ended session, a missing session, and the final feedback
delivery the published poll marks with `session_ended`. An adapter with no
terminal command keeps its source armed exactly as before. Capture before
publication, captured-result durability, queued wake durability, bounded
re-announcement, handled deduplication, one-owner ownership, and explicit
idempotent retirement are all unchanged.

A captured result queued its `check` wake durably, but a healthy watcher
with a fresh beacon never delivered it; the result surfaced only after a
manual drain. Publication happens outside the watcher (in the runner) or
unconditionally (in reconcile), so the watcher had no newly actionable
signal to report and never reached its rewake path. It now reports a
queued-but-unsurfaced process-event record through the same actionable exit
every other wake uses, deduplicated by the same `.seen-*` marker discipline
the signal scan uses, so the record is always durable before it is
suppressed. The durable queue remains the authority and no second notifier,
poller, timer, queue, or adapter-specific wake path is added.

Regressions cover both, driven end to end: an armed Lavish source against a
stand-in for the published poll polls once, captures once, publishes one
distinct event, and retires itself; two fixture adapters prove the terminal
decision follows the adapter alone; and a real capture plus a real watcher
prove one proactive wake before any drain, with no duplicate wake while the
record stays queued or after it is acknowledged.

* no-mistakes(review): Harden process-event retirement and proactive delivery

* no-mistakes(review): Route process-event delivery through shared wake owner

* no-mistakes(document): Clarify process-event delivery and retirement documentation

* no-mistakes(lint): Fix ShellCheck control-flow warnings

* no-mistakes(lint): Fix wake output status lint warning
@YrFnS

YrFnS commented Aug 2, 2026

Copy link
Copy Markdown
Owner Author

Closing this duplicate fork PR; the validated branch updates the existing upstream PR kunchenguid#1238.

@YrFnS YrFnS closed this Aug 2, 2026
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.

2 participants