Skip to content

Merge upstream round 3 of 3 through 7b88520c - #199

Merged
HelloWorldSungin merged 14 commits into
mainfrom
fm/fm-upstream-sync-2026-08-24-round-3
Aug 25, 2026
Merged

HelloWorldSungin merged 14 commits into
mainfrom
fm/fm-upstream-sync-2026-08-24-round-3

Conversation

@HelloWorldSungin

@HelloWorldSungin HelloWorldSungin commented Aug 25, 2026 •

Copy link
Copy Markdown
Owner

Round 3 of 3. Takes the contiguous upstream first-parent range f170cede..7b88520c from kunchenguid/firstmate as one merge commit, preserving upstream parentage so the merge base can advance.

  • Fork base: HelloWorldSungin/firstmate@0344b1d23d3152f13ebcd753d1155d359e1459f6
  • Measured merge base: f170cedeb735759e9547a5b9de1a26eca7ea6d71
  • Upstream endpoint: kunchenguid/firstmate@7b88520c055408a18f1476ecce08be60b2885fc9
  • Exactly one merge commit on the branch (d25ca4db), second parent 7b88520c055408a18f1476ecce08be60b2885fc9 (git rev-list --merges over the branch returns 1)

This completes the planned three-round sequence. Upstream has advanced 6 further first-parent commits beyond 7b88520c - fb2ce5b2, 8b21c99e, 52ff62e8, d55e00af, 8fa0505e, 038d0f7e - which a future round takes. Each was checked with git merge-base --is-ancestor against this branch's head and is absent.

Applicability of the eight upstream first-parent changes

Upstream change Applies here Reasoning
kunchenguid/firstmate#2876 2f250c7ab37d test(watcher): stop fixture confirmation budgets racing real child startup Yes, adopted whole Merged into tests/fm-watcher-lock.test.sh beside the fork's own peer-readiness fix in the same file; both sides' changes are present. Upstream's ARM_FAIL_EXIT_POLLS budget and its two production-budget cases landed unmodified.
kunchenguid/firstmate#2870 197afbb79f8b fix(bin): deterministically order remote tool paths Yes, adopted whole bin/fm-remote-job-lib.sh has no fork divergence. The only conflict was adjacency in tests/fm-remote-job.test.sh, where the fork's set -m sits next to upstream's new mise-ordering assertion; both kept.
kunchenguid/firstmate#2848 52f62ab155e8 fix(bin): prevent routed secondmate work from stranding Yes, adopted whole, with one fixture race repaired Receiver-wake correlation in bin/fm-backlog-handoff.sh plus the staged handoff_wake_retire* sequence in bin/fm-teardown.sh. This home has no registered secondmates, so the operational risk is low, but the handoff contract was merged rather than skimmed - see the teardown row below for how it composes with the fork's manifest gate. Its new tests/fm-backlog-handoff.test.sh carried a race that failed this PR's first CI run; see "Repair 3".
kunchenguid/firstmate#2857 822a9902484b fix: make macOS inbox test path portable Yes, adopted whole bin/fm-inbox.sh path handling; no fork divergence, merged clean.
kunchenguid/firstmate#2856 e46df1a55a9e feat(bin): deliver local steers through durable task inboxes Yes, adopted whole - highest-impact change in the round Rewrites bin/fm-send.sh into two data planes. Two fork divergences collided with it; both are repaired and re-pinned. Details under "Send contract" below.
kunchenguid/firstmate#2891 8d8362c9abeb feat(bin): add fast local lint mode Yes, adopted whole bin/fm-lint.sh gains --fast; CI and no-mistakes keep the no-argument full-analysis path. The fork's ShellCheck divergence lives in bin/fm-install-shellcheck.sh, which this change does not touch (git diff 0344b1d2 HEAD -- bin/fm-install-shellcheck.sh is empty).
kunchenguid/firstmate#2901 ddf74ef22f73 feat(bin): deliver remote steers through durable inboxes Yes, adopted whole The remote leg moves to the same durable-record model with idempotent enqueue. The fork's previous remote exit-3-as-delivered handling in the typed block is superseded by upstream's design rather than carried alongside it.
kunchenguid/firstmate#2858 7b88520c0554 feat: add persistent Pi supervision branch Yes, adopted whole New .pi/extensions/fm-branch-supervision.ts, lib/fm-branch-dispatch.ts, bin/fm-branch-{outcome,prompt}.sh, bin/fm-lease{,-lib}.sh, docs/pi-supervision-branch.md. The fork's own .pi/extensions/fm-primary-pi-watch.ts conflicted only on the node:fs import line; the fork's existsSync (away-flag read) and upstream's readdirSync (queue project scan) are both kept. Inert on this home, which is not a Pi primary and has no config/pi-supervision-branch grant.

Send contract: what a caller can now conclude from an exit status

This is a real contract change, adopted deliberately rather than reconciled quietly.

For an ordinary local or remote text steer, on every harness, the exit status now reports the durable inbox record, not whether the terminal accepted the keystroke. Exit 0 means the steer is durably recorded; a swallowed doorbell is no longer an error, because the watcher's re-ring ladder owns loss detection from there. The pre-merge property "fm-send exits non-zero when an Enter is positively swallowed" therefore now describes the typed plane only - a harness-native invocation (/..., or $... to a codex target) or an explicit backend target.

Verified as still holding after the merge, behaviourally and not merely by test presence:

  • Fail-closed target resolution. An unresolvable selector still refuses instead of falling back to a search: tests/fm-send-strict.test.sh cases unresolvable selectors do not fall back to tmux, prefixless herdr pane ids are rejected before tmux fallback, unmatched single-colon explicit targets must verify live before sending, and unset FM_HOME fails before target resolution all pass.
  • The unverifiable vs pending distinction (active ledger divergence). Both still exit 3, unverifiable still marks delivery state unknown while pending still discards as undelivered, and both are still exercised end to end. tests/fm-agy-adapter.test.sh passes, including a matching cached agy prompt snapshot without a receipt must remain unverifiable (exit 3, verdict=unverifiable), an unsubmitted steer on a composer-supported harness is reported as delivered-unconfirmed (exit 3, verdict=pending), and the three agent_not_found / agent_not_ready / agent_prompt_failed known-undelivered cases (exit 1).
    Those cases previously sent plain text. After the merge that text rides the inbox and exits 0 off the record, so they asserted nothing about the adapter - they had gone vacuous, not red. They now send a harness-native invocation, naming the plane they test, and the suite header records why.
  • The consequence, stated plainly: an ordinary agy steer no longer produces unverifiable. Its delivery is now a durable record backed by a re-ring ladder, which is stronger in the direction that matters - a steer is never silently lost - but it is a narrowing of where that verdict is reachable. The ledger entry is updated to say so.

Repair 1: the agy/cursor doorbell had silently lost the atomic prompt

Upstream's fm_task_inbox_ring calls the shared submit dispatch without an expected-harness argument. On Herdr, cursor and agy are routed to Herdr's atomic agent prompt by exactly that argument, so every ordinary steer's doorbell fell back to the typed composer text this fleet does not trust for those harnesses. Nothing failed; the coverage simply stopped reaching the adapter.

fm_task_inbox_ring now takes the target's harness and passes it through, and all three ring callers - bin/fm-send.sh, bin/fm-watch.sh, bin/fm-remote-secondmate-control.sh - resolve it from the target's own metadata. New regression test_ordinary_agy_steer_rings_doorbell_through_atomic_prompt asserts the prompt log is used and that no separately typed text is left behind. Counterfactual checked: reverting the one-argument change makes that case fail with the agy doorbell did not use the atomic prompt, so it is not vacuous.

Repair 2: the fork's scout completion gate moved onto the durable plane

A steer to a scout that already reported its captain calls reviewed reopens that completion gate, so a follow-up cannot be erased past the gate captain-hold-lifecycle owns. That reopen lived on the typed submit path, which ordinary steers no longer take - it would have retired silently. It now fires at durable enqueue and is restored when the record cannot be written, while the typed plane keeps its existing send-failure and unconfirmed-submit behaviour. tests/fm-send-strict.test.sh pins both planes in both failure directions.

This divergence was fork-local and unrecorded since 2026-08-12; it now has its own ledger entry.

Repair 3: the pre-move crash fixture landed the move it simulates crashing before

The first CI run on this branch was green except for the expected no-mistakes check and Behavior portable serial 6, which failed in tests/fm-backlog-handoff.test.sh (new upstream suite from kunchenguid/firstmate#2848) with pre-move crash recovery failed: Task "pre-move-crash" not found in this backlog.

The stub tasks-axi for that case kills the handoff and then execs the real mv anyway, so an orphan lands the move about a second later. Every later assertion that the item is still in the source backlog then holds or fails purely on how fast the runner is - it passes locally and failed on CI.

Reproduced deterministically rather than assumed: adding a three-second wait after the simulated crash makes the upstream stub fail the case locally every time. The stub now exits without moving, which is the scenario it exists to build, and with that same three-second window the case passes. Recorded as a ledger entry, since upstream will most likely land its own fix and retire it.

Base reconciliation after HelloWorldSungin/firstmate#200

#200 landed on main after this PR first went green, so origin/main moved from 0344b1d2 to 32d2e6ca. The base was absorbed by merge (git merge origin/main, commit c5e824c2) rather than by rebase: this branch's value is the single upstream merge and its two parents, and a rebase would linearize it and destroy that parentage. No force-push and no squash; the push was a fast-forward.

One file conflicted, docs/configuration.md, and it was a collision rather than a contradiction - one sentence, two different clauses appended to the same list. The resolution starts from main's sentence, which carries #200's new recall search-read clause, and re-inserts this round's steering-inbox clause ahead of the pending-reply clause. All three of #200's edits to that file are present after the merge: the state/recall.jsonl clause, the widened FM_RECALL_TIMEOUT note, and the new FM_RECALL_JSONL_MAX_BYTES operator-inventory entry. AGENTS.md auto-merged, keeping #200's trimmed-to-a-pointer recall.jsonl row alongside this round's four new state records.

Structure re-verified after the reconciliation:

  • The upstream merge commit is still d25ca4db, unchanged, with exactly two parents; the second is still 7b88520c055408a18f1476ecce08be60b2885fc9.
  • The branch carries two merges: that upstream merge, and c5e824c2 absorbing origin/main (whose second parent is a fork commit).
  • All six upstream first-parent commits past the endpoint remain excluded, re-checked against the new head with git merge-base --is-ancestor. Every branch commit that is not an ancestor of the endpoint is fork-side work.

Re-run on the reconciled head c5e824c2: bin/fm-lint.sh exit 0; full suite 207 suites, 3 failed - the same three suites and the same failing cases as the fork-parent baseline (ruby is required to parse .github/workflows/ci.yml as YAML, arm did not exit with HUP status (got 124), and the orphaned-process case in fm-remote-job-worker-leak). bin/fm-doc-audience-check.sh (118 surfaces, 635 local links) and bin/fm-pointer-check.sh (0 broken, 0 unverified) both pass.

Baseline versus final, matched by suite and case identity

All three runs on one host, Linux, 24 cores, under bin/fm-test-run.sh --all, taken from isolated temporary clones for the two parents so the task branch and its merge structure stayed untouched. Harness taken under claude, which is what CI's harness-detection and supervision-text suites expect - a non-claude local harness fails those suites spuriously, which is exactly how a real regression hides behind an unchanged failure count, so the set is matched by identity below rather than by count.

Run Suites Failed Lint
Fork parent 0344b1d2 201 3 bin/fm-lint.sh exit 0
Upstream parent 7b88520c 160 6 bin/fm-lint.sh exit 0
Merge result a0e41ea7 207 3 bin/fm-lint.sh exit 0

Every remaining merge failure is attributed to the fork parent by suite AND failing-case identity:

Suite Failing case Fork parent Upstream parent Merge
tests/fm-test-run.test.sh ruby is required to parse .github/workflows/ci.yml as YAML fails fails fails
tests/fm-watcher-lock.test.sh arm did not exit with HUP status (got 124) fails fails fails
tests/fm-remote-job-worker-leak.test.sh HUP of the serving worker after its lock directory disappeared left orphaned processes fails suite absent upstream fails

fm-test-run is a host gap (no ruby installed). The other two are host timing and process-group cases. None is introduced by this round: the merge's failing set is identical to the fork parent's.

The upstream parent additionally fails fm-bootstrap, fm-on, fm-remote-doctor, and fm-session-start. None of those four appears in the merge result, so nothing upstream-parent-only was carried in. The upstream parent runs 160 suites rather than 201 because the fork carries suites upstream does not have; the merge runs 207 because this round adds fm-task-inbox, fm-send-inbox, fm-pi-branch-extension, fm-branch-supervision, and their live-gated siblings.

Also green on the merge result: bin/fm-doc-audience-check.sh (118 surfaces, 630 local links) and bin/fm-pointer-check.sh (32 checked, 0 broken, 0 unverified).

Survival evidence for every active deliberate divergence

Ledger entry Evidence
Agy crew adapter, incl. the unverifiable/pending send verdicts tests/fm-agy-adapter.test.sh green with the verdict cases retargeted to the plane where those verdicts live, plus the new atomic-prompt doorbell regression and its counterfactual. See "Send contract" above. Entry updated.
Watcher restart hand-over bin/fm-watch-arm.sh still declares --restart upstream's handling successor via FM_WATCH_PREDECESSOR_ARM_PID (bin/fm-watch-arm.sh:376,445-446,504), and tests/fm-watcher-lock.test.sh retains test_watch_restart_rejects_reused_pid, test_watch_restart_attaches_to_healthy_peer, and test_watch_restart_hands_over_within_its_own_budget, all registered in the runner and all passing. The only failing case in that suite is the pre-existing HUP-status case, which fails identically on the fork parent. Both halves hold: a fresh watcher takes the lock inside the arm's own stop budget, and the outgoing watcher is gone by the time it does.
Run-progress wedge hold (required fifth hold-count parameter) wedge_timer_check keeps its 5-parameter signature at bin/fm-watch.sh:720, and all four call sites plus busy_turn_bound_check pass the hold-count file. Upstream added no new wedge_timer_check caller this round, so nothing arrived one argument short. bin/fm-run-progress.sh and crew_wedge_progress are unchanged.
Pinned ShellCheck download retry budget git diff 0344b1d2 HEAD -- bin/fm-install-shellcheck.sh is empty; the wall-time budget and its comment are untouched by kunchenguid/firstmate#2891.
Repository-local validation evidence .no-mistakes.yaml test.evidence.store_in_repo re-read after the merge and still false, with the fork's explanatory comment intact. (This file does not conflict textually, so it is re-read rather than trusted to a clean merge.)
Upstream-read-only posture in shared tracked docs AGENTS.md's hard-rule block kept the fork's rule-2 insertion and 3-6 renumbering; tests/fm-agents-hard-rules.test.sh passes. CONTRIBUTING.md still does not restore upstream's clone-the-parent instruction (the only kunchenguid references left are the no-mistakes product links).
LLM quota sidecar Untouched this round; tests/fm-quota-sidecar.test.sh passes.
Herdr pre-Enter footer read on a native working baseline tests/fm-backend-herdr.test.sh merged clean and passes, keeping the fork's renumbered response indices.
GBrain per-home knowledge memory Fork wiring intact in bin/fm-brief.sh (the # Brain section still renders in the generated briefs), bin/fm-bootstrap.sh, and bin/fm-teardown.sh; fm-gbrain-* and fm-recall suites pass.
Fleet dashboard and agent-event instrumentation bin/fm-spawn.sh still composes the per-harness event emitters; teardown still publishes the completion manifest and still refuses to erase a task whose manifest could not be written - publish_outcome_manifest remains a blocking `
Upstream tracking mechanism This PR is the mechanism exercising itself.

Per-file reasoning for every changed contract file

Conflicted files (20), resolved on meaning:

  • AGENTS.md - kept the fork's condensed layout lines and added upstream's four new records (config/pi-supervision-branch, <id>.inbox/, the branch outcome store and session, .lease-<task>) rewritten in that condensed style. Upstream's rewritten section 7 steer paragraphs merged clean and now describe the durable-inbox model.
  • .agents/skills/harness-adapters/SKILL.md - the "theirs" side was upstream's pre-split per-harness tables, which the fork moved into harnesses/*.md. Kept the fork's structure and ported upstream's two sentence edits into harnesses/opencode.md and harnesses/cursor.md, where those facts now live.
  • bin/fm-send.sh - adopted upstream's two-plane header and rewritten body; re-inserted the fork's unverifiable paragraph into the typed-plane exit contract, the fork's TARGET_HARNESS argument to fm_backend_send_text_submit, the unverifiable verdict arm, and the scout completion-gate helpers on both planes. Upstream's fm_send_known_undelivered_cleanup replaces the fork's inline discard branches, with the fork's fm_send_restore_scout_completion kept beside it. Upstream removed the remote branch from the typed block entirely (remote text now rides the inbox), so the fork's remote exit-3 handling there is superseded rather than duplicated.
  • bin/fm-watch.sh - kept the fork's fm_sup_busy_turn_max_seconds window and its bin/fm-supervision-lib.sh source, added upstream's SECONDMATE_WAKE_STALL_SECS, fm-task-inbox-lib.sh source, and two new stale-reason header entries. Threaded the target harness into the re-ring call.
  • bin/fm-teardown.sh - kept both library sources; ordered upstream's handoff_wake_retire (retryable route cleanup) before the fork's publish_outcome_manifest (blocking, last point at which the composed records still exist). In the secondmate path, upstream's stage/restore/commit sequence now wraps the fork's NO_VERDICT_RETAIN_WORKTREE guard, so a retained worktree still commits the route retirement.
  • bin/fm-brief.sh - kept the fork's BRAIN_SECTION and ship-only PROJECT_MEMORY_SECTION variables and added upstream's $INBOX_SECTION in the ordering the already-merged charter template established (inbox, then brain, then the rest).
  • bin/fm-pr-merge.sh - kept the fork's fm-issue-lib.sh / fm-forge-lib.sh sources and added upstream's fm_lease_forbid_branch guard. Re-verified for this PR's own landing: caller_has_merge_method still detects an explicit --merge, so --squash is not added and --merge is forwarded verbatim to gh-axi pr merge.
  • bin/fm-test-run.sh - took upstream's re-wrapped family lists and new fm-task-inbox routing, unioned the live-harness-optin list so the fork's fm-agy-smoke, fm-gbrain-*, fm-cmux-claude-composer, and fm-dashboard-browser entries survive alongside upstream's new fm-pi-branch-live-e2e and fm-send-inbox-doorbell-live-e2e, and kept the fork's dashboard-inclusive snapshot-bearings arm.
  • .pi/extensions/fm-primary-pi-watch.ts - import-line conflict only; kept both existsSync (fork away-flag read) and readdirSync (upstream queue project scan).
  • CONTRIBUTING.md - took upstream's two new fixture-timeout sentences and kept the fork's wording about opt-in live tests staying outside portable lanes.
  • docs/architecture.md - adopted upstream's reworded secondmate sentence (which now distinguishes worktree-write probing from the new wake-queue probing) and dropped the duplicate busy-pane sentence the fork had already moved into the run-progress paragraph.
  • docs/configuration.md - kept the fork's data/ and fleet-data-contracts lines while taking upstream's inbox clause into the state/ sentence; kept the fork's agy sentence beside upstream's typed-plane Cursor rewording; kept the fork's four supervision env lines and added upstream's FM_SECONDMATE_WAKE_STALL_SECS.
  • docs/herdr-backend.md - adopted upstream's typed-plane framing sentence and rewrote the following sentence so the fork's cursor/agy atomic-prompt paragraph reads as part of that plane rather than an unrelated "instead"; applied upstream's agent-process liveness rewording to the fork's longer bullet.
  • docs/scripts.md - took upstream's updated fm-send.sh row and four new rows, kept the fork's fm-trigger-validation.sh row.
  • docs/supervision-protocols/pi.md - kept both paragraphs: the fork's away-mode standby rule and upstream's supervision-branch delegation rule.
  • docs/verification/runtime-backends.md - both sides added a new section at the same position; kept the fork's agy control-mechanics record and upstream's Pi supervision-branch record.
  • tests/fm-gotmp.test.sh - unioned the teardown fixture symlinks, folded the two fm-timeout-lib.sh rationales into one comment, and removed the duplicate fm-control-lib.sh link that the union would otherwise have made a second ln -s onto an existing path.
  • tests/fm-remote-job.test.sh, tests/fm-remote-secondmate-lifecycle-e2e.test.sh, tests/fm-send-popup-settle.test.sh - kept both sides; the popup-settle case adopts upstream's rides_inbox assertion and keeps the fork's trailing all-tests-passed marker.

Non-conflicted contract files worth naming:

  • bin/fm-task-inbox-lib.sh (new, upstream) - amended so fm_task_inbox_ring takes and forwards the target harness; the header comment records why that argument is not optional in effect.
  • bin/fm-remote-secondmate-control.sh - resolves REMOTE_ENDPOINT_HARNESS from the endpoint metadata and passes it to the ring.
  • tests/fixtures/fm-brief-no-issue.sha256 - regenerated for upstream's new instruction-inbox section (about +600 bytes per variant, +735/+759 for the two secondmate charters, which also gained a sentence). Values match what tests/fm-brief.test.sh independently computes; the suite is green.
  • docs/fork-divergence.md - see below.

Parked branches

The ledger's parked-branch list is docs/fork-divergence.md "Parked branches outside the fork baseline": fm/fm-afk-injection-wedge, fm/fm-crew-state-blind-during-fix-round, fm/fm-parked-decision-stale-noise, fm/fm-subagent-model-routing-guard, fm/fm-vault-drift-check.

Each was checked with git merge-base --is-ancestor <branch> HEAD; none is an ancestor of this branch. Nothing was merged, rebased onto, resurrected, or cherry-picked from any of them.

Ledger changes in this round

  • Agy crew adapter - amended. Records that upstream's durable steering inbox scopes the unverifiable/pending verdicts to the typed plane, that the distinction itself is unchanged, that tests/fm-agy-adapter.test.sh now names the plane so a verdict case cannot go vacuous, and that the inbox plane still owes this adapter its atomic prompt through the harness argument every ring caller resolves from metadata.
  • Scout completion gate reopened by a firstmate steer - added. Fork-local and unrecorded since 2026-08-12; this round is where it first collided.
  • Pre-move crash fixture does not perform the move it simulates crashing before - added, for Repair 3 above.
  • No entry was retired.

Head and mergeability

  • Pushed head: c5e824c2658a5b33626831f43c45e8d09d75967c
  • Base at check time: origin/main = 32d2e6ca1fc40c8251cefc78b8972a7d55f71d2d (after HelloWorldSungin/firstmate#200)
  • Mergeable: yes. git merge-base --is-ancestor origin/main HEAD succeeds, so the base is an ancestor of this head and no conflict is possible; git merge-tree --write-tree origin/main HEAD writes a tree cleanly.
  • The expected single red remains PR must be raised via no-mistakes; every other check must be green. The previous head 4f8364cf reached exactly that state (16 passed, 1 failed), including Behavior portable serial 6, which the Repair 3 fixture fix turned green.

Expected red check

This round ships direct-PR, not through no-mistakes. The no-mistakes pipeline rebases onto origin/main, which would replay and linearize this merge-only branch and destroy the upstream parentage the round exists to preserve. The fork's compliance workflow requires a no-mistakes signature with no bypass for direct PRs, so this PR carries the red PR must be raised via no-mistakes check by design. Every other required check must pass.

Landing

Do not squash. Land with:

bin/fm-pr-merge.sh fm-upstream-sync-2026-08-24-round-3 <full-PR-url> -- --merge

The script defaults to squash, and squashing loses upstream parentage and prevents the merge base from advancing. The explicit --merge is load-bearing. Rounds 1 and 2 both proved this path: the merge base moved 1cb900c2 to 52d20f13 to f170cede exactly as intended, and this round should move it to 7b88520c.

karotkriss and others added 14 commits August 23, 2026 11:25
…artup (kunchenguid#2876)

tests/fm-watcher-lock.test.sh passed in isolation but failed intermittently
under full-suite and ambient concurrent load. bin/fm-watch-arm.sh computes its
confirmation deadline immediately after forking the real child watcher, so the
child's entire fork, exec, lock acquisition and beacon publication has to land
inside that wall clock. Two cases shrank that budget to one second, leaving a
two-second window for work measured at 3.1-4.9s under CPU oversubscription, so
the arm honestly reported "FAILED - no live watcher with a fresh beacon" and
their premises collapsed. A third case ran on the production budget, but its
child must also execute a registered check before exiting: measured at 1.9-2.3s
idle and 9.1-13.1s under load, against an 11s budget.

The two cases that must confirm a real child now hold the arm to production's
own budget instead of a shrunken fixture one, the immediate-wake case gets an
explicit budget with headroom over its measured loaded cost, and the two waits
for the arm's typed failure are sized off the largest production default rather
than a fixed eight seconds.

No bin/ change and no default behavior change: the lock's fail-closed semantics,
SIGSTOP handling, stale-heartbeat detection and the arm's typed failures are
untouched. Verified 4/4 green at 3x CPU oversubscription (loadavg 75-80) after
3/3 red before the change, and CONTRIBUTING.md records the convention.
* fix(bin): order discovered tool installs by the shell's own expansion

fm_remote_job_compose_operator_path built the asdf and mise install
directories with `compgen -G`, which does not sort. Bash sorts glob
matches in pathexp.c, on the shell's own pathname-expansion path only;
`compgen -G` reaches the same glob_filename through pcomplete.c, which
sorts nothing. On bash 3.2 (macOS /bin/bash) and every bash before 5.3
that handed the composition raw readdir order, so which install of a
multi-version tool a remote job resolved was decided by directory order
on disk rather than by this composition.

Expand the globs at the call sites and let the function take the matches,
so the composition and the documented portable-PATH contract are the same
operation. Quoting the account home at the call site also stops a home
whose name contains glob metacharacters from being reinterpreted.

The colocated regression pins both the order and the mechanism: bash 5.3
moved sorting into the glob library, so an order-only assertion cannot
see the defect there.

* no-mistakes(review): Remove source-reading PATH regression guard
…2848)

* fix: surface stalled secondmate queues and wake handoffs

* no-mistakes(review): Make handoff wakes retryable and stall alerts crash-safe

* no-mistakes(review): Prevent duplicate handoff wakes and cover remote delivery

* no-mistakes(review): Serialize local handoffs and preserve pre-move wake intent

* no-mistakes(review): Serialize teardown with handoffs and retain remote wake confirmation

* no-mistakes(review): Reconcile correlated handoff wake delivery after crashes

* no-mistakes(review): Keep failed wakes retryable and isolate stall receipts

* no-mistakes(review): Reset known-undelivered wake attempts for durable retries

* no-mistakes(review): Refuse duplicate sends for unresolved delivery attempts

* no-mistakes(review): Atomically restore retryability after reconciled send failures

* no-mistakes(review): Serialize delivery confirmation with reconciliation

* no-mistakes(document): Document routed wake and stall supervision

* no-mistakes(lint): Fix ShellCheck expansion and subshell warnings

* no-mistakes: apply CI fixes

* no-mistakes: apply CI fixes

* no-mistakes(review): Retire stale wake state and defer pre-move wakes

* no-mistakes(review): Secure markers, bind batches, and preserve teardown routes

* no-mistakes(review): Preserve unresolved prepared wakes across unrelated handoffs

* no-mistakes(review): Preserve prepared wakes before unrelated moving handoffs

* no-mistakes(document): Document prepared wake batch ownership

* no-mistakes: apply CI fixes

* no-mistakes: apply CI fixes

* no-mistakes(review): Make local wake retirement recoverable

* no-mistakes(document): Clarify handoff recovery and teardown documentation
…guid#2856)

* feat(bin): steer local tasks by durable inbox record plus constant doorbell

Stage 1 (local steers) of the captain-adopted reframe in
data/fm-send-reliability-reframe-s1/report.md: an ordinary fm-send text
steer to a task recorded in this home is appended as a sequenced durable
record under state/<id>.inbox/ and the terminal receives only one constant
self-describing doorbell line, best-effort. The worker acknowledges by
moving the record into handled/; the watcher re-rings an unacknowledged
message on an idle pane and escalates once as an ordinary stale wake.
--resolve-key closes decisions at enqueue time, because the durable
enqueue IS delivery to the task's record. bin/fm-task-inbox-lib.sh owns
the record format, doorbell line, and re-ring ladder.

The typed plane remains for what must reach the terminal itself:
lifecycle keys, harness-native slash and codex $-skill invocations,
explicit backend targets, and the remote secondmate leg (unchanged until
the remote inbox leg ships separately). The composer classifier is
demoted from delivery proof to an advisory ring guard that skips only on
a proven pending verdict.

Verified live against claude, codex, opencode, pi, grok, and muse: each
real worker read its record, acted, and acked with the mv
(docs/verification/runtime-backends.md "Steering-inbox doorbell").

* docs(verification): flag the grok 1.0.5 composer-matrix staleness observed by the doorbell run

* test(captain-hold): read the chat-channel answer from the durable inbox record

* test: migrate fm-control's marker contrast to the inbox record and fix macOS wc padding in the tool-update suite

* no-mistakes(review): Harden inbox locking, teardown races, and acknowledgements

* no-mistakes(review): Serialize watcher actions with inbox acknowledgements

* no-mistakes(review): Bound metadata locking and tighten acknowledgement rechecks

* no-mistakes(review): Preserve exact inbox bytes and harden delivery recovery

* no-mistakes(review): Harden watcher bookkeeping against concurrent inbox teardown

* no-mistakes(document): Update inbox and typed-plane documentation

* no-mistakes: apply CI fixes

* no-mistakes: apply CI fixes

* revert(pipeline): keep parser-native secondmate marking and the both-failed exit out of stage 1

The CI monitor's fix changed the secondmate marking contract for
parser-native invocations (appending the marker after the text) and
softened the both-commit-and-marker-failed branch to exit 0. The merge
authority ruled the marking question out of scope for this stage-1
transport PR (follow-up: fm-send-secondmate-harness-invocation-r1) and
ruled the both-failed case a loud nonzero local failure. Restore both,
keeping the monitor's legitimate migrations and hardening.

* no-mistakes(document): Document inbox and typed-plane boundaries

* no-mistakes(document): Scope backend transport docs to typed plane

* no-mistakes(document): Clarify inbox attempt-budget documentation

* no-mistakes: apply CI fixes

* fix(send): the durable record alone governs the inbox exit status

Captain-refined ruling on the F2/Greptile finding: the durable inbox
record is what delivers the steer, so pending-reply bookkeeping trouble
after a successful enqueue never exits nonzero - a resend-inviting status
would make automated callers enqueue the delivered instruction again
under a new sequence. With the recovery marker stored the watcher
reconciles silently; with the commit and marker both lost the send
surfaces a distinct reply-tracking-degraded do-not-resend warning and
still exits 0. Nonzero remains only where nothing was delivered (or a
decision close needs its manual command). Regression: record durable +
both bookkeeping writes lost -> exit 0, one record, no duplicate.

* no-mistakes(review): Preserve inbox ordering with drain-all doorbells

* no-mistakes(review): Surface unwritable inbox ladder bookkeeping

* no-mistakes(review): Silence ladder failures after inbox acknowledgement

* no-mistakes(document): Update steering inbox documentation

* no-mistakes: apply CI fixes
* feat: add fast local lint mode

* fix: preserve complete fm-lint help

* fix: isolate fast lint mode

* no-mistakes(document): Clarify lint mode documentation ownership

* no-mistakes: apply CI fixes
…#2901)

* feat(bin): deliver remote secondmate steers through durable task inboxes

Stage 2 of the inbox+doorbell steer channel (stage 1: kunchenguid#2856). A remote
secondmate steer now crosses fm-on.sh as a durable record written
idempotently into the remote home's steering inbox plus a best-effort
remote doorbell, and the last typed-payload steer transport is deleted:

- fm-remote-secondmate-control.sh cmd_send writes the record via the new
  fm_task_inbox_write_idempotent and rings the doorbell; it no longer
  types the payload through an inner fm-send at an explicit pane target.
- fm-send.sh routes every remote text steer (harness-native included,
  which marking already reduced to chat) onto the remote inbox leg,
  retries the identical leg once on ssh 255, closes --resolve-key
  decisions at enqueue for remote too, and preserves a marked request's
  reply expectation when completion stays unknown. The exit-3-as-
  delivered remap, the 255 do-not-resend trap, and the remote typed
  submit block are removed.
- fm-task-inbox-lib.sh owns the idempotent enqueue: an exact-body re-run
  lands on the existing record, handled or not, so an ambiguous
  transport can always be safely re-run.
- Tests pin the new contract end to end (record + doorbell + no typed
  payload across ssh, one-record idempotence under an ambiguous
  transport, enqueue-time decision close, loud real failures, and the
  deleted typed-payload behaviors gone), and AGENTS.md plus
  docs/remote-secondmates.md describe the remote leg's new semantics.

* no-mistakes(review): Harden remote inbox delivery against lifecycle races

* no-mistakes(review): Enable correlation-preserving remote steer resends

* no-mistakes(review): Fail closed on stale correlation resends

* no-mistakes(review): Include home context in remote resend commands

* no-mistakes(review): Lock and revalidate remote parent routes

* no-mistakes(document): Clarify remote steer retry documentation

* no-mistakes: apply CI fixes
* wip: forked supervision on Pi (checkpoint before docs)

* fix(pi-branch): harden mirror delivery, fallback encoding, and session replacement

Peek-then-shift mirror flush so a failed append retries instead of dropping;
durable mirror cursor commits only after delivery into the branch;
the main fallback wake is operational-encoded like every watcher injection;
session_shutdown quiesces the generation and session_start re-arms, so /new
and /resume no longer kill the branch permanently. Registers the extension in
the strict typecheck, adds the dispatch handshake test, the branch extension
suite, the bash-level regression suite, the session-start replay test, and
the opt-in real-SDK live guard.

* test(fixtures): carry the branch-dispatch lib and lease lib into isolated fixtures

The watcher extension now imports lib/fm-branch-dispatch.ts and fm-teardown
sources fm-lease-lib.sh, so every fixture that copies or symlinks those
files in isolation gains the new sibling.

* no-mistakes(review): Prevent shutdown wake loss and serialize lease claims

* no-mistakes(review): Durably hand off wakes and retain portable leases

* no-mistakes(review): Require durable reports and clear disposed branch leases

* no-mistakes(review): Enforce per-wake outcomes and quiescent lease cleanup

* no-mistakes(review): Require wake acknowledgements and tighten branch lifecycle boundaries

* no-mistakes(review): Require complete acknowledgements and replay cleanup failures

* no-mistakes(review): Bind supervision to lock ownership and durable delivery

* no-mistakes(review): Activate branch lazily after session lock acquisition

* no-mistakes(review): Preserve undelivered mirror context across extension rebinds

* no-mistakes(review): Acknowledge startup replay only after main delivery

* no-mistakes(review): Isolate replay metadata from untrusted digest content

* no-mistakes(review): Reject duplicate reports for active wake sequences

* no-mistakes(review): Retain failed fallbacks and deduplicate outcome replay

* no-mistakes(review): Deduplicate durable outcomes and cache delivery receipts

* no-mistakes(review): Anchor wake sequence matching to outcome fields

* no-mistakes(document): Clarify Pi supervision durability contracts

* no-mistakes(lint): Fix ShellCheck issues in branch supervision scripts

* no-mistakes: apply CI fixes

* no-mistakes: apply CI fixes

* no-mistakes: apply CI fixes

* no-mistakes: apply CI fixes

* no-mistakes: apply CI fixes

* refactor(pi-branch): collapse to confused-agent-grade guards per captain decision

Captain decision A: the lease/actor guards target the CONFUSED-AGENT threat
model bin/fm-gate-refuse-lib.sh already documents; adversarial-grade
separation is impossible in the shared-process design and is filed as
separate follow-up work. Rip out the machinery that chased it: the
generation fence and shell-provenance markers, the wrapper-tagged ancestry
walks, guard auto-claim with per-script release traps, the pending-wake
files and ack-receipt correlation (the durable wake queue already
re-presents anything unacknowledged), the delivery-receipt store with
contiguous cursor advancement, the session-start replay-metadata channel,
and the branch tool quiescence counters.

Keep the behaviors the board requires, each on its simplest implementation:
lazy per-action session-lock ownership (cold start activates after the lock
lands; a secondary session stays inert), mirror durability across extension
rebinds via the durable cursor, replay-exactly-once from the one read
cursor, the awaited operational-encoded fallback, per-generation stray-lease
cleanup, session-lock-bound lease liveness (a recycled pid or a non-Pi home
never honors a leftover lease), the loud accidental-override guards
(readonly actor prelude, cross-actor claim refusal), and the role-partition
refinements (no forced teardown, no direct relaunch for the branch).
Default-on-for-Pi is unchanged.

* no-mistakes(review): Enforce lock ownership and serialize lease mutations

* no-mistakes(review): Synchronize guard cleanup and bind leases to lock owner

* no-mistakes(review): Report outcomes before acknowledging durable wakes

* no-mistakes(review): Restrict leases to Pi and instruct main claims

* no-mistakes(review): Reject malformed lease locks and torn outcome tails

* no-mistakes(review): Validate complete outcome tails before appending

* no-mistakes(review): Guard branch side effects across session replacements

* no-mistakes(document): Update Pi supervision durability and lease documentation

* no-mistakes(lint): Suppress intentional nested-shell expansion warning

* no-mistakes: apply CI fixes

* fix(pi-branch): authorize lease releases by caller

* fix(lint): break redundant source-analysis path in fm-lease-lib.sh

fm-lease-lib.sh's lazy fallback source of fm-wake-lib.sh gave ShellCheck's
--external-sources traversal a second path into an already 1540-line file
that fm-send.sh and fm-teardown.sh also source directly, blowing up the
recursive analysis past CI's lint timeout. Mark it a source=/dev/null
analysis boundary, matching the existing fm-task-inbox-lib.sh convention.

Also restores bin/fm-lint.sh and tests/fm-lint.test.sh to the shared
serial-lint definition (dropping an unrelated parallel-sharding change
that was itself hanging and masked this root cause).

* no-mistakes(document): Correct lease caller-authorization documentation

* no-mistakes: apply CI fixes

* no-mistakes: apply CI fixes

* no-mistakes: apply CI fixes
Takes the contiguous upstream first-parent range f170ced..7b88520 from
kunchenguid/firstmate as one merge, preserving upstream parentage.

Resolutions keep every active fork divergence: the agy unverifiable send
verdict, the run-progress wedge hold's fifth hold-count parameter, the
watcher restart hand-over, the GBrain and fleet-dashboard attachments,
the upstream-read-only posture, and repository-local validation evidence.
…e steer plane

Upstream's steering inbox makes the durable record, not a confirmed submit,
the delivery of an ordinary local steer, so the fork's scout completion-gate
reopen now fires at enqueue and is restored when the record cannot be written.
The typed plane keeps its existing send-failure and unconfirmed-submit
behaviour, and the suite now pins both planes.
… send verdicts to the typed plane

Upstream's durable steering inbox splits fm-send into two delivery planes, so
the agy unverifiable/pending distinction now describes the typed plane alone.
The scout completion-gate reopen was fork-local and unrecorded since
2026-08-12; this round is where it first collided, so it gets its own entry.
…harness

A doorbell is ordinary text, so it takes the same submit dispatch as any steer.
Without the target's harness, cursor and agy lost Herdr's atomic agent prompt
and fell back to typed composer text, which this fleet does not trust for them.
Every ring caller now resolves the harness from the target's metadata.

The agy delivery-verdict cases move to the typed plane they actually test, and
a new case pins that an ordinary agy steer is delivered by its durable record
while its doorbell still reaches the pane through the atomic prompt.

The brief goldens are regenerated for upstream's new instruction-inbox section.
…move it simulates crashing before

The stub killed the handoff and then exec'd the real mv, leaving an orphan that
landed the move about a second later. Every later assertion that the item was
still in the source backlog then depended on outrunning that orphan, which is
why the case passed locally and failed CI. The stub now exits without moving,
which is the scenario it exists to build.

Reproduced by widening the window with a three-second wait after the simulated
crash: the old stub fails the case there, the new one passes.
…026-08-24-round-3

# Conflicts:
#	docs/configuration.md
@HelloWorldSungin
HelloWorldSungin merged commit bb8a66b into main Aug 25, 2026
16 of 17 checks passed
@HelloWorldSungin
HelloWorldSungin deleted the fm/fm-upstream-sync-2026-08-24-round-3 branch August 25, 2026 23:09
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.

3 participants