Merge upstream round 1 through 88fb3c0a - #269
Merged
Merged
Conversation
…o dedicated scripts (kunchenguid#3221) * Add quota exhaustion detection and safe fallback helpers - bin/fm-procevent-quota.sh: generic procevent adapter that arms a recurring quota-axi --json poll and wakes firstmate when a tracked provider's effectivePercentRemaining drops below a threshold or its runway.status becomes exhausted_now. - bin/fm-quota-choose.sh: worker-side helper that picks the first ranked harness:model candidate with positive effectivePercentRemaining. - AGENTS.md and .agents/skills/quota-array-dispatch/SKILL.md: document the new helpers and the mid-task quota-exhaustion wake path. - tests/fm-quota-choose.test.sh: unit tests with a mocked quota-axi JSON source. * no-mistakes(review): Fix quota polling and scope bounds * no-mistakes(review): Enforce safe default quota selection * no-mistakes(review): Handle decimal quota values safely * no-mistakes(review): Fail closed on invalid quota inputs * no-mistakes(review): Reject empty quota candidate segments * no-mistakes(review): Harden quota parsing and timeout ownership * no-mistakes(review): Reuse captured quota snapshots consistently * no-mistakes(review): Match quota using explicit candidate providers * no-mistakes(review): Centralize fail-closed quota schema validation * no-mistakes(review): Reject out-of-range quota percentages * no-mistakes(review): Validate quota runway status enum * no-mistakes(review): Tighten quota scope and status contracts * no-mistakes(review): Preserve unknown quota and exact product bounds * no-mistakes(review): Preserve provider-level unknown quota * no-mistakes(review): Reuse canonical verified harness validation * no-mistakes(document): Document mid-task quota handling * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes * fix(docs): restore default routing contract, keep quota helper optional Restore the AGENTS.md section 4 always-loaded routing paragraph the PR had deleted, so the standing TOON-first intake, spendPriority ranker, every-candidate accounting, and load-trigger contract stay exactly as before this PR. The mid-task quota wake is optional and must not alter default routing. Restore the quota-array-dispatch skill ownership line to section 4 as the always-loaded intake boundary owner; keep the worker-side helper section as an addition only, without rewiring ownership or load triggers to section 13. * fix(bin): use harness-keyed quota matching in optional helper Revert fm-quota-choose.sh from harness:provider:model tuples back to harness:model candidates with harness-keyed provider matching, per the resolved ask-user finding. The helper is optional; authoritative multi-provider routing (provider discovery from the harness catalog and quota matching by that explicit provider) stays owned by AGENTS.md section 4 and the quota-array-dispatch skill intake procedure, not the helper. Document the multi-provider limitation in the helper header and the quota-array-dispatch skill: the helper maps each harness to one primary provider family only, so a candidate whose established provider differs from that primary family is checked against the wrong quota row. Use it only when the brief fixed the candidate order and every candidate's provider is the harness's primary family. The helper still consumes one already-captured default-TOON or JSON snapshot via stdin or --snapshot and never calls quota-axi itself, so it selects from the same quota state as the intake. * no-mistakes(review): Fix Muse quota mapping and helper contract docs * no-mistakes(review): Reject known-empty quotas and map quota tests explicitly * no-mistakes(review): Preserve unmeasured candidates and enforce snapshot reuse * no-mistakes(review): Fix quota retirement and dependent regression coverage * no-mistakes(review): Accept zero-row quota TOON snapshots * no-mistakes(review): Enforce quota semantics status consistency * no-mistakes(review): Veto dispatch on any exhausted applicable scope * no-mistakes(review): Record exhausted quota scope in wake details * no-mistakes(review): Fix quota help and control dependency coverage * no-mistakes(review): Decode quoted TOON fields and document quota wakes * no-mistakes(review): Validate zero-row TOON and map timeout coverage * no-mistakes(review): Reject multi-value JSON and malformed TOON envelopes * no-mistakes(review): Validate complete nonzero TOON envelopes * no-mistakes(review): Accept producer-shaped quota TOON envelopes * no-mistakes(review): Support empty quota arrays and validate counted rows * no-mistakes(review): Harden TOON completion, scopes, and quoted fields * no-mistakes(review): Preserve unknown-headroom exhaustion and reject trailing fields * no-mistakes(review): Allow unknown headroom under known semantics * no-mistakes(review): Reject noncanonical quota identities * no-mistakes(review): Preserve empty quota polling and validate attention identities * no-mistakes(review): Reject noncanonical provider watches * no-mistakes(review): Validate all candidates before quota selection * no-mistakes(document): Correct quota helper safety documentation * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes
* fix(bin): keep typed Lavish comments when an element is also annotated read preferred element text over prompt, so an annotate-and-comment item dropped the captain's words. Surface prompt as its own field. Co-authored-by: Cursor <cursoragent@cursor.com> * no-mistakes(review): Filter non-comment prompts from Lavish reader output * no-mistakes(document): Clarify Lavish comment presentation contract * no-mistakes(ci): Fixed Lavish reader comment provenance: non-choice prompts are now emitted even when identical to element text. Added observable regression coverage for identical selector+comment input while retaining pure annotation/message coverage. Reader cases, bash syntax, and diff checks pass. Full fm-procevent suite stops earlier at unrelated “reconcile never claimed” setup failure * no-mistakes(ci): Fixed duplicate pure-annotation prompts by emitting `prompt:` only when it differs from captured element text. Updated behavioral coverage for selector+comment, pure annotation, and pure message cases. Focused reader regressions, syntax checks, and diff checks pass. Full suite remains blocked by the pre-existing “reconcile never claimed the registered source” failure * fix(bin): always emit Lavish comments and use real annotation fixtures Stop inferring comment provenance from prompt==text. Real pure annotations have no prompt, so always-emit does not duplicate. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
…uid#3420) * Fix public-followup register crashing on empty lock arrays under bash 3.2. bash 3.2 with set -u treats "${arr[@]}" on an empty array as unbound, so the first register in a fresh home aborted before taking the registry lock. The empty-lock regression also runs under the existing stock macOS Bash CI lane so pre-fix code would fail there. * no-mistakes(document): Document stock Bash registration coverage * no-mistakes(ci): Pinned the stock macOS Bash CI lane to tasks-axi@0.2.5, eliminating dependency drift. Verified workflow YAML parsing, git diff checks, and the focused regression under /bin/bash 3.2.57 with tasks-axi 0.2.5 * no-mistakes(ci): Fixed the flaky portable CI test: it treated exited zombie processes as live because `kill -0` succeeds for zombies. The watcher and descendant assertions now check process state and regard zombies as exited. Verified `tests/fm-pr-check-security.test.sh`, ShellCheck, `git diff --check`, and the focused Bash public-followup regression
* fix(herdr): isolate server launch environment * no-mistakes(review): Clear inherited supervision model from Herdr launches * no-mistakes(document): Document Herdr server launch environment isolation
* fix: surface inbound Relay attachments to the responding agent A Discord support thread's screenshots were never seen by the agent handling the mention. The relay delivered them and the poll stashed them: the reporter's images arrived on the `thread_starter` entry of `in_reply_to_chain` while the mention's own media list was empty. The gap was in the responder's playbook, which enumerated a fixed field list (`request_id`, `text`, `in_reply_to`, `in_reply_to_chain`) and so made every other field, attachments included, invisible. Fix it where the gap is, in prose: - Read the complete payload object rather than a fixed field list, so media and later relay fields are never skipped again. - Fetch and view attached media with the agent's own tools, on the mention and on every chain entry, and call out the common shape where only the thread starter carries the screenshots. - Restrict those fetches to known-good platform media hosts over https (Discord: cdn.discordapp.com, media.discordapp.net, images-ext-1.discordapp.net, images-ext-2.discordapp.net; X: pbs.twimg.com, video.twimg.com), report a blocked host instead of working around it, and treat everything fetched as untrusted public input on the same terms as the surrounding thread text. The poll stays out of it and downloads nothing, so no third-party bytes are pulled on the polling path. The new test pins the contract the playbook depends on: a mention in the incident's shape, with an empty top-level media list and screenshots on the thread starter, must reach the inbox with the payload intact and its media URLs unfetched. * no-mistakes(review): Preserve media authority and enforce poll-only fetching * no-mistakes(document): Clarify Relay attachment safety prose
) * Defer inactive startup reconciliation * no-mistakes(review): Queue deferred inactive reconciliation diagnostics durably * no-mistakes(review): Require worker phases to cover startup requests * no-mistakes(review): Make diagnostic wakes safely acknowledgeable * no-mistakes(document): Document deferred startup phase coverage
* fix: bound status presentation lock waits * no-mistakes(review): Distinguish malformed presentation locks from live contention * no-mistakes(review): Bound no-ack drain queue lock acquisition * no-mistakes(document): Document bounded presentation-lock drain behavior * no-mistakes(lint): Annotate bounded lock output global * no-mistakes(ci): Added deterministic regression coverage for successful bounded-lock acquisition after live contention, verifying helper-to-caller PID ownership handoff and caller release. Verified with bash syntax checks, git diff checks, and the full fm-wake-queue test suite
* fix(relay): close a public loop whose work lives in a remote secondmate home A public-followup loop bound to a REMOTE secondmate could never be closed. `clear_public_followup_link` (bin/fm-public-followup.sh:701) required an absolute recorded `work_home_path` for a `secondmate:*` work home, but a remote route has no local path on this machine, so registration records that field empty (bin/fm-public-followup.sh:291). Every close ran that clear first, so `retire` died with "could not clear the legacy X link ... retained for reconciliation" forever, and `deliver` posted the public reply and then stranded the loop at `posted`. `--force` never covered that step. The clear now goes to the remote home over that route's SSH transport, running `fm-x-followup.sh --clear <work-id>` through `bin/fm-on.sh`. The route is decided from `data/secondmates.md` before any local path is consulted, so a same-named local directory can never stand in for a remote home, and registrations already on disk retire without needing a new field. `fm-on.sh` passes ssh's status through, so 255 stays the established "delivered but completion unknown" result this codebase already reconciles: the close is refused, the registration and the remote link are left exactly as they were, and the message names the unknown completion instead of claiming a definite failure. Local secondmate and `main` work homes are untouched, and `--force` still governs only the unresolved-obligation refusal. Three regression cases drive a remote route end to end, faking only the ssh binary at the FM_SSH_BIN seam and then running the real remote entrypoint against a local checkout, so the clear that must reach the remote home actually happens there. * no-mistakes(review): Guard remote link clears by request identity * no-mistakes(review): Fail guarded clears on unreadable remote state * no-mistakes(review): Reject guarded clears on non-writable remote state * no-mistakes(review): Allow no-link retirement in non-writable remote state * no-mistakes(document): Correct public-followup verification guarantee count * no-mistakes(ci): Fixed the guarded link-clear race by ensuring absence is decided under the metadata lock whenever publication is possible. Added a behavioral concurrency regression test. Verified with fm-x-mode and fm-public-followup suites, Bash syntax checks, diff checks, and bin/fm-lint.sh * no-mistakes(ci): Fixed the guarded link-clear race by refusing an unlocked absence decision when a publisher already owns the metadata lock in a non-writable directory. Added a behavioral concurrency regression test. Verified with fm-x-mode, fm-public-followup, syntax/diff checks, and fm-lint * no-mistakes(ci): Fixed the guarded-clear race by refusing all guarded clears when the metadata parent is non-writable, including apparent link absence. Added a behavioral regression with a publisher waiting to create the lock, updated remote-retirement expectations and verification docs. Passed fm-x-mode, fm-public-followup, fm-lint, documentation audience, Bash syntax, and diff checks * fix(relay): bound the guarded remote link clear so it refuses instead of hanging The guarded clear checks that the remote state directory is writable before taking the metadata lock, but that check cannot close the window: the parent can turn non-writable between the check and lock creation, and a lock held by a live holder is indistinguishable from that at the acquire. `fm_lock_acquire_wait` is an unbounded `while ! try; do sleep 0.1; done`, so either case retried forever and `deliver` or `retire` wedged with nothing reported, instead of returning the retained-for-reconciliation refusal the guard exists to produce. This path runs unattended over the secondmate transport, where a wedge is worse than either outcome the guard defines. The guarded clear now acquires through `fm_lock_acquire_wait_bounded` (FMX_LINK_CLEAR_LOCK_TIMEOUT, default 10 seconds) and refuses on timeout through the existing failure path. Unguarded local callers keep the ordinary unbounded wait, so local behavior is unchanged. The bounded primitive's header no longer claims presentation-only scope, since this is a second authorized caller; nothing else in the shared lock infrastructure changed. The regression holds the metadata lock with a genuinely live process while leaving the state directory writable, so the refusal can only come from the bound and never from the writability precondition. Against the unbounded wait it does not terminate at all; with the bound it refuses, retains the registration, writes no receipt, and leaves the remote link untouched. * no-mistakes(review): Harden lock-timeout regression with independent deadline * no-mistakes(review): Restore no-op guarded clears on read-only state * no-mistakes(document): Clarify remote public-followup cleanup contract
) * fix(bin): resolve process-event state roots before validating them The process-event module validated the caller's spelling of a home's state root instead of the directory it operates on: it required the supplied path to equal its own lexical normalization, which rejects any path reached through a symlinked ancestor. On macOS both /tmp and $TMPDIR are symlinks, so an operator home under either could never claim a source. Reconcile still reported the runner started, while the detached runner died writing "cannot claim source" to the discarded stderr, and the source silently never fired. Resolve the state root to its physical directory once, then apply the existing private-directory validation to that resolved directory and derive every path, recorded claim identity, and later confinement check from it. This keeps the confinement contract for the directory actually operated on rather than only for callers that already spelled it physically, and removes the window where an ancestor symlink could be repointed between check and use. Homes already spelled physically behave identically. This was the single cause of both deterministic macOS failures in tests/fm-procevent.test.sh ("reconcile never claimed the registered source") and tests/fm-procevent-when.test.sh ("the winning concurrent arm did not produce an outcome"). The new case pins the behavior with an explicit symlinked-ancestor home, so it fails without the fix on any platform rather than only where the temp root happens to be a symlink. * fix(bin): pin the external capture staging boundary to its physical path The extension capture path pinned its registry staging boundary by comparing `pwd -P` against the caller-spelled registry directory, so a home reached through a symlinked ancestor still refused to start an extension-backed source after the state root itself resolved correctly. That left such a home half working: built-in sources ran while external ones failed. The staging preparer now prints the physical registry directory it validated, matching the inbox and reservation preparers beside it, and the start path pins on that returned path. The new end-to-end case drives the shipped file-signal package from a symlinked home spelling. * no-mistakes(review): Propagate canonical process-event state roots * no-mistakes(review): Propagate canonical state to process-event adapters * no-mistakes(document): Document physical process-event state roots
…kunchenguid#3312) * fix(pi): persist captain outcomes visibly * no-mistakes(review): Recover captain outcomes after cold-start lock acquisition * no-mistakes(document): Document cold-start captain-outcome recovery * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes * no-mistakes(review): Prove immediate Pi captain-outcome transcript delivery * 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 * fix(pi): process captain outcomes through a sequence-keyed turn PR kunchenguid#3312 made every captain-facing supervision outcome a durable, exact-once visible transcript entry with the read cursor advancing only after that entry exists. That is the display half of the delivery contract. Left alone it turns a probabilistic silent loss into a deterministic one: the captain sees an anchor line, and firstmate never acts, because nothing opens a turn and nothing records whether main ever processed the outcome. The 2026-08-31 timeline showed the two shapes this must survive on the previous hidden-turn path: seven delivered decision outcomes each answered by an empty assistant message (cursor advanced, no retry, unanswered for close to three hours), and two answered by an unrelated prior reply. Both happened because delivery advanced the cursor at enqueue and accepted whatever the next assistant message was. Add the processing half on top of the persistence half: - bin/fm-branch-outcome.sh keeps a processed marker separate from the read cursor (`unprocessed`, `mark-processed --through`, `processed-init`). It only advances through an explicit sequence-bound acknowledgement, never past the read cursor and never backwards; an absent marker reads as zero and `processed-init` migrates delivered history once so an upgraded home is not re-presented its past. - After the visible entry for a captain outcome exists, the extension hands every still-unprocessed captain row to main as one hidden, typed `fm-branch-process` request listing each `[seq N] task: summary`, opening exactly one main turn. Main closes it only by calling the new `fm_branch_processed` tool with the highest sequence listed. An unrelated, empty, or paraphrased answer leaves the sequence open, and the same request is presented again at the end of the next main run and at session start. The first two presentations of a sequence set open a turn of their own; after that the request rides the captain's next prompt so an ignored request cannot loop, and a session replacement resets that budget. Routine outcomes stay turn-free. - The regressions cover exactly those incident shapes against the real store scripts: an empty answer and an unrelated prior answer neither advance the marker nor stop re-presentation, the acknowledgement is refused beyond the read cursor and outside lock ownership, a partial acknowledgement keeps the newer sequence open, and kunchenguid#3312's own assertions now forbid an unkeyed turn rather than any turn. The store suite pins the marker's bounds and the migration; the real-SDK guard for appendEntry persistence and model exclusion is unchanged. Docs move the protocol from "no model turn" to "one sequence-keyed processing turn closed only by its acknowledgement", and the verification record carries the dated run against Pi 0.84.4. * no-mistakes(review): Harden outcome listing and sequence-bound acknowledgements * no-mistakes(review): Harden outcome state validation and request pacing * no-mistakes(review): Reject unsafe sidecars and unterminated outcome stores * no-mistakes(review): Validate canonical mark-read cursor state * no-mistakes(review): Guard cursor advancement against corrupt processed state * no-mistakes(review): Bind acknowledgements to active processing requests * no-mistakes(review): Reset pacing when processing sequence membership changes * no-mistakes(review): Enforce silent outcome invariants at storage boundary * no-mistakes(document): Document hardened captain outcome processing contracts --------- Co-authored-by: kunchenguid <kun@kunchenguid.com>
…3481) * feat: bound Bearings remote ledger collection * no-mistakes(review): Clarify default remote-ledger collection behavior * no-mistakes(review): Detach reconcile delivery from watcher loop * no-mistakes(review): Enforce bounded snapshot and request captures * no-mistakes(review): Bound legacy summary capture before parsing * no-mistakes(review): Bound primary remote ledger captures * no-mistakes(document): Correct snapshot and reconcile documentation * no-mistakes(lint): Fix ShellCheck quoting in bounded collector * no-mistakes(ci): Fixed all three CI failures: updated the macOS Bearings assertion to 44 tests, made the home-summary test deterministic and aligned with default ledger consumption, and increased the asynchronous reconcile retirement wait for loaded CI. Verified both focused suites, all 44 Bearings tests, ShellCheck, actionlint, Bash parsing, and git diff checks * test: await reconcile request retirement * no-mistakes(review): Avoid empty reconcile queue process churn * no-mistakes(review): Read ledger summaries from immutable snapshots * no-mistakes(review): Reject multi-document home ledger streams * no-mistakes(review): Coalesce durable reconcile requests per target * no-mistakes(review): Unify reconcile keys and reject snapshot streams * no-mistakes(review): Key reconcile requests by stable target ID * no-mistakes(document): Document per-target reconcile request coalescing * no-mistakes(lint): Remove unused snapshot summary file variable * no-mistakes(ci): Adjusted the concurrent collector regression’s end-to-end timing ceiling to account for stock macOS process/jq overhead outside the three-second remote collection budget, while remaining below the 15-second serial-read floor. Verified with stock /bin/bash 3.2: all 44 Bearings tests pass; bash syntax and git diff checks pass * no-mistakes(ci): Fixed legacy summary validation to require exactly one top-level JSON document and added behavioral regression coverage. Stabilized CI by conditionally waiting longer for durable reconcile delivery and synchronously stopping the fm-on worker tree before fixture cleanup. Removed a redundant flaky healthy-path timing assertion; the wedged-reader test still proves concurrent bounded collection. Verified fm-bearings-snapshot, fm-secondmate-reconcile, and fm-on tests, plus project ShellCheck, bash syntax, and git diff checks
* fix(ci): rebalance the portable serial shards on measured durations The "Behavior portable serial 3" shard ran 17-20 minutes against its 20-minute job cap and intermittently timed out seconds after a passing test, on branches and on main alike. Shards are packed longest-processing-time from per-script duration hints, and those hints were last measured on 2026-08-21 at 116 scripts. The lane has since grown to 139 scripts and from ~42 to ~63 minutes: 17 scripts had no hint at all and fell back to the 20 s default, and several existing hints were low by 2-5x (fm-watch-triage 142 s hinted vs 263 s measured, fm-public-followup 36 s vs 197 s). The partition therefore looked perfectly balanced in hint space, 734.6 s per shard, while really running 11.5, 13.6, 18.8 and 16.5 minutes. Script-count balance, which is what the tests asserted, stayed normal throughout and hid it. Refresh the hints from the timing artifacts of three green runs, taking the slowest measurement of each script so the balance holds on a slow runner, and split the lane across five shards instead of four. Replayed against those runs' real per-script durations the worst shard is now 12.54 minutes, 63% of the unchanged 20-minute cap, and the serial lane's wall clock drops from ~20 to ~12.5 minutes. Bound the drift that caused this rather than relying on the hints being refreshed by hand: the coverage guard now reports the unmeasured share as serial_unhinted= and refuses past PORTABLE_SERIAL_MAX_UNHINTED_PERCENT, which leaves room for newly added tests while making a stale table fail the guard instead of silently pushing one shard into its cap. No test changes what it asserts and no test stops running; only the partition across shards changes. * no-mistakes(document): Clarify conservative shard timing aggregate
…uid#3491) * fix(pi): fall back after settled branch errors * no-mistakes(review): Detect provider errors across prompt compaction * no-mistakes(review): Preserve in-flight branch state across selection changes
* fix(pi): recover supervision branch after cooldown * no-mistakes(review): Defer branch recovery until prompt settlement * no-mistakes(document): Clarify supervision cooldown recovery contract
* refactor: remove legacy remote summary reads * no-mistakes(document): Document ledger-only snapshot reads * no-mistakes(ci): Fixed the snapshot test fixture so ledger refreshes use the same fake executable PATH as the snapshot consumer. This preserves observable endpoint freshness after removing legacy summary computation. Verified stock Bash parsing and all 44 Bearings tests pass under /bin/bash; git diff checks pass * no-mistakes(ci): Fixed the CI-only snapshot fixture failure by ensuring the bounded-ledger refresh uses its fake tmux backend. This removes host tmux availability as a source of nondeterminism. Verified all 44 Bearings tests pass, Bash syntax passes, and git diff checks are clean * no-mistakes(ci): Fixed CI nondeterminism in the Bearings fixture: all local ledger refreshes now use the fixture’s fake tmux backend when available, instead of depending on host tmux state. Verified stock /bin/bash syntax, git diff checks, and all 44 Bearings tests with a deliberately failing host tmux
…henguid#3498) * fix(pi): rearm watcher after session replacement * no-mistakes(review): Queue actionable closes across Pi session replacement * no-mistakes(review): Stop replacement arm when handoff persistence fails * no-mistakes(review): Preserve actionable wakes through branch and late child races * no-mistakes(review): Surface late handoff failures without crashing Pi * no-mistakes(review): Coordinate replacement delivery settlement and unique handoff tokens * no-mistakes(review): Retry stale deliveries and release settled claims * no-mistakes(review): Distinguish branch settlement and retry handoff cleanup * no-mistakes(review): Deduplicate persistent handoff cleanup alerts * no-mistakes(review): Acknowledge watcher follow-ups only when consumed * no-mistakes(review): Persist idle follow-ups until agent consumption * no-mistakes(review): Preserve pending outcomes when handoff persistence fails * no-mistakes(review): Arm replacement before awaiting prior delivery settlement * no-mistakes(review): Adopt pending handoffs after lock reclamation * no-mistakes(review): Prevent stale generations from adopting replacement handoffs * no-mistakes(review): Scope replacement handoffs by watcher state * no-mistakes(document): Clarify replacement handoff documentation * no-mistakes(ci): Fixed the failing branch-extension tests to model the new settlement-promise contract. Failure cases now assert that delivery ownership returns to the watcher instead of expecting direct extension fallback. Verified the updated branch suite, Pi watcher suite, shell syntax, and diff checks * no-mistakes(review): Update branch settlement tests and preserve chunked outcomes * no-mistakes(document): Document watcher-owned replacement handoffs * no-mistakes(document): Verify replacement handoff documentation * test(pi): cover watcher-owned branch fallback * no-mistakes(document): Refresh watcher-owned fallback documentation
…d#3495) * fix(bin): resurface terminal statuses lost after branch handling * test(watch): canonicalize process-event fixture homes * no-mistakes(review): Index branch outcomes by causal status position * no-mistakes(review): Recover outcome indexes and deduplicate resurfaced statuses * no-mistakes(review): Handle legacy ambiguity and oversized status diagnostics * no-mistakes(review): Keep unclassifiable oversized statuses silent * no-mistakes(document): Document lost-wake outcome backstop * no-mistakes(document): Update outcome backstop documentation * no-mistakes(ci): Fixed CI regressions in wake-drain: parseable reserved-key decisions can no longer bypass the durable decision-fold guard, and status output is prepared and receipt-committed before presentation to prevent repeated one-shot outcomes after later failures. Added a behavioral regression for receipt commit failure and retry. Targeted backstop, correlation-token, decision-cursor, open-decision, unread-status, syntax, and diff checks pass locally. Shard-4 failures appeared unrelated/flaky; the network-parallel test passed locally * no-mistakes(ci): Fixed the Greptile P1 data-loss issue by committing presentation receipts only after prepared output reaches stdout. Added behavioral coverage proving output failure leaves the backstop retryable and receipt failure may duplicate but never lose a presentation. Relevant wake-drain suites and syntax/diff checks pass. The shard-4 Pi extension failure is unrelated to this PR and did not warrant changes * no-mistakes(ci): Stabilized tests/fm-bootstrap-network-parallel.test.sh by replacing scheduler-sensitive equal-sleep timing with bounded synchronization between mocked fetch and remote probes. This preserves detection of real serialization while avoiding false failures under CI load. Verified with five consecutive test runs, bash syntax validation, ShellCheck, and git diff checks. The separate Pi stock-rendering failure reproduces locally but is unrelated environment/version drift * no-mistakes(ci): Fixed Behavior portable serial 4 by adding fm-classify-lib.sh and fm-timeout-lib.sh to the broken-root Pi test fixture; fm-branch-outcome.sh now depends on them. Verified the full Pi branch-extension suite with real-Pi checks skipped, the wake-drain outcome-backstop suite, Bash syntax, and git diff checks. Greptile findings are already addressed at HEAD; the no-mistakes attestation failure is external head-SHA state
…id#3503) * fix(bin): deliver typed terminal results from remote work homes A public commitment whose work is bound to a REMOTE secondmate home could never receive its typed terminal result. `fm-public-followup.sh brief` printed an emit command carrying this home's own absolute path and this checkout's own script path, neither of which exists on the machine the worker runs on, so the worker had nothing it could write to that the owning home would ever read - and `consume` kept finding nothing while the promise stayed open. The brief is now route-aware: for a remote work home it prints that route's own code root and home with `--stage-in`, so the typed event is staged in the home where the work actually runs, and the closing paragraph names the owning home as the one on the other machine instead of pointing at the path above it. The owning home collects those staged results over the same SSH route it reaches that secondmate on, because the transport only runs outbound: `consume` pulls them into its own inbox and reconciles them exactly as it reconciles a local report. Collection is non-destructive until the result is durably held, so a dropped connection cannot lose a terminal result, and a route that could not be reached is named in `consume`'s output with the promise left open rather than reported as an empty inbox. A local work home is untouched: the brief still prints `--home` with this home and this checkout's script, and the event still lands directly in this home's typed terminal-result inbox. This is the emit-side counterpart of the retire/clear fix in kunchenguid#3479 and reuses the remote-route resolution that landed with it. Reconciling a loop bound to a remote route now reaches that route, so the existing remote cases drive `consume` through the same faked transport their other steps already use. * no-mistakes(review): Fail loudly on unresolved routes and invalid staging homes * no-mistakes(review): Fail collection when remote outbox is unreadable * no-mistakes(review): Surface reassigned remote routes during empty collection * no-mistakes(review): Fail remote collection on invalid registrations * no-mistakes(review): Reject unsafe registration entries during remote collection * no-mistakes(review): Restore healthy empty remote collection behavior * no-mistakes(review): Skip remote collection for delivered registrations * no-mistakes(review): Skip delivered registrations before route validation * no-mistakes(document): Document remote follow-up collection semantics
…#3504) * fix(bin): exclude secondmates from home-summary child inventory kind=secondmate meta records never have backlog rows, so counting them in unowned_children or terminal_in_flight made a clean main home look invalid once earlier ledger checks passed. * no-mistakes(review): Cover terminal secondmate in-flight exclusion * no-mistakes(ci): Updated the stock macOS Bash CI snapshot expectation from 15 to 16 tests. Verified all 16 snapshot/fleet-view tests pass under Bash 3.2.57 and `git diff --check` succeeds
* fix(bin): self-heal status-outcome indexes on every drain Missing ready markers were skipping the lost-wake backstop on non-Pi homes because only the Pi branch ran processed-init. Drain now rebuilds those indexes under the outcome lock and fails closed only on a real store fault. * no-mistakes(review): Guard held-lock initialization and fail marker writes * no-mistakes(document): Document cross-harness outcome-index self-healing
Take the complete contiguous upstream prefix requested by Firstmate. Preserve fork divergences while adopting the upstream capabilities. Keep upstream parentage for the next sync round.
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.
This merges the first contiguous upstream prefix through
kunchenguid/firstmate@88fb3c0ae9ddca1cfd58acf87308f0d1d4b73ccbinto fork parentab9f96fa20bee46b4b0a13e00ac06ae97a344530.The measured merge base is
0866a770234502364c268a768cb7c66cc321c629, and the round contains exactly 20 upstream first-parent changes.The ledger TRACK strategy governs same-capability collisions, with compatible fork behavior retained.
Applicability
4ad8cbae- kunchenguid#3221: refactor(quota): extract mid-task polling and candidate selection into dedicated scripts6c1d2db1- kunchenguid#3371: fix: surface comments on Lavish annotationsa5f3cbee- kunchenguid#3420: fix: support first public-followup registration on Bash 3.2355f46fe- kunchenguid#2792: fix(bin): isolate new Herdr server environments41d0ab39- kunchenguid#3442: fix: surface inbound Relay media to responding agentsf2ee922a- kunchenguid#3480: fix(bin): defer inactive reconciliation during startupf42a6291- kunchenguid#3475: fix(bin): bound wake drain presentation lock waitsee58e39b- kunchenguid#3479: fix(bin): retire public follow-ups in remote homes7d4b5177- kunchenguid#3484: fix(bin): support process events under symlinked homes54663948- kunchenguid#3312: fix(pi): deliver captain outcomes as deterministic transcript entries3b891c81- kunchenguid#3481: feat: add bounded concurrent Bearings ledger collection1459c4dd- kunchenguid#3489: ci: rebalance portable serial test shards714da649- kunchenguid#3491: fix(pi): fall back on incomplete supervision branch prompts1c410299- kunchenguid#3497: fix(pi): re-probe supervision branch after cooldown521de54c- kunchenguid#3501: fix(bin): remove legacy remote snapshot reads84c01b40- kunchenguid#3498: fix(pi): preserve watcher continuity across session replacementd9771284- kunchenguid#3495: fix(bin): resurface task statuses missed by wake handling56b4c15c- kunchenguid#3503: fix(bin): collect follow-up results from remote work homes763f5979- kunchenguid#3504: fix(bin): exclude secondmates from home-summary validity88fb3c0a- kunchenguid#3509: fix(bin): self-heal outcome indexes on first drainValidation
Both exact parents were tested in independent temporary clones before the upstream merge was committed.
The complete inventory was run as
bin/fm-test-run.sh --all --exclude-family real-herdr-gated --json <artifact>, followed by every script selected by--list --family real-herdr-gatedserially.Firstmate approved the same outer cleanup/tripwire wrapper for the unchanged
fm-afk-launchfixture on both parents and the merge; every other Herdr fixture used its reviewed named-session cleanup.002002022The original parent sweeps inherited this host's
002umask.Every failing parent script was then rerun on that same unmodified parent with only the umask normalized to
022; the final complete sweep uses022.The isolated extension-binding control also reproduced the private-directory refusal at
002and passed at022.The following table preserves every original parent failure and distinguishes those reruns from the original baseline.
022recheck022recheck022, before follow-upsfm-bearings-board-render.test.shfm-bearings-board.test.shfm-bootstrap-network-parallel.test.shfm-bootstrap.test.shfm-captain-hold-lifecycle.test.shfm-extension-binding.test.shfm-on.test.shfm-procevent-quota.test.shfm-procevent-when.test.shfm-procevent.test.shfm-recall.test.shfm-remote-doctor.test.shfm-remote-reply.test.shfm-remote-secondmate-lifecycle-e2e.test.shfm-remote-secondmate-parent-binding.test.shfm-remote-secondmate-trace-context.test.shfm-secondmate-reconcile.test.shfm-session-start.test.shfm-test-run.test.shfm-wake-queue.test.shfm-watch-triage.test.shOriginal diagnostic attribution:
bravoliveness line; the table records whether normalizing permissions cleared that symptom.MISSING: nodefixture diagnostic on this installed host.fm-onobserved the Nix profile directory in its composed child PATH, and remote-doctor did not create the expected first harness wrapper.fm-test-runrequired absent Ruby for its YAML inspection; the fork retains its existing per-case optional-interpreter handling.These parent failures were recorded separately and were not quietly fixed inside the sync.
The full merge sweep above is preserved verbatim, including its five separately explained failures; follow-up results are not substituted into its counts.
The canonical lint command was
CI=true bin/fm-lint.sh, using pinned ShellCheck 0.11.0 and actionlint 1.7.12.The final coverage guard reports
total=223 parallel=24 serial=161 serial_shards=8 serial_unhinted=5 herdr=12 optin=26.Documentation checks pass: 126 classified surfaces and 726 local links; pointer check reports 35 checked, 19 valid, 0 broken, and 16 skipped.
All real-Herdr runs use installed Herdr 0.8.2/protocol 20, with unchanged-default tripwires checked separately; CI independently uses its pinned Herdr release.
Supplemental scope and limitations:
stopSessionGenerationon the pinned upstream parent and final merge (line 423 upstream, line 445 final). The original inventory runs report the repository-defined missing-tscskip. No type fix was hidden in this sync.Deliberate divergences
Every active entry in the ledger is accounted for below.
fm-agy-adapter.test.sh: pass,fm-backend-herdr.test.sh: passfm-lint.test.sh: passfm-quota-sidecar.test.sh: passfm-send-strict.test.sh: passfm-backlog-handoff.test.sh: passfm-watcher-lock.test.sh: passfm-watcher-lock.test.sh: pass,fm-backend-herdr.test.sh: passfm-run-progress.test.sh: pass,fm-watch-triage.test.sh: passfm-daemon.test.sh: pass,fm-watch-triage.test.sh: passfm-backend-herdr.test.sh: passfm-remote-job-orphan-reap.test.sh: pass,fm-remote-job.test.sh: passfm-remote-job.test.sh: passfm-test-run.test.sh: pass after the reviewed integration correction,fm-on.test.sh: passfm-crew-state.test.sh: pass,fm-teardown.test.sh: passfm-trigger-validation.test.sh: pass,fm-brief.test.sh: passfm-backlog-atomicity.test.sh: passfm-spawn-dispatch-profile.test.sh: passfm-fleet-snapshot-view.test.sh: pass,fm-home-summary-refresh.test.sh: pass,fm-bearings-snapshot.test.sh: passfm-pi-watch-extension.test.sh: passfm-no-mistakes-required-gate.test.sh: passfm-no-mistakes-required-gate.test.sh: passfm-pr-merge.test.sh: passfm-upstream-status.test.sh: pass,fm-update.test.sh: pass.no-mistakes.yamlstill setstest.evidence.store_in_repo: false; no validation artifact is tracked.fm-agents-hard-rules.test.sh: passfm-gbrain-lib.test.sh: pass,fm-gbrain-capture.test.sh: pass,fm-gbrain-health.test.sh: pass,fm-recall.test.sh: pass in clean merged copy; workspace limitation attributed above,fm-remote-secondmate-lifecycle-e2e.test.sh: passfm-dashboard.test.sh: pass,fm-dashboard-events.test.sh: pass,fm-dashboard-gbrain.test.sh: pass,fm-dashboard-gbrain-ui.test.sh: pass,fm-dashboard-history.test.sh: pass,fm-dashboard-usage.test.sh: pass,fm-usage.test.sh: pass,fm-teardown.test.sh: passfm-test-run.test.sh: pass after the reviewed integration correctionfm-wake-queue.test.sh: pass after the reviewed integration correction,fm-guard-stale-banner.test.sh: pass,fm-turnend-guard.test.sh: passContract-file reasoning
.agents/skills/bearings/SKILL.md.agents/skills/fmx-respond/SKILL.md.agents/skills/process-event-sources/SKILL.md.agents/skills/quota-array-dispatch/SKILL.md.github/workflows/ci.yml.pi/extensions/fm-branch-supervision.ts.pi/extensions/fm-primary-pi-watch.ts.pi/extensions/lib/fm-branch-dispatch.tsAGENTS.mdREADME.mdbin/backends/herdr.shbin/fm-bearings-snapshot.shbin/fm-branch-outcome.shbin/fm-classify-lib.shbin/fm-dashboard-server.mjsbin/fm-fleet-snapshot.shbin/fm-inactive-reconcile.shbin/fm-procevent-lavish.shbin/fm-procevent-lib.shbin/fm-procevent-quota.shbin/fm-procevent.shbin/fm-public-followup-collect.shbin/fm-public-followup-emit.shbin/fm-public-followup-lib.shbin/fm-public-followup.shbin/fm-quota-axi-lib.shbin/fm-quota-choose.shbin/fm-secondmate-reconcile.shbin/fm-session-start.shbin/fm-startup-network.shbin/fm-test-run.shbin/fm-wake-drain.shbin/fm-wake-lib.shbin/fm-watch.shbin/fm-x-followup.shbin/fm-x-lib.shdocs/architecture.mddocs/calm-mode-feasibility.mddocs/configuration.mddocs/dashboard.mddocs/fm-test-portable-shards.mddocs/fork-divergence.mddocs/herdr-backend.mddocs/pi-supervision-branch-poster.svgdocs/pi-supervision-branch.mddocs/scripts.mddocs/sessionstart-nudge.mddocs/supervision-protocols/pi.mddocs/verification/public-followup.mddocs/verification/runtime-backends.mddocs/verification/supervision.mddocs/watcher-continuity.mdCollision test-file reasoning
The contract-file table above and this test-file table account for every reached collision-risk path.
tests/fm-backend-herdr.test.shtests/fm-bearings-snapshot.test.shtests/fm-bootstrap-network-parallel.test.shtests/fm-extension-binding.test.shtests/fm-fleet-snapshot-view.test.shtests/fm-home-summary-refresh.test.shtests/fm-on.test.shtests/fm-pi-watch-extension.test.shtests/fm-pr-check-security.test.shtests/fm-procevent.test.shtests/fm-public-followup.test.shtests/fm-remote-secondmate-lifecycle-e2e.test.shtests/fm-secondmate-reconcile.test.shtests/fm-session-start.test.shtests/fm-startup-network.test.shtests/fm-test-run.test.shtests/fm-wake-drain-open-decisions.test.shtests/fm-wake-queue.test.shtests/fm-watch-triage.test.shtests/fm-x-mode.test.shtests/lib.shCollision audit and ledger changes
The first prefix reaches 50 of the detector collision-risk paths and has 20 textual conflict paths.
The 50-path set was independently checked against both the net upstream diff and the union of every first-parent change in the prefix; both sets match.
The silent-collision audit includes the new remote cache versus the fork read-only dashboard, local task bounds and abandoned-child summaries versus ledger-only collection, and upstream replacement delivery versus fork away-mode standby.
Four active ledger entries are updated: the per-script validation bound and shard timing/cap; local snapshot bounds, abandoned children, and scratch ownership; Pi away-mode standby under upstream tokenized replacement serialization; and read-only dashboard cache consumption.
Two active entries are added: locale-independent coverage comparisons and queued wakes as a supervision requirement.
They record retained fork guarantees that the new upstream comparison and presentation-deadline case must preserve.
No active divergence intent is retired.
The parked-branch list remains unchanged.
All five parked branch tips were resolved from fetched origin references, and none of their commits unique to the fork base intersects the incoming upstream ancestry.
No parked branch was merged, resurrected, used as a rebase base, or cherry-picked.
CI observation and retry
The first CI attempt passed lint, coverage, repository invariants, both parallel shards, seven serial shards, real Herdr, macOS Bash compatibility, and timing aggregation.
Serial shard 4 failed only the existing
fm-dashboard.test.shcadence assertion:a poll arriving mid-snapshot queued an immediate catch-up run (2 runs).The original local full sweep passed this suite on both the fork parent and merge.
A controlled replay of that exact case against each tree's unchanged dashboard server passes with the original 20 ms observation delay and reproduces the same failure on both trees when only that delay becomes 140 ms.
Observed snapshot starts are 252 ms apart on both trees, consistent with the configured 150 ms timeout plus 100 ms poll interval; this disproves an immediate catch-up loop in the replay.
The merge changes only the snapshot child's read-only cache environment, and the cadence function and production scheduling are unchanged from the fork parent.
This is retained fork fixture timing sensitivity, recorded separately from merge adaptations; no tracked fix or assertion relaxation was added for it.
The complete unchanged dashboard suite was rerun on the fork parent and merge after this CI observation; both pass (exit 0).
The failed job was rerun once at the same head and passed.
CI attempt 2 is successful, with all 16 validation checks passing and only the intentional no-mistakes compliance check red.
This retry does not erase the first-attempt failure or its fork-parent attribution above.
Delivery and landing
This ships direct-PR, as instructed by Firstmate.
The expected red
PR must be raised via no-mistakescheck is intentional: no-mistakes rebases ontoorigin/main, which would linearize this merge-only round.All 16 other checks pass: lint, test coverage, repository invariants, two portable parallel shards, eight portable serial shards, real Herdr, stock macOS Bash compatibility, and timing aggregation.
Pushed head:
1f0c113c88d68bd4bb45475b49eebafdd1f02ef0.Mergeability checked at 2026-09-09 00:03 UTC: GitHub reports
mergeable: trueat the exact pushed head and unchanged baseab9f96fa20bee46b4b0a13e00ac06ae97a344530;mergeable_state: unstablereflects the intentional compliance red after all other checks pass.Do not squash this PR.
The landing handler must use:
The explicit
--mergepreserves upstream parentage and advances the merge base for subsequent rounds.