feat: adopt upstream multi-brain supervision and fleet upgrades - #11
Merged
Conversation
* fix(no-mistakes): drop full-suite local Test override Local no-mistakes Test is intent-targeted; CI Behavior keeps the broad tests/*.test.sh suite. Keep commands.lint on bin/fm-lint.sh and add a focused contract test so the override cannot silently return. * no-mistakes(lint): Make CI contract assertion ShellCheck-clean
* feat(test): add canonical timed suite runner and honest CI timeout Introduce bin/fm-test-run.sh as the single serial owner for selecting one script, a family, a conservative changed-file set, or the explicit complete suite, with per-script timing markers and a JSON artifact. Wire CI Behavior through the runner, raise the hang-tripwire timeout to 25 minutes, and document entry points without restoring a full-suite local no-mistakes Test command. * no-mistakes(review): Captain: fix changed selection and empty summaries * no-mistakes(review): Captain: fail closed on unmapped changed sources * no-mistakes(document): Document canonical timed test entry points
* fix: disclose main-home orphan and unstructured inventory gaps Main Bearings could report an empty fleet while structured in-flight rows lacked meta or current backlog rows were free-form. Emit main_inventory from the fleet snapshot, map it into Bearings omitted surfaces and a Charted Next gate, and keep meta as the only live Underway source. * no-mistakes(document): Document Bearings inventory-integrity projection * no-mistakes: apply CI fixes
* feat: add concurrent test isolation proof for Phase 2 Prove an audited portable candidate set passes under concurrent workers with private mode-0700 temp roots, without enabling production CI sharding or fm-test-run --jobs. * no-mistakes(review): Pin isolation proof to audited candidate manifest
* feat(secondmate): parent-owned guards for missed status reports Marked parent-to-secondmate requests now create a durable pending-reply expectation with a privacy-safe correlation id before delivery. Transport success never resolves it; only a correlated parent status or document pointer does. After a completed turn with no report, the parent sends one recovery repost and escalates once if that turn is also missed, without scraping the secondmate conversation or looping. * no-mistakes(review): Deduplicate wrong-home pending-reply sightings * no-mistakes(review): Harden pending-reply recovery and escalation guards * no-mistakes(review): Bound pending-reply backend polling * no-mistakes(review): Cache pending-reply status scans * no-mistakes(review): Protect undelivered pending-reply records from scans * no-mistakes(review): Close pending-reply delivery durability gaps * no-mistakes(review): Separate pending-reply transport outcomes * no-mistakes(review): Escalate stalled pending-reply deliveries once * no-mistakes(review): Resolve attempted deliveries from correlated reports * no-mistakes(review): Resolve late reports after delivery escalation * no-mistakes(document): Document pending-reply grace and ownership * no-mistakes(lint): Silence intentional pending-reply test fixture lint warnings
* feat: add required pinned Herdr CI lane Install exact Herdr 0.7.4 and Treehouse 2.0.1 with official assets and SHA-256 pins, run the real-herdr-gated family serially through fm-test-run with hard-fail on herdr-not-found, and keep portable Behavior free of claimed Herdr coverage. * no-mistakes(document): Consolidate real-Herdr CI documentation ownership * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes
…guid#841) * feat: shard portable CI tests after isolation proof Balance the Phase 2 proven-isolated set into two LPT portable parallel lanes from Phase 1 timing evidence, keep stateful work in a required portable serial lane, exclude real Herdr to its dedicated required lane, and prove complete inventory coverage with a deterministic guard. Add bounded local --jobs only for the proven set, per-lane timing plus aggregate artifacts, and reduce the interim portable hang tripwire now that the serial remainder owns the long wall-clock path. * no-mistakes(review): Captain, fix CI contracts and completion-order worker scheduling * no-mistakes(review): Captain, preserve stderr gate-skip detection in parallel tests * no-mistakes(document): Document portable sharding and timing aggregation * no-mistakes: apply CI fixes
) * feat: fence primary-session delegation outside the fleet A firstmate primary that delegates through Claude Code's built-in delegation tools creates work with no state/<id>.meta. Because fm-supervision-lib.sh counts *.meta and fm-turnend-guard.sh exits silently at zero, such work does not merely go unsupervised: it makes the whole guard stack structurally inert, and it dies with the primary session. On 2026-07-22 that cost two workers mid-flight and left supervision down for 73 minutes unnoticed. Layer 1, the primary fix: a permissions.deny list in .claude/settings.json removes the 18 delegation, scheduling, worktree, and task-tracking tools from the model's schema, so they are never offered. This is removal rather than interception, so there is no call to intercept and no fail-open path. The list is flat and in one file so its width stays reviewable; the captain owns that width. Layer 2, bin/fm-subagent-pretool-check.sh: a deny list is fail-open against tools that do not exist yet, and permissions.allow is a pre-approval list rather than an availability list, so there is no fail-closed allowlist to use instead. This backstop classifies the tool NAME by shape rather than against a fixed list, so a delegation tool that ships before the deny list is updated is still refused. It excludes mcp__* names and observe-or-stop operations, scopes itself to a genuine primary home via the shared fm_primary_scope_matches predicate so a crewmate's task worktree is unaffected, and offers one deliberate FM_ALLOW_SUBAGENT=1 escape hatch that must be set at launch. Verified live against Claude Code 2.1.217, including a deny-key A/B with a nonsense-name control, layer 2 denying an un-denied Workflow call, the same call allowed in a linked worktree, and the escape hatch. Corrects a prior finding: both Task and Agent work as deny keys, so both are pinned. Codex 0.144.1 verified to expose no delegation tool; grok, opencode, and pi are inspected and documented as not wired because those binaries are absent from this host and the repo requires live validation before trusting a harness hook. Evidence in docs/subagent-guard.md. * no-mistakes(review): Ship scoped Claude delegation guard * no-mistakes(test): Ship Claude delegation deny list * no-mistakes(document): Clarify PreToolUse guard ownership * no-mistakes(lint): Keep Claude deny list local
Reproduction: portable-parallel-2 completed successfully without tasks-axi while fm-decision-hold-lifecycle emitted a gate skip in 30 ms. The pre-shard lane installed tasks-axi and exercised the test fully. Installing tasks-axi is the smallest counterfactual and makes the representative shard execute the test with gate_skip=false in about 20 seconds. Both parallel jobs receive symmetric setup, while the exact 91-test inventory and coverage guard remain unchanged.
* feat: make dispatch profiles quota aware * no-mistakes(review): Fix quota window and Grok product scoping * no-mistakes(document): Document implicit quota-aware dispatch accurately
…guid#875) * fix: preserve mixed Bearings projections * no-mistakes(review): Enforce strict invalidity precedence for partial snapshots * no-mistakes(review): Enforce ownership for unknown child metadata * no-mistakes(document): Document partial structured Bearings projections * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes
* Add session-local Pi calm mode * no-mistakes(review): Preserve Pi HTML exports during calm mode * no-mistakes(review): Preserve calm exports across submit bindings and share * no-mistakes(document): Document calm-mode feasibility across supported harnesses
* fix(pi): limit watcher arm tool to recovery * no-mistakes(review): Strengthen Pi live re-arm regression coverage * no-mistakes(document): Document Pi first-cycle and recovery-only watcher arming
* fix(pi): clean up Calm transcript rendering * no-mistakes(review): Captain, preserve Calm exports and classify Pi launch briefs * no-mistakes(review): Captain, eliminate Calm gaps and verify exported conversations * no-mistakes(review): Restore Calm rows received while active * no-mistakes(review): Preserve diagnostics during Calm restoration * no-mistakes(document): Clarify Calm transcript behavior and injection paths
* fix: execute every PR body compliance event * no-mistakes(document): Document independent PR compliance events
…#899) * fix: distinguish operational input in ahoy * no-mistakes(review): Handle legacy Ahoy operational boundaries * no-mistakes(review): Narrow legacy Ahoy boundaries with live regressions * no-mistakes(document): Document Ahoy operational marker ownership * no-mistakes(lint): Suppress intentional literal fixture lint warnings
…nguid#909) * fix: type canonical operational inputs * no-mistakes(document): Correct canonical operational-input documentation ownership
* fix: avoid generic secondmate acknowledgements * no-mistakes(document): Document sparse secondmate acknowledgement behavior * no-mistakes: apply CI fixes
* fix(pi): preserve calm presentation across sessions * no-mistakes(review): Fix Calm home fallback persistence * no-mistakes(document): Clarify Calm gapless and export contracts * no-mistakes: apply CI fixes
…nguid#932) * fix: retire merged PR polls after notification * no-mistakes(review): Decouple PR retirement recovery from template updates * no-mistakes(review): Recover pending PR retirements before poll migration * no-mistakes(document): Document merged PR poll retirement contracts
* Clarify intake evidence and overlap handling * no-mistakes(review): Align scout guard with intake classification * no-mistakes(document): Clarify scout documentation and intake ownership
* fix(pi): preserve operational follow-up semantics in Calm * no-mistakes(document): Correct Calm operational-row visibility documentation
* perf(lint): shrink shell source graph * no-mistakes(review): Ensure lint workers terminate fully on cancellation * no-mistakes(document): Repair stale lint documentation ownership
* fix(pi): remove Calm hidden-block gaps * no-mistakes(review): Validate Calm geometry against current viewport * no-mistakes(review): Synchronize Calm geometry checks with reload completion
* fix: escalate ask-user contract expansion * no-mistakes(document): Point project management to authority owner
* fix(pi): hide Calm operational user rows * no-mistakes(review): Narrow Calm operational input suppression * no-mistakes(review): Avoid Calm replay classifier subprocesses * no-mistakes(document): docs: point Pi verification to Calm owner
* fix(session-start): relaunch missing second mates * fix(test): detect completed parallel workers * no-mistakes(review): Isolate session-start recovery test cleanup * no-mistakes(review): Complete backend-safe secondmate session recovery * no-mistakes(review): Resolve Zellij task ownership before recovery * no-mistakes(review): Recover relocated Zellij ghost tabs safely * no-mistakes(review): Restore conservative Zellij recovery boundary * no-mistakes(review): Reject malformed tmux recovery targets * no-mistakes(document): Align secondmate recovery documentation * no-mistakes: apply CI fixes
…id#967) * fix(herdr): reclaim resumed task projections safely * no-mistakes(review): Enforce safe Herdr reclaim close boundaries * no-mistakes(document): docs: clarify Herdr restart projection contract
…#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
* feat(bin): parallelize session-start remote secondmate network sweeps Run per-secondmate liveness and convergence probes concurrently and overlap clone refresh, while replaying each mate's fail-closed diagnostic in original order. Ignore scratchpad* so untracked scratch no longer blocks remote sync. Co-authored-by: Cursor <cursoragent@cursor.com> * no-mistakes(document): Document parallel startup network sweeps * no-mistakes(lint): Fix empty environment assignment lint warning * no-mistakes: apply CI fixes --------- Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(tests): count declared-pause wakes without crashing on an absent queue The exited-declared-pause case counts queued stale wakes by handing state/.wake-queue straight to awk. A watcher that queues nothing never creates that file, and awk aborts on a missing path before its END rule runs, so the count collapses to the empty string. The next comparison then fails as an integer-expression error and surfaces as a wake flood with no number, hiding the real contract breach the following grep names. Read the queue the way the drain-count assertion at the end of this file already does: silence awk's open error and default an absent queue to zero. Applied to all four counts in this case, including the live external-decision gate pair whose queue an acknowledged drain can also leave behind. An absent queue now reports "did not use the bounded paused recheck", while a genuine flood still fails with its real count. Fixes kunchenguid#2628 * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes
… icon (kunchenguid#2934) * style(pi): restyle supervision merge notes with a sailboat and matching pad Secondary-session notes were flush against the TUI edge and fully tinted. Use the sailboat prefix, Pi's default outputPad, boat-only color, and dim remainder so they sit like real messages. * style(pi): distinguish routine and captain merge notes by icon only Visible notes now lead with a sailboat or anchor, then only the dim outcome. Drop the branch-merged wording and verdict brackets so the icon is the only kind signal.
…id#2938) The markdown contract stays the owner; the still is only the visual of the idea.
* fix(bin): bound remote worker supervisors * no-mistakes(review): release incumbent supervisor before starting its replacement * no-mistakes(review): wait out a healthy same-root supervisor instead of replacing it * no-mistakes(review): narrow remote worker change to restart accounting only * no-mistakes(document): clarify supervisor restart guard is a lifetime total
…sion Merge kunchenguid/firstmate:main at 038d0f7 into rega10/firstmate:main at bae3180, preserving both histories. Conflicts were resolved against the fork's last reconciled upstream tip (f74d9e4, merged by fork PR #9) so only genuine fork-vs-upstream divergence needed manual resolution; every fork-only behavior (Claude session sanitize, hosted Codex lock owners, landed-work library, Herdr legacy repair, install/contract tests) is retained alongside upstream's persistent Pi supervision branch.
Pi 0.83.0 types TerminalInputHandler as returning {consume?, data?} | undefined,
so the strict no-emit typecheck in tests/fm-pi-primary-types.test.sh rejected the
void-returning submit interceptor. Returning undefined explicitly leaves the
keystroke untouched on every Pi version and restores the typecheck.
Refresh the Pi supervision branch record with the dated real-SDK guard and strict typecheck results against the installed Pi 0.83.0, as the record's own refresh contract requires after a Pi upgrade.
Stock macOS Bash 3.2 does not decode $'\uXXXX' escapes, so the Herdr half-block rule fixture compared literal backslash sequences and failed on every macOS run. Spell the glyphs directly, the way bin/fm-composer-lib.sh already does.
Bash 3.2 aborts on "${arr[@]}" for an empty array under set -u, so every
fm-public-followup.sh invocation died at its registry-lock helpers on macOS
(tests/fm-backlog-handoff.test.sh and tests/fm-public-followup.test.sh both
failed on unmodified upstream). Use the repo's existing ${arr[@]+"${arr[@]}"}
idiom. The rechain test also relied on an inline-quoted pattern substitution
that Bash 3.2 splits at its first slash; substitute through variables instead.
Upstream renamed the CI lint job from "Lint shell scripts" to "Lint" when it grew workflow linting; the fork-only contract test still asserted the old name. Accept either spelling so the assertion keeps guarding the job's presence rather than its label.
Bash 3.2 aborts on "${PROJECT_NAMES[@]}" when the array is empty under set -u,
so every --no-projects remote seed died before provisioning on macOS (both
remote secondmate suites failed on unmodified upstream). Use the repo's
existing ${arr[@]+"${arr[@]}"} idiom.
Under set -e, Bash 3.2 treats a failed `.` of a missing file as fatal even inside `||`, so fm_backend_source could never return 1 and a caller such as teardown's Herdr preflight died silently instead of refusing with its "nothing was changed" message (tests/fm-teardown.test.sh failed on unmodified upstream). Check readability before sourcing each adapter.
This reverts commit 097973e. The reverted commit was a temporary compatibility relaxation: it let the required "PR must be raised via no-mistakes" check accept the legacy pre-1.46.0 PR-body format because the shared no-mistakes gate was still v1.45.4. The captain approved restoring upstream's stronger structured attestation requirement (upstream kunchenguid#2710) once the shared gate was upgraded; it now runs v1.57.0, which emits the required attestation, so the fork's workflow returns to the exact upstream check and the interim verifier script, its test, and its runner family entry are removed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Bring the rega10 Firstmate fork fully up to the current kunchenguid/firstmate:main, preserving every intentional fork behavior, and ship the reconciliation through a reviewed fork PR. The captain explicitly requested the latest upstream Firstmate because upstream now includes Pi's persistent multi-brain supervision architecture (upstream commits 7b88520 persistent Pi supervision branch, d55e00a multi-brain architecture poster, 8fa0505 default-on branch supervision and heartbeat routing, plus later fixes through the exact live upstream tip) and wants to use it.
Required reconciliation, all accepted as stated: (1) fetch live fork and upstream refs and verify exact identities before branching (fork origin/main was bae3180, upstream/main was 038d0f7; fork diverged, 17 ahead / 231 behind, not fast-forwardable); (2) create a normal feature branch from fork origin/main and perform a history-preserving merge of exact upstream/main - never rebase, squash, reset, force-push, replace fork history, or write upstream; (3) resolve every semantic conflict deliberately, preserving intentional fork-specific safety, private-home behavior, dispatch preferences, runtime support, Herdr repairs (fork PRs #4, #6, #10), Claude worker transcript preservation (PR #8), spawn metadata-publication abort (PR #5), hosted Codex opaque lock owners (PR #1/#3/#9), and later fork fixes unless upstream supplies a demonstrably equivalent or stronger implementation; never mechanically choose ours/theirs for broad files; inventory every conflict and document each resolution; (4) adopt the complete upstream Pi multi-brain implementation and its later fixes as a coherent unit - branch extension, watcher dispatch, leases, outcome store, recovery, prompts, docs, tests, default-on heartbeat behavior - never cherry-pick only headline commits or create a competing implementation; (5) preserve the feature's upstream contract: default-on only for a Pi primary owning the fleet session; ordinary resolvable supervision and eligible heartbeat work moves to the persistent in-process branch; genuinely quiet heartbeat outcomes are silent; ordinary non-silent outcomes append a compact sailboat note; captain-relevant outcomes open exactly one main-session turn; fleet-wide/unresolvable events and watcher failures stay on main; away mode declines branch offers; broken/absent branch falls back to main without losing notifications; branch authority cannot merge, land local work, or freshly spawn; current captain approval and security boundaries remain unchanged; (6) verify whether any fork-only behavior conflicts with the upstream branch lease/role partition and adapt the smallest authoritative owner rather than bypassing new guards; (7) keep the diff to one reconciliation merge plus only narrowly necessary follow-up fixes for proven semantic conflicts or validation failures; do not opportunistically redesign unrelated systems.
Decisions and tradeoffs made while doing the work: fork PR #9 was squash-merged on GitHub (single-parent 3015811) although its branch had merged upstream f74d9e4, so git's raw merge base was the ancient 59ece45 and produced ~200 spurious add/add conflicts; a temporary LOCAL
git replace --graftrestoring that true parent was used only for merge-base computation, then deleted, and the resulting commit 17cd039 is an ordinary two-parent merge of bae3180 and 038d0f7 (both verified as ancestors). Ten real conflicts were resolved: harness-adapters skill (both fork sentences plus upstream's Zellij wording), ci.yml and docs/fm-test-portable-shards.md (upstream's measured timeout wording, same 20-minute value the fork raised in PR #9), bin/fm-session-start.sh (fork's opaque-owner completion read via fm_session_lock_owner_read retained plus upstream's COMPLETION_RECORDED flag), bin/fm-test-run.sh (both family entries; upstream's refreshed weight-hint table plus the fork's two fm-herdr-legacy-repair hints - hints are balance-only), tests/fm-gotmp.test.sh (all symlinks from both sides), tests/fm-pi-watch-extension.test.sh (upstream's later flake rework taken for every hunk because it supersedes the fork's PR #9 fix for the same flake; upstream's call list verbatim; the fork-original Node 22+ skip guard re-inserted before the Pi test calls; the fork's 500->1000 wait bound kept), tests/fm-session-start.test.sh and tests/fm-sessionstart-nudge.test.sh (conflicts split mid-function; regions rebuilt from both sides' complete functions so all tests survive), tests/fm-spawn-dispatch-profile.test.sh (the launch string now composes upstream's outerenv -u CURSOR_AGENT -u CURSOR_INVOKED_ASwith the fork'senv -u CLAUDE_CODE_CHILD_SESSION -u CLAUDE_CODE_SESSION_ID -u CLAUDE_PID -u CLAUDE_JOB_DIRtemplate, both intentional; expectations updated to the composed string). Every line the fork added over f74d9e4 was verified present in the merged tree except those deliberate resolutions. The fork's non-numeric Codex lock owners make fm_lease_live report not-live so lease guards are silent no-ops on non-Pi homes, exactly as upstream intends; no fork behavior needed adaptation.Narrow follow-up commits, each justified by a proven validation failure: 9d21048 returns undefined explicitly from .pi/extensions/fm-calm.ts's terminal-input handler because installed Pi 0.83.0 types TerminalInputHandler as returning {consume?, data?} | undefined and the strict typecheck failed on the unmodified upstream file (upstream's evidence was against 0.80.10); f368599 records the dated Pi 0.83.0 real-SDK live guard and typecheck results in docs/verification/runtime-backends.md as that record's own refresh-after-upgrade contract requires; 2a2f3ed spells half-block glyphs literally in tests/fm-composer-lib.test.sh because stock macOS Bash 3.2 does not decode$'\uXXXX'; 9a3649e fixes bin/fm-public-followup.sh expanding empty arrays under set -u (fatal on Bash 3.2, so every Relay follow-up command died on macOS; reproduced on pristine upstream) using the repo's existing $ {arr[@]+"${arr[@]}"} idiom, and fixes the rechain test's inline-quoted pattern substitution that Bash 3.2 splits at its first slash; 5fce5ef lets the fork-only CI contract test accept upstream's renamed ${BASHPID:-$ $}, so these are in-scope compatibility fixes, not redesign.
Lintjob (wasLint shell scripts); 3445ba5 applies the same empty-array idiom to bin/fm-remote-home-seed.sh so --no-projects remote seeding works on Bash 3.2 (both remote-secondmate suites failed on pristine upstream); 5e4f0cc adds a readability check before each adapter.in bin/fm-backend.sh's fm_backend_source because under set -e Bash 3.2 treats a failed.as fatal even inside||, so a missing adapter killed teardown's Herdr preflight silently instead of refusing with its "nothing was changed" message (tests/fm-teardown.test.sh failed on pristine upstream). Stock macOS Bash 3.2 is a real runtime for the captain's fleet (#!/usr/bin/env bash resolves to /bin/bash here) and production scripts already guard BASHPID withValidation run on macOS 26.5.2 arm64 with /bin/bash 3.2.57, Node 24.19.0, Pi 0.83.0, pinned ShellCheck 0.11.0 and actionlint 1.7.12 (scratch installs, nothing global): bin/fm-lint.sh green, bin/fm-doc-audience-check.sh ok, coverage guard ok (167 tests), strict Pi typecheck ok against 0.83.0, tests/fm-pi-branch-extension.test.sh, tests/fm-branch-supervision.test.sh, tests/fm-pi-watch-extension.test.sh (36/36), and FM_PI_BRANCH_LIVE_E2E=1 real-SDK guard all green, proven-isolated lane 24/24, real-Herdr family 13/13, upstream's macos-stock-bash job steps replicated (207-file parse sweep, 15/15 and 42/42 snapshot suites). Known and deliberately unfixed: five tests still fail on this macOS/Bash 3.2 machine and fail identically on a pristine upstream/main export, none merge-caused - tests/fm-wake-queue.test.sh (new upstream self-held-lock reclaim case needs $BASHPID to distinguish a subshell; a lock-primitive change upstream should own), tests/fm-pending-reply.test.sh (test-only bare $BASHPID at line 692; production lib guards it), tests/fm-muse-harness.test.sh (macOS kills the fixture's unsigned copied bash; also fails on fork main), tests/fm-on.test.sh (upstream remote-root fixture omits fm-composer-lib.sh which the herdr adapter sources; also fails on fork main), tests/fm-bootstrap-network-parallel.test.sh (fake-ssh timing assertion failing 3/3 on both HEAD and pristine upstream here). Linux CI is the proof for those; no Bash >= 4 exists on this machine.
Constraints: never modify anything under projects/; never update, stop, or restart the shared no-mistakes service; do not change private data/, state/, or config/ except the generated task status protocol; never push or open anything against kunchenguid/firstmate - the delivery target is a new PR in rega10/firstmate:main only; never merge the fork PR; do not fold upstream PR kunchenguid#282 unless already in the upstream tip (it is not); do not touch other workers' branches. The private report with refs, ancestry, conflict inventory, fork preservation, activation requirements, validation and skip evidence, and the post-merge /updatefirstmate plus Pi restart procedure is at data/firstmate-upstream-multibrain-sync/report.md (private, not part of the PR).
Later accepted decisions, carried in their current form: the first pipeline run (01M0V9GQW0WQG4NFZ2VB6R5J0M) reached checks-passed as PR #11 with two accepted review-fix commits (84de0a2 semantic lint-job assertion plus Node-guard scoping so the OpenCode watcher tests keep running on old Node plus reconciled Pi version provenance; b925d75 requiring a parsed workflow step whose command token is bin/fm-lint.sh). Its CI auto-fix commit 097973e temporarily relaxed the fork's required "PR must be raised via no-mistakes" check to also accept the legacy pre-1.46.0 PR-body format because the shared gate was v1.45.4. The captain explicitly decided (Option B) to restore upstream's stronger structured-attestation requirement: after the shared no-mistakes was upgraded to v1.57.0, commit 9b724ab reverts exactly 097973e, returning .github/workflows/no-mistakes-required.yml to its byte-identical upstream-adopted form and removing the interim bin/fm-no-mistakes-pr-body-verify.sh, its test, and its runner family entry, while preserving every other commit. The revert of those interim files is deliberate and captain-approved, not a lost fix. The PR must remain unmerged; the same branch and PR are being revalidated.
What Changed
Risk Assessment
Testing
The supplied baseline had already covered lint, documentation, coverage, Pi 0.83 typechecking/live guards, portable and Herdr lanes, and macOS Bash 3.2 checks; this phase reran the focused workflow contract and core multi-brain suites, reproduced the shell-bypass regression against the predecessor, verified all inheritance-level bypasses are now rejected, and captured persisted startup-replay evidence. All targeted checks passed; no UI screenshot was applicable because the change exposes shell/CLI and workflow behavior rather than a rendered interface.
Evidence: Workflow shell-override regression matrix
Source: Workflow shell-override regression matrix
Evidence: Persistent Pi supervision startup replay
Source: Persistent Pi supervision startup replay
Evidence: Persisted supervision outcome store
Source: Persisted supervision outcome store
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 1 issue found → auto-fixed (6) ✅
tests/fm-nm-test-contract.test.sh:125- The changed contract test still violates the test-quality rule: the serial-step check acceptsecho bin/fm-test-run.sh --lane "$FM_SERIAL_LANE"because it searches any token subsequence, while lines 24, 90, 100-103, and 129-140 rely on raw YAML text and can pass on comments or unrelated content. Parse both YAML files semantically, require direct executable commands after optional environment assignments, assert companion-job behavior through normalized jobs/steps, and remove the commented-text assertion.🔧 Fix: Make workflow contract assertions semantic
1 error still open:
tests/fm-nm-test-contract.test.sh:75- The semantic lint assertion remains bypassable because an empty expected argument list makes every invocation beginning withbin/fm-lint.shmatch. For example, changing the CI step tobin/fm-lint.sh --list-fileswould pass this test but exit before running ShellCheck or actionlint. Require the lint workflow command to have exactly the executable and no arguments.🔧 Fix: Require exact lint workflow command
1 error still open:
tests/fm-nm-test-contract.test.sh:75- The semantic workflow check remains bypassable becausedirect_commandaccepts any trailing tokens. For example,bin/fm-test-run.sh --lane portable-parallel-1 --listsatisfies this assertion, butfm-test-run.shthen prints the inventory and exits 0 without running tests; the serial and Herdr assertions have the same bypass. Validate each command's complete permitted argument schema, rejecting--list, exclusion flags, shell operators, and other behavior-changing suffixes.🔧 Fix: Enforce exact workflow command schemas
1 error still open:
tests/fm-nm-test-contract.test.sh:111- The exact-token check still accepts dead commands. For example, a step containingexit 0followed bybin/fm-lint.shsatisfiesinclude?, succeeds, and never runs lint; every lane assertion has the same flaw. Validate each owning step’s complete permitted command sequence, including only its real prologue and exact invocation, and reject early terminators or unexpected commands.🔧 Fix: Enforce exhaustive workflow step sequences
1 error still open:
tests/fm-nm-test-contract.test.sh:56- The claimed exhaustive workflow-step check still ignores execution metadata. For example, addingif: ${{ false }}orcontinue-on-error: trueto the exactrun: bin/fm-lint.shstep leaves its normalized sequence unchanged, so this test passes while lint is skipped or cannot fail CI. This is another expansion of the same bespoke parser after four fix rounds: decide whether to extend the owning job/step model to require an enabled condition and failure propagation; that is the smallest correction and is recommended.🔧 Fix: Enforce lint execution metadata
1 error still open:
tests/fm-nm-test-contract.test.sh:165- The lint predicate still ignores the effective shell. A step with the exactrun: bin/fm-lint.shplusshell: true {0}passes this contract, but GitHub executestrueand exits successfully without running lint; job- or workflow-leveldefaults.run.shellhas the same path. Reject shell overrides for the required lint step, including inherited defaults, and cover the no-op-shell case.🔧 Fix: Reject lint shell overrides
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
/bin/bash tests/fm-nm-test-contract.test.shTemporarygit archivefixture matrix comparing450a1c4and4bf373cafter step-, job-, and workflow-levelshell: true {0}mutations; the predecessor accepted every bypass while the fix rejected all three/bin/bash tests/fm-branch-supervision.test.sh/bin/bash tests/fm-pi-branch-extension.test.shThreebin/fm-branch-outcome.sh appendcalls followed bystartup-replayandunread, demonstrating durable quiet/routine/captain outcomes, silent quiet replay, visible routine/captain replay, and cursor advancement✅ **Document** - passed
✅ No issues found.
🔧 Fix: Suppress intentional single-quoted Ruby ShellCheck warning
1 warning still open:
✅ **Push** - passed
✅ No issues found.