sync: merge upstream/main into fork - #20
Merged
Merged
Conversation
* feat(backends): add cmux runtime backend (experimental) Session-provider-only adapter for cmux (bin/backends/cmux.sh), mirroring zellij/herdr structurally, wired into fm-backend.sh and fm-spawn.sh with --secondmate refused for now. Verified against the real cmux 0.64.17 app: send does not auto-submit, cwd is creation-time-frozen (zellij-shape, pwd-marker-probe workaround), close-surface refuses on a workspace's last surface (falls back to close-workspace), workspace ids do not survive a relaunch, and the control socket defaults to cmuxOnly access (requires a one-time password-mode setup, documented in docs/cmux-backend.md). Also found and fixed a live bug during development: read-screen fails on a surface that has never been written to, so liveness now uses list-panes instead. Fake-CLI unit suite (40 tests), a real-binary smoke test, and a full spawn/steer/peek/done/merge/teardown E2E pass against a real claude crewmate all pass, including the popup/second-Enter regression class. * no-mistakes(review): Harden cmux recovery and password parsing * no-mistakes(review): Harden cmux capture failure handling * no-mistakes(review): Mark cmux test scripts executable * no-mistakes(review): Scope cmux workspaces and teardown * no-mistakes(review): Captain, honor cmux password config override * no-mistakes(review): Captain, hash cmux home labels * no-mistakes(document): Sync cmux backend docs
* Add firstmate-coding-guidelines skill (AGENTS.md diet PR 0) Encodes the knowledge-placement decision tree, one-owner rule, and inline-stub pattern from the diet analysis so future contributions stop adding conditional detail inline. AGENTS.md gets one section-13 trigger line; fm-brief.sh's REPO argument has no reliable signal for "this is firstmate's own repo", so the load instruction goes in CONTRIBUTING.md's Development section instead of the scaffold. * no-mistakes(review): Captain, align tracked-material trigger scope * no-mistakes(document): Sync coding guidelines docs * no-mistakes(lint): Fix Markdown style issues
* feat: structural Stop-hook backstop for primary turn-end supervision fm-guard.sh is pull-based: it only warns when some other supervision script happens to run, so a primary session that ends a turn without re-arming the watcher and then runs no further fleet-touching command can sit blind for hours (the 2026-07-04 incident this fixes). Add bin/fm-turnend-guard.sh, a Claude Code Stop hook registered in the tracked .claude/settings.json, that fires on every primary turn end and blocks (exit 2, verified empirically to force continuation) when work is in flight with no fresh watcher beacon. It never blocks more than once per turn, using Claude Code's own stop_hook_active loop-guard field, and scopes itself to the actual primary checkout only (inert in crewmate/scout worktrees and secondmate homes). Factor the shared "in-flight but no live watcher" predicate out of fm-guard.sh into bin/fm-supervision-lib.sh so the pull-based banner and the push-based hook can never drift on what "unhealthy" means. Document the verified Stop-hook mechanism and scoping in docs/turnend-guard.md, add a harness-adapters note, and cover the predicate and hook with tests/fm-turnend-guard.test.sh. * no-mistakes(review): Respect active home in turnend guard * no-mistakes(review): Require live watcher for turn-end guard * no-mistakes(review): Captain: portable turn-end timing * no-mistakes(document): Sync turn-end guard documentation
* feat(backends): auto-detect cmux runtime from CMUX_WORKSPACE_ID Wires cmux into fm_backend_detect the same way herdr already is: a firstmate process running inside a cmux-spawned terminal now spawns new tasks into cmux by default, no config needed. Verified from cmux's own shipped source that CMUX_WORKSPACE_ID/CMUX_SURFACE_ID/CMUX_SOCKET_PATH are unconditionally, non-overridably injected into every terminal surface it spawns, and that cmux's own CLI treats CMUX_WORKSPACE_ID as its own ambient-target fallback - the same role $TMUX/HERDR_ENV play for their backends. CMUX_WORKSPACE_ID is checked last (after $TMUX and HERDR_ENV=1) since cmux is a terminal application, not a nestable multiplexer. Socket auth (config/cmux-socket-password) stays required regardless of how the backend was selected; the existing spawn refusal now also names the config/backend=tmux / --backend tmux opt-out for a caller who never explicitly chose cmux. A live env dump inside a real cmux terminal was not obtained safely on the shared dev machine (documented in docs/cmux-backend.md); this rests on the source read instead, mirroring this doc's existing verified-from-source precedent. * no-mistakes(review): Fix cmux autodetect docs and tests * no-mistakes(document): Document cmux auto-detection
* fix(afk): make the away-mode daemon backend-aware for herdr bin/fm-supervise-daemon.sh discovered its supervisor pane and injected via raw tmux calls only, so /afk failed outright on a herdr-based fleet (TMUX_PANE unset, firstmate:0 fallback unresolvable). Discovery now resolves backend (tmux|herdr) and target independently, mirroring fm-backend.sh's own runtime auto-detection, with an explicit FM_SUPERVISOR_BACKEND override alongside the existing FM_SUPERVISOR_TARGET. zellij/orca refuse loudly at startup instead of misapplying tmux primitives. Injection (pane-exists probe, busy-guard, composer-guard, verified submit) now dispatches through bin/fm-backend.sh's generic primitives, adding a new fm_backend_composer_state dispatcher; the tmux path is byte-identical to before. Also fixes a pre-existing bug in fm_backend_target_exists's herdr arm (missing --session, so it silently misrouted once more than one herdr server was running) found while verifying this end to end against a real isolated herdr session. Classification, batching, max-defer, the marker contract, locks, and wake-queue handling are unchanged - this is a transport-layer fix. * no-mistakes(review): Corroborate Herdr idle busy state * no-mistakes(review): Stabilize Herdr daemon startup wait * no-mistakes(review): Captain, route cmux composer and update AFK docs * no-mistakes(document): Document AFK supervisor backend support
* docs(agents): collapse X-mode section 14 into fmx-respond/docs pointers
AGENTS.md diet PR 1 of 3 (agentsmd-diet-s2 report, move-plan items 1-2).
Replaces section 14's "Answering"/"Completion follow-up"/"Conversations"/
"Length and threads"/"Preview / dry-run" blocks (54 lines) and the
"Mechanism" narrative (6 lines) with two short pointers: fmx-respond
(section 13) for the procedure, docs/configuration.md "X mode (.env)"
for the wire protocol. Net -55 lines in AGENTS.md.
Destination edits landed first, deletions second (q4 discipline):
- docs/configuration.md: added the "purely additive, watcher untouched"
guarantee that AGENTS.md's Mechanism block stated but configuration.md
did not.
- fmx-respond/SKILL.md: added the x-mode-error wake boundary (report as
a blocker, do not load this skill), the --image flag for replies and
follow-ups, the "images are for real artifacts, not prose" rule, and
the dry-run compact-image-marker behavior - none of these were
previously in the skill even though AGENTS.md described them, so they
were genuine gaps, not pre-existing duplication. Also made the skill's
own "Completion follow-up" section the sole, full owner of that
procedure instead of deferring to AGENTS.md section 14 for substance
that no longer lives there (two internal cross-references updated to
point at section 8's terminal-wake trigger and the skill's own section
instead).
Mechanical line-by-line audit of every removed AGENTS.md line:
Mechanism block (6 lines removed):
- bootstrap artifact-writing description -> already owned by
docs/configuration.md "X mode (.env)" (locked-bootstrap paragraph)
- check-shim/poll mechanism description -> already owned by
docs/configuration.md same section
- missing-deps/x-mode-error diagnostic description -> already owned by
docs/configuration.md ("Relay auth or config problems...") plus
bin/fm-x-poll.sh's own header comment for the missing-curl/jq mechanics
- opt-out artifact removal description -> already owned by
docs/configuration.md same section
- "purely additive, no edit to fm-watch.sh/fm-watch-arm.sh/fm-wake-lib.sh/
afk daemon" guarantee -> MOVED to docs/configuration.md (added in this
PR; this fact had no other home before)
Answering/Completion follow-up/Conversations/Length and threads/
Preview-dry-run blocks (54 lines removed):
- x-mention wake -> load fmx-respond: already owned by section 13's
existing trigger line (unchanged) and restated in the new pointer
- x-mode-error wake -> report as blocker, don't load fmx-respond: MOVED
to fmx-respond/SKILL.md (added in this PR)
- inbox-draining, classification, acting, reply composition, submission,
cleanup-on-success/failure: already owned by fmx-respond/SKILL.md
"Procedure" section (unchanged, pre-existing)
- owner-only routing / captain-as-asker framing: already owned by
fmx-respond/SKILL.md "The asker is your own captain" section
- standing X-mode authorization / autonomous posting / dry-run as only
non-posting path: already owned by fmx-respond/SKILL.md same section
- acknowledge-first -> act -> follow-up shape, three-case classification:
already owned by fmx-respond/SKILL.md "A request to act on" section
- destructive/irreversible/security-sensitive escalation guardrail:
already owned by fmx-respond/SKILL.md "Public channel..." section and
Procedure step 2c
- dismiss-instead-of-reply for pure acknowledgments, relay re-offer
prevention, dry-run honoring: already owned by fmx-respond/SKILL.md
Procedure steps 2b/2c/2e-skip and docs/configuration.md
- public-safety bar (no task ids/internals/captain-private/secrets):
already owned by fmx-respond/SKILL.md "The reply is public" section
- never-inline-into-shell-command / --text-file or stdin: already owned
by fmx-respond/SKILL.md Procedure step 2e and Notes
- --image flag for replies (formats, base64, no-inline guarantee): MOVED
to fmx-respond/SKILL.md Procedure step 2e (added in this PR - this was
not previously in the skill)
- fm-x-link field names (x_request=, x_request_ts=, x_followups=):
already owned by AGENTS.md section 2's state/<id>.meta field list
(untouched, out of scope for this PR) and fmx-respond/SKILL.md
- carry-count/carry-ts relink behavior, three-follow-up budget, milestone
sparingness, --check/--text-file posting, connector/followup wire
detail, --final clearing, cap/window graceful degradation: already
owned by fmx-respond/SKILL.md "Completion follow-up" section (now sole
owner) and docs/configuration.md wire-protocol paragraphs
- --image flag for follow-ups: MOVED to fmx-respond/SKILL.md "Completion
follow-up" section (added in this PR - genuine gap)
- "failed task still gets an honest final follow-up": already owned by
fmx-respond/SKILL.md "Completion follow-up" section
- FMX_DRY_RUN whole-loop previewability: already owned by
fmx-respond/SKILL.md "Dry-run / preview mode" section
- in_reply_to conversation continuity, untrusted-thread handling,
follow-up worthiness judgment, relay-owned self-reply guard/cap:
already owned by fmx-respond/SKILL.md "The direct ask is the captain's"
section and Notes (one bullet is a verbatim match)
- concise-by-default / no hand-numbered threads: already owned by
fmx-respond/SKILL.md "Voice" section
- auto-split behavior, char/tweet caps, premium-independence, wire shape
({text}/{text,texts}): behavior already owned by fmx-respond/SKILL.md
Voice section; exact defaults and wire shape already owned by
docs/configuration.md; "premium-independent" mechanics already owned
by bin/fm-x-reply.sh's own header comment
- "images are for real artifacts, not prose": MOVED to fmx-respond/
SKILL.md "Voice" section (added in this PR - genuine gap)
- image-on-thread wire behavior: already owned by docs/configuration.md;
reinforced in fmx-respond/SKILL.md's new --image note
- dry-run POST-body shape, endpoint marker, truthy-value definition,
jq-only dependency, end-to-end testability, x-outbox inspection:
already owned by fmx-respond/SKILL.md "Dry-run / preview mode" section
(several near-verbatim matches) and docs/configuration.md wire detail
- dry-run compact image marker: MOVED to fmx-respond/SKILL.md "Dry-run /
preview mode" section (added in this PR - genuine gap)
Section 8's terminal-wake completion-follow-up trigger (the one fact
required to survive inline) is untouched and already present; the new
section 14 pointer references it instead of restating it.
Nothing outside section 14 (plus the two destination files) is touched.
Full test suite green, including all 74 fm-x-mode.test.sh checks.
* no-mistakes(review): Preserve X-linked follow-up triggers
* no-mistakes(review): Fix x-mode error trigger
* no-mistakes(document): Docs cross-reference synchronized
* no-mistakes(lint): Clean Markdown lint pass
* docs(agents): trim section 4 harness/secondmate duplication AGENTS.md diet PR 2 of 3 (data/agentsmd-diet-s2/report.md, move-plan items 3-4; redundancy item 2 folded into item 3). Removed the five claude/codex/grok/pi/opencode model/effort-flag bullets from section 4 - byte-for-byte duplicated by harness-adapters' "Launch profile axes" table (which is already a superset: it carries verified CLI versions per adapter that the AGENTS.md bullets lacked). Replaced with a one-line pointer; the skill is already loaded before every spawn per section 4's own closing trigger, so no new trigger was needed. Moved the config/secondmate-harness model/effort pin-format detail (the `<harness> [<model>] [<effort>]` line format, the secondmate-model/secondmate-effort accessors, back-compat, and the durability-across-respawn behavior) into secondmate-provisioning, which is already a mandatory load at every secondmate lifecycle touchpoint. Added the destination content to the skill first, then replaced the AGENTS.md paragraph with a 3-line pointer. Mechanical audit - every removed line's new home: - 5 harness bullets (claude/codex/grok/pi/opencode model+effort flags, per-harness max-omission rationale) -> already present in harness-adapters SKILL.md's "Launch profile axes" table (lines 53-59), confirmed fact-by-fact before deleting. - "config/secondmate-harness may also pin..." paragraph (pin format, bare-harness back-compat, secondmate-model/secondmate-effort accessors, per-spawn override precedence, respawn durability, secondmate-only scope) -> secondmate-provisioning SKILL.md's "Charter and seed" section, added verbatim before this trim. - The following paragraph (inheritable config: crew-dispatch.json, crew-harness, backlog-backend) is untouched - out of scope for this PR, still inline. - The bootstrap CREW_DISPATCH effort-mismatch diagnostic sentence is untouched - not part of the five-bullet duplication, stays inline. No script changes. Section 4 shrinks from 104 to 87 lines (958 -> 901 total AGENTS.md lines) with zero facts lost: every fact is reachable through harness-adapters or secondmate-provisioning, both already mandatory loads at the relevant lifecycle points. * no-mistakes(document): Align secondmate skill triggers * no-mistakes(lint): Markdown style clean
* Fix turn-end Stop hook to use CLAUDE_PROJECT_DIR path Claude Code runs hook commands via /bin/sh from the session cwd, so the bare relative bin/fm-turnend-guard.sh path fails when cwd is not the repo root. Anchor the command with "$CLAUDE_PROJECT_DIR"/bin/fm-turnend-guard.sh instead; verified CLAUDE_PROJECT_DIR is set on Stop hooks in Claude Code 2.1.201. Document the cwd caveat and add a settings.json regression test. * no-mistakes(document): Document Stop hook path anchoring
* docs: trim AGENTS.md redundancy (diet PR 3/3) Consolidates five duplicated passages to a single owner each, per data/agentsmd-diet-s2/report.md redundancy items c3-c7: - Inheritable-config propagation mechanism: owned by section 3 (where the sweep runs); sections 4 and 7 keep compact references. Section 4 retains its one genuinely unique fact (crew-harness inherit-vs-fallback semantics), just no longer restates the propagation mechanism itself. - Landed-work definition: owned by section 7's ship-teardown detail (PR-containment mechanics, pr= discovery fallback); section 1's hard rule #3 keeps the rule plus a three-case summary and a pointer. - Backend meta-field enumeration: owned by docs/configuration.md ("Runtime backend", already comprehensive including cmux) and each backend's own doc; AGENTS.md keeps only the fields common to every task plus a pointer. - Dropped one redundant restatement of "silence is correct while waiting" in section 8. - Worktree-tangle guard explanation: owned by section 8 (already the fuller, cross-referenced version); section 3's TANGLE bullet keeps the remediation action and points at section 8 for the why. Also adds two captain-requested single-sentence rules: invoke bin/ scripts by absolute $FM_ROOT path after any cd away from the home, and a backend spawn refusal must be surfaced to the captain rather than silently worked around by switching backends. AGENTS.md: 901 -> 889 lines, 112355 -> 108560 bytes. * no-mistakes(review): Clarify post-cd bin invocation guidance * no-mistakes(document): Sync AGENTS trim docs * no-mistakes(lint): Fix Markdown line style
…henguid#259) * feat(backends): cmux detection fallbacks and socket-mode matrix Workstream A: cmux's bundled claude wrapper strips every CMUX_* env var on its passthrough path (reproduced live 2026-07-04, cmux 0.64.17), so a claude-harness firstmate inside a cmux tab has no CMUX_WORKSPACE_ID. fm_backend_detect now falls back - macOS-only, only when the primary marker is absent - to __CFBundleIdentifier=com.cmuxterm.app and then a process ancestry walk resolved by bundle id (lsappinfo) plus a bundle-shaped ps comm match. Innermost-first ordering is unchanged and absorbs the tmux-inside-cmux bundle-id false positive; the auto-detect NOTICE names the winning fallback signal. Workstream B: the five socketControlMode values were traced through cmux source (commit 9c91710e3f58): off/cmuxOnly can never admit an external CLI, automation admits same-user clients with no secret (0600 socket only), password needs the auth handshake, allowAll opens the socket to every local user (0666). Automation mode is now the documented recommendation; the adapter's refusals name every viable mode, classify Invalid password as unauth, and the launch-timeout message names the off-mode possibility. Docs carry the wrapper-strip empirical record, the fallback contract and authority split, and the full mode matrix with rationale; tests cover the new detection paths, the nested false positive, and the refusal wording. * no-mistakes(review): Document cmux fallback detection * no-mistakes(review): Update cmux architecture docs * no-mistakes(document): Align cmux backend docs
* fix(backends): home-scope zellij tab titles to close cross-home collision gap Zellij's one shared "firstmate" session has no per-home split and enforces no tab-name uniqueness, so two firstmate homes with colliding task ids could send/peek/close each other's tabs - the same gap a no-mistakes review gate caught for cmux (docs/cmux-backend.md). Ports that fix: every new tab is created with a home-scoped title (fm-<home-label>-<id>), and every list/find/recover/kill path scopes matches to this home's own tag. A tab spawned before this change still matches via its old untagged bare title, but only when unambiguous - two live tabs sharing a bare title refuse rather than guessing which one is ours. Factors the home-label/hash derivation shared with cmux into bin/fm-backend-hometag-lib.sh so the two adapters can't drift. * no-mistakes(review): Fix zellij child teardown home tag * no-mistakes(review): Fix zellij teardown and selector scoping * no-mistakes(document): Sync zellij home-scope docs
* fix(fleet-sync): auto-sync on merged-PR wake, accept project name
fm-fleet-sync.sh's single-project form failed on a bare project name
("not a directory"), forcing hand-typed full paths (4 manual runs in
one incident). It now resolves a bare name or projects/<name> against
the home's projects dir.
AGENTS.md now encodes the trigger: a wake whose status reports a
merged PR for a project cloned in this home runs fleet-sync for that
project as part of handling the wake, so a secondmate-reported merge
does not leave the primary's clone stale until the next session start
or teardown.
* no-mistakes(review): Fix fleet-sync project name shadowing
* no-mistakes(document): sync fleet-sync docs
* fix(spawn): canonicalize worktree-isolation guard against symlinked project prefixes fm-spawn.sh compared a logical PROJ_ABS against the physically-resolved pane cwd every backend reports, so a project reached through a symlinked prefix (e.g. macOS's /tmp -> /private/tmp) could trip the isolation guard's false refusal before treehouse ever moved the pane. Canonicalize once into PROJ_ABS_REAL and compare against that everywhere instead. * no-mistakes(review): Canonicalize spawn cwd comparisons * no-mistakes(document): Refresh symlinked spawn docs
* docs: add Orca operator skill * no-mistakes(document): Document Orca checklist --------- Co-authored-by: Stephen Brouhard <vesta@stephens-macbook-air.tail2122af.ts.net>
* fix(crew-state): detect green-PR CI monitoring, escalate repeat wedges fm-crew-state.sh's ci step never distinguishes "still waiting on checks" from "checks green, waiting on merge" via axi status alone, since a repo that defers merge to the captain keeps the ci step at status=running for the whole monitor phase. Read the ci step's own log tail (axi logs) for the checks-passed marker and surface done instead of a false "validating (running)" - verified against the real PR kunchenguid#252 run's ci.log. The watcher's wedge timer can re-escalate the same stale pane forever without ever signaling that it is a repeat; track a per-pane consecutive escalation count and add a demand-deep-inspection marker to the wake payload once it crosses a threshold, so the supervisor can no longer dismiss each one as an isolated, still-validating pane. Also clarify the ship-brief's checks-green line: it is owed at the CI-ready return point, not after the background monitor-until-merge loop finishes. * no-mistakes(review): Captain, distinguish pending no-checks CI marker * no-mistakes(review): Harden CI relapse handling * no-mistakes(review): Block stale done during fixing * no-mistakes(review): Captain, tighten CI status gating * no-mistakes(review): Captain, harden stale CI green handling * no-mistakes(review): Captain, recognize ranged CI rearm markers * no-mistakes(document): Sync crew-state supervision docs
* fix(teardown): recover from a stale worktree git index.lock A crew process killed mid-git-operation can leave a stale .git/worktrees/<wt>/index.lock behind, making fm-teardown.sh's `treehouse return --force` fail closed. On that failure, retry once after a short wait (the owning process may be exiting), then remove the lock and retry once more only when it is provably stale: old enough by mtime and lsof shows no live holder on the lock or the worktree itself. A lock that isn't provably stale is left in place and the original failure still surfaces. * no-mistakes(review): Harden teardown lock refusal paths * no-mistakes(review): Harden stale-lock teardown safety rechecks * no-mistakes(review): Harden stale teardown lock checks * no-mistakes(document): Document teardown lock recovery
…-governance section (kunchenguid#307) * Encode project AGENTS authoring bar * no-mistakes(review): Captain, centralize CLAUDE promotion governance * no-mistakes(review): make ensure_maintenance_section idempotent-success, drop || true guards * no-mistakes(review): separate appended maintenance section on newline-less CLAUDE.md promotion * no-mistakes(review): assert maintenance heading present before separator check in test * no-mistakes(document): sync docs with AGENTS.md authoring bar and self-governance --------- Co-authored-by: fmtest <fmtest@example.invalid>
…chenguid#300) * Add captain-invocable bearings skill Generates a pick-up-where-I-left-off status report from live fleet state to data/status-report-<YYYY-MM-DD>.md plus a concise chat summary. Read-mostly procedure: reads backlog, per-task crew state via bin/fm-crew-state.sh, open PRs via gh-axi, scout reports, pending decisions, and date-gated queued work; composes the exemplar's sections (TL;DR, Check first, Landed, In flight, Plans, Decisions pending, Date-gated/queued); never tears down, merges, or mutates task state as a side effect. * no-mistakes(document): docs: list new /bearings skill in README built-in skills table
* fix(watcher): pin LC_ALL=C in fm_pid_identity for locale-invariant identity ps's lstart date format follows the caller's LC_TIME/LC_ALL. The watcher records its process identity under one locale, but arm/guard/turn-end re-read it under the machine's ambient locale. On a non-C locale (e.g. ko_KR) the two strings differ only in the date portion, so fm_watcher_lock_matches_pid / fm_watcher_healthy reject a genuinely live watcher - breaking fm-watch-arm.sh, fm-guard.sh, and fm-turnend-guard.sh on every non-C-locale machine. Pin LC_ALL=C on that one ps call so the write and read sides agree regardless of machine locale, matching the LC_ALL=C determinism the file already uses elsewhere. Add a colocated regression test asserting fm_pid_identity is locale-invariant across exported LC_ALL/LC_TIME. * no-mistakes(document): Document watcher PID identity coverage
* docs: reconcile Codex App backend contract * no-mistakes(document): Sync backend docs * docs: clarify Codex Desktop bridge blocker * no-mistakes(document): Align Codex App backend docs * no-mistakes(test): Captain, stabilize watcher self-eviction test cadence * no-mistakes(document): Document Codex App backend contract * no-mistakes(document): Captain, document blocked codex-app coverage * docs: make Codex App contract doc authoritative * no-mistakes(document): Align Codex App backend docs * docs: redact local Codex App smoke paths --------- Co-authored-by: Stephen Brouhard <vesta@stephens-macbook-air.tail2122af.ts.net>
* docs: add Codex App coordination skill * no-mistakes(review): Captain, mark Codex App skill agent-only * no-mistakes(document): Document Codex App backend boundary * no-mistakes(document): Captain, document Codex Desktop backend boundary * no-mistakes(lint): Captain, lint clean * no-mistakes(document): Document Codex Desktop boundaries * docs: narrow Codex App skill playbook
* fix(afk): recognize unbordered herdr composer rows to stop escalation redelivery loop fm_backend_herdr_composer_state only recognized bordered composer rows (the grok shape). Real claude and codex render their live input row with no border at all, so once a harness's own startup banner scrolled out of the capture window the classifier read the composer as unknown forever. fm_backend_herdr_send_text_submit never confirmed "empty", so escalate_flush never cleared state/.subsuper-escalations, and the away-mode daemon retyped and resubmitted the same buffered digest every housekeeping cycle - reproduced live against a real herdr+claude pane (5+ identical deliveries in 40s). The classifier now recognizes an unbordered (bare) composer row led by a known prompt glyph alongside the existing bordered shape, keeping whichever match is bottom-most so a stale decorative box never outranks the live composer. * no-mistakes(review): Narrow herdr bare prompt matcher * no-mistakes(document): Sync herdr composer docs
…guid#323) * fix(herdr): confirm message submit via native agent-state, not composer text fm_backend_herdr_send_text_submit now confirms a landed submit by polling herdr's own agent-state (agent get) for the idle->working transition instead of reading composer content. Composer scraping remains, unchanged, for the away-mode daemon's pre-injection empty-box guard only. This fixes the practical effect of the codex idle-tip gap from the 2026-07-07 incident: codex's dynamic idle-composer hint text can no longer misread as pending and block/mis-confirm a send, since confirmation no longer looks at composer text at all. Verified empirically against real claude and codex agents (timing, swallowed-Enter, unreadable-target, and already-busy-target scenarios), and against the real away-mode daemon end-to-end after updating its synthetic supervisor-pane test fixture to register itself as a real herdr agent (herdr's own report-agent primitive) so it can still exercise the new confirmation path. * no-mistakes(review): Captain, harden herdr submit confirmation * no-mistakes(review): Captain, harden herdr submit confirmation * no-mistakes(document): Sync Herdr submit docs * no-mistakes: apply CI fixes
* Add quota-balanced dispatch selection * no-mistakes(document): Document dispatch selector guidance
* fix(session-start): deterministically respawn dead-shell secondmates A secondmate agent that exits leaves its backend pane alive as a bare shell. The session-start endpoint check only verified pane presence, so recovery and the watcher (which exempts secondmates from stale-pane detection) never noticed - evidence 2026-07-07: every secondmate in one fleet was found sitting at a dead zsh shell. Add fm_backend_agent_alive (bin/fm-backend.sh), a deeper per-backend liveness probe distinct from pane presence: fm_backend_tmux_agent_alive classifies the pane's live foreground process via tmux's own pane_current_command, and fm_backend_herdr_agent_alive reuses the already-verified pane_agent_state husk classifier. Both are conservative: anything ambiguous reports unknown, never a false dead. Wire this into a new session-start-only, locked-and-primary-only sweep in bin/fm-bootstrap.sh that kills and respawns only a confidently dead secondmate endpoint, leaving alive/unknown readings untouched - idempotent by construction, so repeated runs converge without duplicating agents. * no-mistakes(review): Guard raw secondmate liveness respawns * no-mistakes(review): Fix detect-only bootstrap test * no-mistakes(test): Pin liveness fixture harness * no-mistakes(document): Sync secondmate liveness docs * no-mistakes: apply CI fixes
* Fix NUDGE_SECONDMATES to print stable fm-<id> selectors. Session-start secondmate sync used to accumulate raw backend window targets into NUDGE_SECONDMATES, but the liveness sweep in the same bootstrap run can respawn secondmates onto new endpoints. fm-send with those stale explicit targets bypasses meta resolution and fails, while fm-<id> resolves correctly. Accumulate fm-<id> in process_secondmate, update the bootstrap/update contracts and /updatefirstmate skill, and add a herdr respawn regression test. * no-mistakes(review): Captain, guard herdr regression jq dependency * no-mistakes(document): Document stable secondmate nudge selectors * no-mistakes(lint): Fix shell lint hints
* Make tasks-axi and quota-axi required bootstrap tools Add both to the normal toolchain checks alongside lavish-axi, keep the tasks-axi 0.1.1+ compatibility gate, and report quota-axi through the standard MISSING install-consent flow. TASKS_AXI: available remains a backlog-backend capability signal only; manual opt-out no longer suppresses the missing-tool report. Update bootstrap tests and point docs/configuration.md at the canonical toolchain contract. * no-mistakes(review): Clarify manual backlog bootstrap reporting * no-mistakes(document): Document bootstrap AXI tools
Replace overwrite-in-place wording with explicit delete-then-create instructions so agents do not modify an existing daily report file.
* Add primary turn-end guards for all harnesses * no-mistakes(review): Normalize Codex hook cwd resolution * no-mistakes(review): Fix OpenCode guard worktree anchoring * no-mistakes(review): Anchor Codex guard outside nested roots * no-mistakes(review): Anchor Codex guard to hook root * no-mistakes(review): Avoid Grok permission escalation * no-mistakes(document): Sync turn-end guard docs
* fix backend selector task id resolution * no-mistakes(document): Document selector resolution behavior
…id#967) * fix(herdr): reclaim resumed task projections safely * no-mistakes(review): Enforce safe Herdr reclaim close boundaries * no-mistakes(document): docs: clarify Herdr restart projection contract
…id#994) * docs: separate current guides from verification * no-mistakes(review): Restore Herdr 0.7.5 restart-reclaim verification evidence
…d#997) * feat(claude): Stop-owned tokenless watcher continuity via asyncRewake auto-arm Claude primaries (main home and marked secondmate homes) no longer depend on the model remembering to re-arm the watcher after each wake. A tracked Stop asyncRewake hook (bin/fm-claude-stop-autoarm.sh, timeout 28800s) fires on every turn end, claims one home-scoped single-flight owner, foregrounds bin/fm-watch-arm.sh inside the hook-owned process tree, and translates an actionable close or typed watcher failure into exactly one exit-2 rewake. The hook scopes to genuine primary checkouts, requires the session lock to be held by its own harness ancestor, stays inert while AFK owns triage or the home is idle, and hands AFK transitions mid-cycle to the daemon without rewaking. The synchronous turn-end guard gains a --claude cooperative mode: it ignores stop_hook_active (true on every post-continuation stop, which is what re-opened the 2026-07-21 blind window), waits briefly for a watcher health proof, a live auto-arm owner claim, or a fresh rewake epoch, and re-blocks only when the auto-arm genuinely failed to establish - bounded to 3 consecutive blocks per session, safely below Claude Code's 8-block override, then a degraded allow with a visible systemMessage. Codex keeps the previous one-block loop guard byte-identically, and Pi, OpenCode, and Grok adapters are untouched. Continuity PreToolUse gate and durable wake queue are preserved; the gate's recovery guidance now names the Stop-owned re-arm and reserves manual background arms for auto-arm failure. Claude supervision protocol, harness-adapters facts, architecture, configuration, and continuity docs updated; docs/turnend-guard.md records the 2026-07-24 Claude 2.1.218 contract revalidation (tokenless multi-cycle rewake, no-dedup, timeout process-group kill, 8-block cap, interactive non-stall) and the 2.1.219 product live E2Es. Regression matrix: hermetic tests cover scope, identity, AFK, need, single-flight, translation, guard cooperation, budget, and registration; the new live E2E proves two full tokenless auto-arm rewake cycles with zero model arm commands; Pi and OpenCode Option B live E2Es pass unchanged. * no-mistakes(review): Fix Claude X-mode auto-arm continuity backstop * no-mistakes(review): Remove unsupported Claude contract-lab verification claims * no-mistakes(document): Update Claude auto-arm continuity documentation
* Clean stale Herdr projections at session start * no-mistakes(document): Document stale Herdr session-start projection cleanup * no-mistakes(review): Enforce locked exact Herdr projection cleanup * no-mistakes(review): Fail closed on unverified session lock ownership * no-mistakes(review): Serialize session lock acquisition atomically * no-mistakes(document): Align session-start and Herdr cleanup documentation * no-mistakes(document): Generalize lock-refusal diagnostics * no-mistakes(lint): Avoid reserved keyword in concurrency test * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes
…uid#1001) * fix: recover Claude supervision at session start * fix: remove Claude watcher-status command gate * no-mistakes(document): docs: remove stale continuity gate references
* Replace quota dispatch selector instructions * no-mistakes(review): Align bootstrap docs with agent-owned dispatch selection
* remove vestigial dispatch selector * no-mistakes(review): Synchronize isolation proof and portable shard evidence * no-mistakes(review): Correct shard history and proof archive date * no-mistakes(review): Remove reintroduced selector documentation reference * no-mistakes(document): Remove stale dispatch strategy documentation
# Conflicts: # .agents/skills/afk/SKILL.md # .agents/skills/bearings/SKILL.md # .agents/skills/bootstrap-diagnostics/SKILL.md # .agents/skills/decision-hold-lifecycle/SKILL.md # .agents/skills/firstmate-coding-guidelines/SKILL.md # .agents/skills/firstmate-orca/SKILL.md # .agents/skills/harness-adapters/SKILL.md # .agents/skills/project-management/SKILL.md # .agents/skills/secondmate-provisioning/SKILL.md # .agents/skills/stuck-crewmate-recovery/SKILL.md # .agents/skills/updatefirstmate/SKILL.md # .claude/settings.json # .codex/hooks.json # .github/workflows/ci.yml # .gitignore # .no-mistakes.yaml # .opencode/plugins/fm-primary-turnend-guard.js # .opencode/plugins/fm-primary-watch-arm.js # .pi/extensions/fm-primary-pi-watch.ts # .pi/extensions/fm-primary-turnend-guard.ts # AGENTS.md # CONTRIBUTING.md # README.md # bin/backends/herdr.sh # bin/backends/tmux.sh # bin/fm-afk-launch.sh # bin/fm-afk-return.sh # bin/fm-backend.sh # bin/fm-bearings-snapshot.sh # bin/fm-bootstrap.sh # bin/fm-brief.sh # bin/fm-classify-lib.sh # bin/fm-config-inherit-lib.sh # bin/fm-config-push.sh # bin/fm-decision-hold.sh # bin/fm-fleet-snapshot.sh # bin/fm-fleet-view.sh # bin/fm-gate-refuse-lib.sh # bin/fm-guard.sh # bin/fm-install-shellcheck.sh # bin/fm-lint.sh # bin/fm-marker-lib.sh # bin/fm-pr-check-migrate.sh # bin/fm-pr-check.sh # bin/fm-pr-lib.sh # bin/fm-pr-merge.sh # bin/fm-pr-poll.sh # bin/fm-project-mode.sh # bin/fm-send.sh # bin/fm-session-start.sh # bin/fm-spawn.sh # bin/fm-supervise-daemon.sh # bin/fm-supervision-instructions.sh # bin/fm-supervision-lib.sh # bin/fm-teardown.sh # bin/fm-tmux-lib.sh # bin/fm-turnend-guard-grok.sh # bin/fm-turnend-guard.sh # bin/fm-wake-lib.sh # bin/fm-watch-arm.sh # bin/fm-watch.sh # bin/fm-x-lib.sh # bin/fm-x-poll.sh # docs/architecture.md # docs/arm-pretool-check.md # docs/cmux-backend.md # docs/codex-app-backend.md # docs/configuration.md # docs/decision-hold-lifecycle.md # docs/examples/crew-dispatch.json # docs/herdr-backend.md # docs/orca-backend.md # docs/scripts.md # docs/supervision-protocols/claude.md # docs/supervision-protocols/codex.md # docs/supervision-protocols/grok.md # docs/supervision-protocols/opencode.md # docs/supervision-protocols/pi.md # docs/supervision-protocols/unknown.md # docs/tmux-backend.md # docs/turnend-guard.md # docs/wedge-alarm.md # docs/zellij-backend.md # tests/fm-afk-inject-herdr-e2e.test.sh # tests/fm-afk-launch.test.sh # tests/fm-afk-pi-herdr-return-e2e.test.sh # tests/fm-afk-return.test.sh # tests/fm-arm-pretool-check.test.sh # tests/fm-backend-cmux-smoke.test.sh # tests/fm-backend-cmux.test.sh # tests/fm-backend-herdr-eventwait-smoke.test.sh # tests/fm-backend-herdr-prune-safety-e2e.test.sh # tests/fm-backend-herdr-respawn-idem-e2e.test.sh # tests/fm-backend-herdr-smoke.test.sh # tests/fm-backend-herdr-workspace-per-home-e2e.test.sh # tests/fm-backend.test.sh # tests/fm-backlog-handoff.test.sh # tests/fm-bearings-snapshot.test.sh # tests/fm-bootstrap.test.sh # tests/fm-brief.test.sh # tests/fm-captain-translation-contract.test.sh # tests/fm-cd-pretool-check.test.sh # tests/fm-composer-lib.test.sh # tests/fm-crew-state.test.sh # tests/fm-daemon.test.sh # tests/fm-decision-hold-lifecycle.test.sh # tests/fm-fleet-snapshot-view.test.sh # tests/fm-fleet-sync.test.sh # tests/fm-gate-refuse.test.sh # tests/fm-herdr-lab.test.sh # tests/fm-instruction-owners.test.sh # tests/fm-lint.test.sh # tests/fm-pi-primary-live-e2e.test.sh # tests/fm-pi-primary-types.test.sh # tests/fm-pi-watch-extension.test.sh # tests/fm-pr-check-security.test.sh # tests/fm-secondmate-harness.test.sh # tests/fm-secondmate-liveness.test.sh # tests/fm-secondmate-sync.test.sh # tests/fm-send-secondmate-marker-herdr-e2e.test.sh # tests/fm-send-secondmate-marker.test.sh # tests/fm-session-start.test.sh # tests/fm-shared-captain-inheritance.test.sh # tests/fm-spawn-dispatch-profile.test.sh # tests/fm-supervision-events.test.sh # tests/fm-supervision-instructions.test.sh # tests/fm-tangle-guard.test.sh # tests/fm-teardown.test.sh # tests/fm-transition-lib.test.sh # tests/fm-turnend-guard.test.sh # tests/fm-watch-triage.test.sh # tests/fm-watcher-lock.test.sh # tests/fm-x-mode.test.sh # tests/herdr-test-safety.sh
Restore fm_supervision_status(), dropped by the conflict resolution during the upstream merge, so bin/fm-turnend-guard.sh actually computes supervision state instead of silently allowing every blind stop. Also split a comment line that had swallowed the guard's set -u, which was masking the missing function instead of crashing loudly on it. Remove the duplicate secondmate_liveness_sweep call in bin/fm-bootstrap.sh left behind when the merge kept both parents' conflicting orderings of secondmate_sync and secondmate_liveness_sweep.
fm_backend_tmux_container_ensure queried the current tmux session with an untargeted display-message, which falls back to tmux's ambient active-client guess instead of this process's own pane; target it at TMUX_PANE (the stable pane id) instead, matching the stable-id pattern already used for window targeting. bin/fm-bootstrap.sh ran secondmate_sync before secondmate_liveness_sweep, so sync's reread nudge could fire before a dead secondmate's respawn completed. Upstream runs liveness_sweep first so SECONDMATE_RESPAWNED_IDS is populated before sync reads it; adopt that order.
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.
Summary
Merges canonical upstream/main into the fork while preserving fork-specific base-branch enforcement, durable decision-hold behavior, crash-durable briefs, and the /explain skill.
Validation scope
fm-pr-check-securityhangs.