fix(spawn): require durable Claude crew profile - #136
Open
quinnbot-ai wants to merge 8 commits into
Open
Conversation
…tions deterministic (kunchenguid#2617) Three assertions in tests/fm-procevent.test.sh depended on a detached runner having finished work that the command starting it does not wait for. reconcile's replacement runner is started through detach_runner, which only forks: reconcile returns and counts the start before that runner has claimed its source or exec'd its child. Any assertion taken straight after reconcile therefore samples a race. - The publish-before-apply recovery section left its always-ready /bin/echo source registered across the recovery reconcile, so that reconcile launched a competing detached poll (observed: started=1) that then raced every later assertion for the source claim, the next capture sequence, and this home's applied record, and outlived the section holding a live claim. It is now retired before that reconcile - re-announcement is proven from the durable inbox alone and needs no registration - and started=0 is asserted so a competing poll cannot be reintroduced unnoticed. This is the same retire-before-reconcile discipline the self-announcing section already carries; that section acquired it after the identical race made its "not-autohandled: self-src" assertion read "already owned: self-src". - The crashed-leader replacement section snapshotted the replacement's claim file and execution log behind a fixed 0.5s settle window. On a loaded machine that window expires first, which is the CI flake behind "a replacement runner started without recording its own claim" and "reconcile did not start exactly one replacement source". Both effects are now waited for with the suite's bounded wait helpers; the exact one-replacement count is still asserted afterwards, unchanged. - The duplicate-start section slept 0.5s for reconcile's runner to record ownership before asserting that a second start loses to it. It now waits for that claim. Also tighten one assertion that could not fail as written: "autohandled: self-src" is a substring of "not-autohandled: self-src", so the applied path was accepted even when the runner reported the capture left for the handler. Evidence: on the unmodified suite, 128 full runs at 6-8x concurrency produced 6 failing runs, all in the crashed-leader section. On the fixed suite, 216 full runs under the same load produced none. Reverting the self-announcing section's retire-before-reconcile line reproduces "already owned: self-src" on the first iteration, confirming the shared mechanism.
) * fix(bin): keep pending-reply expectations honest on both send legs Two related asymmetries let the parent-owned secondmate reply guard drop or nag requests it should not have. Local delivered-unconfirmed dropped the expectation. A marked request whose submit read-back stayed unconfirmed (verdict=pending) is the same not-a-failure outcome the remote leg reports as delivered, but fm-send discarded the parent's pending-reply record for it, so a request that very likely landed stopped being tracked entirely. The record now stays armed on its unconfirmed-delivery marker: a correlated report still resolves it, and an unanswered one still surfaces through the library's own reconciliation. Exit 3 and the local rule that an unconfirmed answer never closes a decision key are unchanged. Remote replies were nagged for a repost they did not need. A remote mate's report reaches the parent's status log only through the asynchronous mirror in fm-procevent-remote-reply.sh, yet the guard read an absent correlated line as proof the mate never reported - even while the answer was still in flight, which is the common case because the mirror's poll window is comparable to the recovery grace. The mirror now publishes one caught-up watermark from a quiet window, and the guard admits a missing report as evidence only once that watermark passes the turn that should have produced it. A genuinely missed report still gets exactly one repost, and a channel that is behind, unarmed, or broken leaves the request durably open and un-nagged rather than nagging blind; the mirror escalates its own continuity failures as before. Tests: a local unconfirmed secondmate send keeps its expectation armed and resolvable; a mirrored correlated remote reply resolves with no repost; a stale or absent watermark withholds the repost while a fresh one still releases it; a quiet remote window publishes the watermark and retirement clears it. * no-mistakes(review): Distinguish preempted polls from quiet windows * no-mistakes(document): Clarify remote reply channel freshness * no-mistakes(lint): Annotate shared remote preemption exit constant
…d#2619) * fix(watch): honor a declared pause on a busy pane's completed-turn bound A worker that declares an external wait (`paused:`) and then blocks in one long foreground call - a review-hosting scout parked in a single blocking `lavish-axi poll`, a bounded watch loop, a rate-limit sleep - keeps its pane BUSY, so the stale path that already honors declared pauses never ran for it. The busy-pane completed-turn bound instead routed it straight into wedge_timer_check, which re-escalated "possible wedge, escalation N" (and, past the threshold, demand-deep-inspection) every FM_STALE_ESCALATE_SECS for as long as the review stayed open. busy_turn_bound_check now owns which absorber takes a crossed bound: a crew whose own last status line declares an external wait or a verified captain-held transfer takes the bounded FM_PAUSE_RESURFACE_SECS recheck, and everything else keeps the unchanged wedge timer. The discriminator is the declaration together with liveness (the caller has already confirmed the pane is busy), never a blanket silencing - a crew that declared nothing, or whose pane is not live, escalates exactly as before, and a declared pause still re-surfaces once per long cadence so a forgotten wait cannot rot invisibly. Away mode is untouched: the daemon owns pause triage there and already reads the same vocabulary. The two call sites also no longer clear pause bookkeeping in the same poll the pause cadence recorded it, which would have erased the re-surface throttle and turned the long cadence back into a per-poll re-surface. Tests: a three-phase regression fixture pins the absorbed pause, its long-cadence recheck, and the restored wedge escalation once the declaration is lifted on the same busy over-age pane. Also de-flakes tests/fm-watch-triage.test.sh, which failed spuriously on a loaded machine: fixed liveness budgets were reaping watchers mid-startup, so assertions on post-poll state passed vacuously or failed spuriously. Waits that describe a poll's outcome now wait for a completed poll cycle via the liveness beacon, the heartbeat test waits for the heartbeat it asserts on, and every wait_for_exit budget is the uniform 10s already used elsewhere in the file. * no-mistakes(review): Fail poll-cycle waits on timeout * no-mistakes(review): Prevent poll timeout test hangs * no-mistakes(document): Clarify paused busy-pane supervision
Added guidelines for decision communication to the captain.
Clarify communication protocols with crewmates regarding task delegation and reporting.
* fix(herdr): confirm local steers that native agent-state misses Herdr can leave agent_status idle for a landed Claude turn and can keep queued Enter text visible while busy, so fm-send was reporting false swallows. Confirm those cases through the shared queued-Enter verdict and a cleared composer, and keep a genuine idle pending composer as unconfirmed. * no-mistakes(review): Stop Herdr Enter retries on unreadable composers * no-mistakes(review): Reject queued delivery when all Herdr Enter sends fail * no-mistakes(review): Prevent confirmation after failed Herdr Enter * no-mistakes(review): Pace Herdr retries and clarify submit fallback * no-mistakes(review): Align Herdr submit docs with idle fallback * no-mistakes(document): Correct Herdr submit-confirmation documentation
* feat(bin): accept any-origin decision bindings with full-identity keys An aggregation surface (the bearings board) carries captain answers for holds across origins, but a binding was one-origin-per-source and the Lavish adapter capped question keys at 64 chars while real full hold identities measure 69-81. - fm-decision-hold.sh: bind <source-id> --any-origin records the (any) marker; binding prints it verbatim and answers accepts it, so the runner's feed seam carries an any-origin source with no runner change. In any-origin mode each key is a full hold identity <origin>-decision-<key>, split at its first -decision-; a key with no separator (merge/dispatch instructions) is skipped and feeds nothing, keeping non-decision answers out of the hold ledger by construction. Every existing close guard applies unchanged. - fm-procevent-lavish.sh: raise the question-key cap 64 -> 128 so a full hold identity fits; the slug-shape security property is unchanged. - tests: cross-origin closure through the real runner seam, an 81-char identity through the adapter, cap and shape refusals, routed-work skips, nonexistent-identity skips, and idempotent replay. * feat(bearings): add the /bearings lavish interactive fleet board /bearings lavish renders the bearings snapshot onto a shipped, reusable board template and arms it as a Lavish process-event source, so the captain answers Captain's Call items on the board and firstmate is woken by an ordinary check wake - no conversational turn ever blocks on a poll. - .agents/skills/bearings/assets/board-template.html: the shipped template (myfirstmate design system inlined, one fm-bearings-board.v1 JSON slot, fail-closed schema guard that renders an error card instead of an empty fleet). Per-invocation agent work is composing the payload only. - bin/fm-bearings-board.sh: build/refresh owner - fail-closed payload validation, slot injection with a round-trip check and \u003c escaping, stable board path, any-origin bind ALWAYS before arm, arm-if-absent. - bearings SKILL.md: the lavish invocation option, board composition rules, board-wake handling, and the captain-ruled merge-click authorization with its mandatory safeguards (PR resolved from the task's own meta record, wake-time green re-verification, never a red or changed PR, merges only through bin/fm-pr-merge.sh, chat echo with the full PR URL). - process-event-sources SKILL.md: one-line board-wake routing trigger. - tests: payload refusals, injection round-trip, bind-before-arm, idempotent re-arm, and template slot integrity. Fleet pickup: homes receive this after merge plus a firstmate self-update; landing timing is coordinated with the main firstmate. * no-mistakes(review): Harden bearings board validation and wake handling * no-mistakes(review): Require HTTPS for bearings board PR links * no-mistakes(review): Fail closed and bound bearings board answers * no-mistakes(review): Enforce UTF-8 byte limits for board answers * no-mistakes(review): Serve bearings board before arming and reject empty actions * no-mistakes(review): Prove bind-before-arm ordering through live answer consumption * no-mistakes(document): Document bearings board and cross-origin answers
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.
Require Claude crewmate and scout launches to use a validated, configured profile; record profile attribution in metadata; and inherit the crew profile into secondmate homes. Focused spawn, relaunch, inheritance, lint, and documentation checks passed.