Merge upstream template up to a7e01bc (43 commits) - #3
Merged
Merged
Conversation
* fix(pi): distinguish stale locks when arming watcher * no-mistakes(test): Stabilize watcher extension async waits * no-mistakes(document): Document Pi lock recovery
* fix: accept secondmate as house vocabulary * no-mistakes(test): Update captain vocabulary contract test * no-mistakes(document): Align secondmate documentation vocabulary
…uid#686) * fix: parse secondmate home after pre-field parentheses Registry summaries often include parentheticals before the structured (home: ...) field. Match that field with a greedy prefix so handoff no longer reports "has no home" for those entries. * no-mistakes(document): Refresh handoff test comments
* feat: add native session-start nudges * no-mistakes(document): Document nudge script inventory
…nguid#688) Relabel absent-captain and related domain defaults wording so it names the firstmate repo rather than treating "template" as this domain's identity label. Keep the design-tenet "shared template" statements and unrelated launch/PR-poll template uses unchanged.
…nsion (kunchenguid#205) * fix(bin): use set -u-safe empty-array expansion in pr-merge and spawn Expanding "${arr[@]}" on an empty array under set -u fails on bash < 4.4 (notably macOS bash 3.2). Quote the portable "${arr[@]+"${arr[@]}"}" idiom in fm-pr-merge and fm-spawn batch dispatch so empty arrays expand to nothing. Co-authored-by: Cursor <cursoragent@cursor.com> * test(brief): harden fm-brief regression coverage for parse and scaffolds Tighten bash -n checking, pin literal backtick rendering in the no-mistakes DOD wording assertion, and keep a scout/secondmate scaffold smoke test so the Co-authored-by: Cursor <cursoragent@cursor.com> kunchenguid#166 apostrophe regression cannot return unnoticed. --------- Co-authored-by: Cursor <cursoragent@cursor.com>
…nchenguid#693) * fix: make watcher supervision continuous * no-mistakes(review): Bound watcher retries and log attached signals * no-mistakes(review): Add bounded successor-recovery wake fallbacks * no-mistakes(review): Prevent overlapping successor-arm retries * no-mistakes(review): Resume supervision after late arm closes * no-mistakes(review): Bind OpenCode recovery to attempted arm * no-mistakes(test): Synchronize peer beacon regression fixture * no-mistakes(test): Synchronize Pi and OpenCode late-close lifecycle fixtures * no-mistakes(document): Captain: document watcher successor protocol behavior * 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 * 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: always fetch PR head for review diffs Prefer a freshly fetched refs/pull/<n>/head over a reachable recorded pr_head= so reviewers never hold a merge over a "missing" fix that already landed on the remote PR. Recorded SHA is offline fallback only; local branch is last resort with a warning. Store the tip under refs/fm-review/ so a later base-branch fetch cannot clobber the compare tip via FETCH_HEAD. * no-mistakes(test): Isolate session-start nudge tests from gate state * no-mistakes(document): Correct review-diff documentation
…and skills (kunchenguid#736) * docs: resolve five contract contradictions * no-mistakes(test): align owner-pointer assertions with reworded docs; skip absent shellcheck
* docs(harness): reverify grok exit command * no-mistakes(test): Correct Grok exit resume attribution
* fix(watcher): bound stale wakes for exited paused crew * no-mistakes(review): Gate pause suppression on confirmed agent death * no-mistakes(test): Fixed stale pause cadence * no-mistakes(document): Document dead-agent hold cadence
…id#744) * fix(supervision): distinguish ordinary wakes from repair * no-mistakes(review): Make passive guard follow-ups recovery-only * no-mistakes(document): Clarify recovery-only turn-end guard documentation
* fix(x-mode): dedupe pending mention wakes * no-mistakes(review): fix x-poll claim error deduplication * no-mistakes(review): separate claim diagnostics from relay recovery * no-mistakes(document): Document X-mode once-only mention wakes
…enguid#747) * feat(wake): enrich drained signal context * no-mistakes(review): Bound wake enrichment reads * no-mistakes(document): Document wake-drain annotations * docs(wake): explain at-least-once drain boundary * no-mistakes(review): Prevent symlink races in wake annotations * no-mistakes(review): Exercise wake symlink race regression * test: document intentional AFK marker subprocesses * fix(wake): isolate annotation marker state
* feat(herdr): add optional presentation spaces * no-mistakes(review): Harden Herdr projection creation and spawn serialization * no-mistakes(review): Captain, disarm Herdr cleanup before launch submission * no-mistakes(test): Correct stale Orca metadata failure fixture * no-mistakes(document): Document Herdr presentation projection accurately
…nchenguid#775) * fix(send): treat opencode busy-queued composer state as submitted When fm-send sends a message to a BUSY opencode crewmate on the tmux backend, opencode accepts the Enter and queues the message for the next turn, but leaves the typed text visible in the composer row. The submit-verification loop sees a pending composer, exhausts retries, and reports a false "Enter swallowed" failure while the message is actually delivered. Fix: after Enter retries are exhausted and the composer still shows pending, check fm_pane_is_busy. If the pane is busy (agent mid-turn, footer shows "esc interrupt"), the harness queued the message, so return "empty" (accepted). On an idle pane, keep returning "pending" (genuine swallow detection preserved). Regression tests cover four scenarios: - busy pane + pending composer -> empty (message queued) - idle pane + pending composer -> pending (genuine swallow) - busy pane + composer clears on first Enter -> empty - idle pane + composer clears on first Enter -> empty (existing path) * docs: document busy-queued Enter exception across backend docs and skills Add explanatory comments and backend documentation for the busy-queued Enter fix (opencode 1.18.4 accepts Enter mid-turn but keeps typed text in composer until the turn ends): - bin/fm-tmux-lib.sh: document the busy-aware fallback in the file header and above fm_tmux_submit_enter_core - .agents/skills/afk/SKILL.md: daemon-facing policy note - .agents/skills/harness-adapters/SKILL.md: harness-specific fact - docs/tmux-backend.md: submit-acknowledgement section with the busy-queue exception - docs/herdr-backend.md: record the known gap - docs/architecture.md: cross-reference in the daemon section * test(tmux): fix SC2181 and make busy-submit test executable
…unchenguid#765) * fix(spawn): require two stable reads before accepting worktree path The treehouse-get worktree-detection loop in fm-spawn.sh accepted the first pane_current_path read that differed from the project path, but on some tmux/WSL setups a brand-new window transiently reports a stale-but-real path before the pane actually settles into the worktree. Since that stale path is itself a real, distinct git checkout, it also passes validate_spawn_worktree's isolation check, so the loop silently recorded the wrong worktree in state/<id>.meta (and, for claude harness spawns, installed the turn-end hook there too). Require two consecutive polls to agree on the same non-project path before accepting it, using the existing inter-poll sleep as the confirmation gap so an already-settled pane isn't slowed down by an extra cycle. * fix(tests): drop unused CASE_DIR read in worktree-settle test ShellCheck SC2034: CASE_DIR is split out of the case record but never referenced; discard it with _ instead. --------- Co-authored-by: Freudator86 <tim@allesknut.de>
…anges (kunchenguid#752) * fix(watcher): stabilize Linux process identity * no-mistakes(document): document FM_PROC_ROOT_OVERRIDE and Linux starttime identity rationale
* fix(supervision): verb-aware captain relevance, AFK wedge, head-bound state Stop free-text tokens like "merged" from promoting nonterminal working: lines to captain-relevant, so AFK no longer permanently suppresses idle recovery. Defend wedge aging independently for nonterminal progress verbs, bind no-mistakes current-state attribution to code identity (not branch alone), and mark setup-complete as nonterminal in the ship brief scaffold. * no-mistakes(review): Enforce nonterminal suppression and head-bound run attribution * no-mistakes(document): Document current-code-bound run attribution * no-mistakes(test): Wait for stable Herdr shell readiness * no-mistakes(test): Make Herdr and watcher readiness tests deterministic * no-mistakes(test): Make tmux capture and watcher lifecycle deterministic * no-mistakes(document): Document corrected supervision contracts
* fix: allow safe teardown during watcher recovery * no-mistakes(review): Distinguish unsafe-teardown deny guidance via policy reason code * no-mistakes(document): Sync continuity-gate docs to allow teardown recovery * test: mark dynamic teardown fixture literal * no-mistakes(document): docs: add teardown to continuity gate allow list
…nguid#790) * feat(herdr): order presentation worker spaces * fix(herdr): preserve focus during projected cleanup * no-mistakes(review): Serialize Herdr cleanup and protect active seeded tabs * no-mistakes(review): Serialize Herdr aborts with guarded focus regressions * no-mistakes(review): Fall back flat when Herdr serialization is unavailable * no-mistakes(test): Stabilize watcher startup and AFK handoff tests * no-mistakes(document): Correct Herdr ordering and focus documentation
…#809) * Send literal config reread after inherited config push When declared inherited config changes under an already-running secondmate, build a per-home instruction from validated destination post-write bytes and deliver it on the routed secondmate path. Unchanged config sends nothing; ABSENT represents removal; captain-shared is never inlined. Covers mid-session config-push and the locked bootstrap convergence path without hardening spawn against deliberate runtime choice. * no-mistakes(review): Fix config reread framing, partial propagation, and respawn order * no-mistakes(review): Send config rereads via durable single-line pointers * no-mistakes(review): Make failed config rereads retryable * no-mistakes(review): Make config reread retries generation-safe * no-mistakes(review): Make config rereads durable and ordered * no-mistakes(review): Drain retries, bound history, preserve detect-only read-only mode * no-mistakes(review): Retain write retries and quarantine stale respawn generations * no-mistakes(review): Preserve exact config reread retries and delivery order * no-mistakes(review): Preserve exact retry bytes and bounded quarantine pruning * no-mistakes(document): Consolidated config-reread documentation
* feat(watch): follow GitLab merge requests to merge The merge watch only understood GitHub pull requests, so a task whose deliverable is a GitLab merge request was never followed to merge. Generalize the stored poll identity from owner/repository to a provider-tagged provider/url/host/path/number record. GitLab runs mostly on self-hosted instances and its projects nest under groups at no fixed depth, so the host and the full project path are data in the record rather than constants, and every consumer rebuilds the URL from those parts and refuses any record that does not reconstruct it exactly. The GitLab state is read with plain glab, matching the GitHub path's use of plain gh, so an upstream checkout needs no extra tooling. Two things about glab were established by running it rather than assumed, because a wrong invocation here fails silently into a permanent "not merged": - glab has no field selector, and its JSON would need a JSON processor that firstmate does not require, so the state is read from glab's own field output. Only an exact "merged" wakes firstmate, so a changed format stays silent instead of reporting a merge. - glab cannot take a merge request URL the way gh can, because that form resolves through the current git repository and the watcher has none. It is addressed by project URL and merge request number instead. An absent glab produces no wake rather than a false merge, and arming refuses with a clear message since that is the one point where a missing CLI can still be reported. A GitLab task records no pr_head, which both consumers already treat as optional. The merge path still addresses GitHub only and refuses a merge request URL rather than sending it to the wrong forge. The record version moves to v2, and the existing non-executing migration rebuilds an already-armed watch from its recorded URL, so no watch is lost by upgrading. docs/gitlab-merge-watch.md records the evidence, taken against the public fixture project https://gitlab.com/KarotKris/gitlab-merge-watch-fixture. * no-mistakes(review): Reject github.com host in GitLab MR URL/sidecar validation * no-mistakes(document): Note GitLab MR URLs are explicitly refused, not just malformed ones, in fm-pr-merge.sh docs
…uid#821) * feat(herdr): correct all-home child presentation topology Inherit the presentation opt-in to secondmate homes, label new projected spaces with the approved corner format, insert each child under its owning parent under one session-scoped lock, and keep flat non-destructive fallback. * no-mistakes(review): Exclude secondmates from Herdr presentation projection * no-mistakes(review): Harden shared Herdr locks and ambiguous child ordering * no-mistakes(review): Use adjacency-only Herdr child ownership * no-mistakes(review): Reject foreign legacy projections safely * no-mistakes(review): Validate Herdr session sockets before projection * no-mistakes(test): Fix Herdr teardown fixture session socket metadata * fix(herdr): canonicalize presentation lock socket paths Always resolve the session socket parent directory so symlink parents such as /tmp -> /private/tmp cannot split the shared cross-home lock identity. Refuse relative socket paths. Clarify lock-unavailable warnings. * no-mistakes(test): Fix Bash-compatible GitLab merge request URL parsing * no-mistakes(document): Document all-home Herdr child topology * no-mistakes(lint): Quote fallback provenance string for ShellCheck
* 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
Sync the fork with kunchenguid/firstmate up to a7e01bc (2026-07-23), stopping deliberately one region short of ec09871, which introduces a bin/fm-brief.sh that does not parse under stock macOS Bash 3.2 and would break every worker dispatch in this home. origin/main is 2 ahead of upstream as well as behind, so this is structurally a merge, not a fast-forward. The fast-forward-only rule governs the home<-origin leg and is untouched. Headline fix imported: ab8cea6 (kunchenguid#743) bounds stale wakes for parked crew by teaching pause_state_class about captain-held work plus an agent-liveness check. Conflicts resolved by merging intent, not by picking a side: - CONTRIBUTING.md: kept the fork's gate-response-policy sentence, took upstream's fuller guidance wording and its two factually-current sentences about .no-mistakes.yaml, which now pins only commands.lint. - tests/fm-brief.test.sh (DOD wording): kept the fork's assertion, which is the one matching the merged generator; upstream never changed that line, so bin/fm-brief.sh correctly auto-merged to the fork's wording and upstream's three assertions would have failed against real output. Upstream's intent is preserved by the apostrophe guard both sides share. - tests/fm-brief.test.sh (new tests): purely additive collision at one file position; both sides' new tests kept. Verification: bash 3.2 parses all 88 bin scripts; a real brief generates well-formed across ship, scout, secondmate, and filled-pre-flight variants; tests/fm-brief.test.sh passes 20/20; full suite 95 scripts, 1583 passing; bin/fm-lint.sh clean. Three suite failures remain, all reproduced at pristine upstream a7e01bc and none introduced by this merge: two from a broken system Ruby YAML on this machine, one an upstream Orca spawn defect.
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.
Merge upstream template up to
a7e01bc(43 commits)Brings the fork up to date with
kunchenguid/firstmateas far as is safe, stopping deliberately short of the commit that breaks brief generation on this machine.origin/mainis 2 ahead of upstream as well as behind, so this is structurally a merge, not a fast-forward. The fast-forward-only rule governs the home ← origin leg and is untouched by this.Two corrections to the assessment this implements
The scout report (
data/firstmate-upstream-gap/report.md) was written earlier and two of its load-bearing facts did not survive re-verification:a7e01bcisec09871^, so it is 44 of 71 commitsec09871^is6b0d21d.a7e01bcsits 7 commits earlier and brings 43, leaving 28 behind.a7e01bc"should therefore merge clean"bin/fm-brief.sh— the one that mattered most — auto-merged.The conflicts do not all trace to
ec09871.CONTRIBUTING.mdconflicts via six test-infrastructure commits (b843c66,f02eef1,622d467,f6c281a,673b6ad,bd43c73);tests/fm-brief.test.shconflicts viaa7e01bcitself plusea3ac2e,3729081,bc1a21b.The report's core recommendation still held:
a7e01bcparses,ec09871does not, and the absorb fix is included.Verification — all four mandatory gates passed
/bin/bash -n bin/fm-brief.shunder stock Bash 3.2.57bin/*.sh+bin/backends/*.sh{TASK}tests/fm-brief.test.shbin/fm-test-run.sh --all)bin/fm-lint.sh(ShellCheck 0.11.0 pinned)Confirmed the breakage boundary directly rather than trusting it:
The 3 suite failures are not from this merge
Each was reproduced against a pristine
a7e01bctree extracted withgit archive:origin/main)a7e01bcfm-gate-refuse.test.shrequire "yaml"at all —psychis broken. Environmental.fm-nm-test-contract.test.shfm-backend-orca.test.shfm-spawn --backend orcano longer fails when metadata cannot be written. Not a resolution artifact; reproduces on untouched upstream code. Deferred as out of scope.Resolved hunks — every one, explicitly
All three resolved by merging intent. Our round-discipline content survives in full.
1.
CONTRIBUTING.md— one hunk, 5 lines vs 4.Kept our fork's sentence
bin/fm-brief.sh adds firstmate's version-independent gate-response policy only to no-mistakes ship briefs.and ourThat wrapper also routes ask-user findings...sentence. Took upstream's fuller lead sentence and both of its.no-mistakes.yamlsentences, because upstream's are now the factually correct ones: the merged.no-mistakes.yaml(which we never edited, so it took upstream's version wholesale) dropscommands.testand pins onlycommands.lint. Our old prose describing a pinned portable-behavior command had become stale against the file it describes.2.
tests/fm-brief.test.sh, DOD-wording hunk — 4 lines vs 9. Kept ours.Traced the line across all three refs first. Base
6de9278and upstreama7e01bcare identical here; only ourcf13c9bchanged it. Sobin/fm-brief.shauto-merged to our wording correctly, and upstream's three assertions (no-mistakes itself provides for the mechanics,`no-mistakes axi run --help`,`help`) would have asserted against text the generator no longer emits. Upstream's actual intent — that the DOD point at no-mistakes' own installed guidance with literal backticks and no apostrophe artifact — is preserved by our assertion plus theassert_no_grep "no-mistakes' own guidance"guard that both sides already share.3.
tests/fm-brief.test.sh, new-tests hunk — 96 lines vs 19. Kept both sides.Not a semantic conflict at all: both sides appended different new test functions at the same file position. Ours (pre-flight section ×3, blast-radius framing ×1) and upstream's (
test_scout_and_secondmate_scaffold) all survive. The runner list at the bottom of the file had already auto-merged to include all of them.AGENTS.mdauto-merged with all four of our fork lines intact (verified individually): the gate-response policy, the blast-radius line, the pre-flight harvest, and the pre-flight recording instruction.The headline fix — and exactly what it covers
ab8cea6"fix(watcher): bound stale wakes for parked crew (kunchenguid#743)" is included. As the report said,crew_absorb_classis byte-identical between us and upstream; the fix is in the caller,pause_state_classinbin/fm-watch.sh.Against our two live cases, it is a partial fix. Evaluated the merged predicate against the real status lines:
spec31d-impl)needs-decisioncrew-absorb-cancelled-run)pausedstatus_is_paused_or_captain_heldadmits exactly two verbs —pausedandcaptain-held. Aneeds-decision:line is rejected at the gate and early-returns straight intocrew_absorb_class, which returnsnone; the new dead-agent upgrade sits downstream of that early return and never executes. So our most expensive case — a worker parked on a captain decision — is untouched by this fix.For the
paused:case the fix engages, but only via[ "$class" = none ] && [ "$agent_alive" = dead ] && class=paused. Note the merge also adds an earlier short-circuit: for a non-secondmate window whose agent reads alive-or-unknown,pause_state_classnow returnsnonewhere our current code returnedpausedunconditionally in the fresh-recheck branch. For a live idle pane this is slightly more surfacing than today, not less. The gain is confined to the dead-agent tail.Net: upstream handles the dead-agent tail; it does not declare the wait at the source.
crew-absorb-cancelled-run(currently held) is not made redundant — the captain's choice between the two implementations still stands, and this merge does not pre-empt it.The other 42 commits, by area
skills/— the public installer surface — is completely unchanged (0 commits), as the report found.Of the four surfaces a running firstmate loads:
bin/.agents/skills/AGENTS.mdskills/Non-loaded:
tests/55 files (+12122),docs/25 files (+1802),.github/2 files (+264).Supervision and watcher robustness (the largest and most relevant cluster) — bounded stale wakes for parked crew; ordinary-vs-recovery wake distinction; enriched drained signals with bounded status context; AFK idle stalls and stale run attribution; continuous supervision across child cycles; safe teardown during watcher recovery; watcher process identity immune to Linux wall-clock changes.
Harness breadth — pi session-local Calm mode and its follow-ups (transcript rendering, duplicate replies, stale lock handling, redundant re-arms); Grok exit guidance; opencode busy-queued composer state; canonical operational-input classification across harnesses.
Delivery and forge — GitLab merge requests followed to merge; current PR head fetched for review diffs; every PR body compliance event executed.
Test infrastructure (most of the +12122) — canonical timed runner (
bin/fm-test-run.sh), portable shards with bounded local parallelism, concurrent isolation proof, pinned real-Herdr CI coverage.Herdr — optional presentation spaces, ordering while preserving focus, projected children grouped under owning parents.
Other — quota-aware dispatch profiles; built-in
ahoyrecap skill (new); Bearings partial-snapshot and main-inventory fixes; secondmate report guards and vocabulary; X-mode pending-mention dedupe.14 new
bin/scripts and 1 new skill (ahoy) arrive. Notequota-axibecomes a bootstrap tool — already installed here, so no new dependency blocker.Two things landing that deserve the captain's eye before merge
1. Primary-session delegation fence (
50cc24a, kunchenguid#854). The report described this as apermissions.denylist removing 18 tools from the tracked.claude/settings.json. That is not what lands. The tracked settings gain hooks only (+24 lines, nopermissionsblock); the script's own header states the deny list "must not be tracked". Enforcement is a runtimePreToolUsehook,bin/fm-subagent-pretool-check.sh, which denies delegation-shaped tool names by substring — the shipped stem list isagent subagent task workflow cron schedul worktree delegate spawn dispatch handoff remote sendmessage monitor— while exempting observe/stop tools so runaway work can always be inspected or ended.It is scoped to a genuine primary home (crew worktrees unaffected) and has an
FM_ALLOW_SUBAGENT=1escape hatch. Why it still matters: this home's/pr-review,/implement,/spec-discuss,/merge-train, and/followupsfan out sub-agents, anddata/captain.mdputs the pr-review↔implement loop at the centre of the delivery model. Whether that loop can still run from the primary session is the captain's call, not something this merge should decide silently.Also arriving on the same surface: a
SessionStartnudge hook and afm-continuity-pretool-check.shon theBashmatcher.2. Upstream issue kunchenguid#1188 — watchers and daemons stranded unhealthy after crossing the
fm_pid_identityformat change (a26b37c). That commit is inside this range. Worth confirming the supervision chain comes back healthy after the home picks this up.What this does not do
Stops 28 commits behind upstream. Closing the rest requires upstream's Bash 3.2 fix for
bin/fm-brief.shto land first — four competing PRs are open on issue kunchenguid#1179 (kunchenguid#1200, kunchenguid#1199, kunchenguid#1198, and others), none merged. No commits were cherry-picked; this is one contiguous merge, so no third divergence lineage is created.