Skip to content

fix: enforce contract boundaries for ask-user findings - #945

Merged
kunchenguid merged 2 commits into
mainfrom
fm/fm-ask-user-complexity-escalation-policy-r1
Jul 24, 2026
Merged

kunchenguid merged 2 commits into
mainfrom
fm/fm-ask-user-complexity-escalation-policy-r1

Conversation

@kunchenguid

Copy link
Copy Markdown
Owner

Intent

Implement a shared authority exception for ask-user findings whose proposed Fix would materially expand the captain's original product or engineering contract. Keep the concise always-loaded boundary in AGENTS.md and a single narrowly scoped detailed owner, with normal tracked inheritance for secondmates. Preserve autonomous yolo momentum for difficult corrections genuinely required by accepted intent and for explicitly requested complex architecture, while requiring captain escalation for unrequested new guarantees, threat models, subsystems, abstractions, compatibility surfaces, state machines, continuous monitoring, generalized frameworks, broader architecture, repeated abstraction-preserving same-theme findings, and the existing stronger destructive, irreversible, or security-sensitive boundaries. The implementation worker must never answer its own ask-user finding. Captain escalations must state the original requirement, proposed expansion, smallest compliant alternative, accept/decline consequences, and recommendation. Add scenario regressions for required defect correction, checkpoint proof expanding to continuous frame-by-frame monitoring, repeated same-theme escalation, genuine security sensitivity, and explicitly requested complex architecture. Use Hi Bit PR 148 only as motivating evidence, not a special-case rule. Validate primary and secondmate instruction generation and update conflicting authority wording without incorporating concurrent work.

What Changed

  • Restrict +yolo ask-user decisions to the accepted contract, escalating material expansions and stronger captain-owned boundaries while preserving autonomy for required corrections and explicitly requested complexity.
  • Add a single authority procedure and update worker briefs, project guidance, AFK behavior, and secondmate inheritance to route decisions through firstmate without worker self-approval.
  • Add regression coverage for contract-expansion scenarios, repeated same-theme findings, security-sensitive choices, and primary and secondmate instruction generation.

Risk Assessment

✅ Low: Captain, the change cleanly centralizes the authority policy, preserves stronger escalation boundaries, updates competing instruction paths, and covers the required scenarios without a source-verifiable bypass.

Testing

Reviewed the base-to-target policy change, exercised all required authority scenarios plus away-mode wording and real secondmate tracked-file synchronization, and manually inspected generated primary and secondmate instruction artifacts; everything passed and no UI screenshot was applicable because this is a Markdown and CLI instruction-generation change.

Evidence: Generated primary worker brief
You are a crewmate: an autonomous worker agent managed by firstmate. Work on your own; do not wait for a human.

# Task
{TASK}

# Herdr lifecycle declaration - NOT ENABLED
**HARD SAFETY GATE:** this scaffold cannot inspect the task text that replaces `{TASK}` later.
If the task will start, stop, delete, restart, profile, or otherwise drive Herdr lifecycle behavior, stop and regenerate the brief with `--herdr-lab` before dispatch.
Do not add Herdr lifecycle commands to this unguarded brief by hand.

# Setup
You are in a disposable git worktree of sample, at a detached HEAD on a clean default branch.

**Verify isolation before anything else.** Run `pwd -P` and `git rev-parse --show-toplevel`; both must resolve to the disposable task worktree you were launched in, such as a treehouse pool path or an Orca-managed worktree, not the primary checkout firstmate operates from.
The path check is authoritative: `git rev-parse --git-dir` and `git rev-parse --git-common-dir` can help inspect the repo, but they do not prove you are outside the primary checkout.
If the top-level path is the primary checkout or not the worktree you were launched in, STOP - do not branch or commit here - append `blocked: launched in primary checkout, not an isolated worktree` to the status file and stop.

1. First action: create your branch: `git checkout -b fm/authority-worker`
2. Run `no-mistakes doctor`; if it reports the repo is not initialized here, run `no-mistakes init`.

# Rules
1. Never push to the default branch. Never merge a PR.
2. Stay inside this worktree; modify nothing outside it.
3. Use gh-axi for GitHub operations and chrome-devtools-axi for browser operations.
4. Report status by appending one line:
   `echo "{state}: {one short line}" >> '/var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KY8PTHFQF15E335R0SC68763/home/state/authority-worker.status'`
   States: working, needs-decision, blocked, paused, done, failed.
   Each append wakes firstmate, so report sparingly: only phase changes a supervisor
   would act on (setup done, bug reproduced, fix implemented, validation passed) and the
   needs-decision/blocked/paused/done/failed states. No step-by-step FYI progress lines;
   firstmate reads your pane for that.
   A mid-task `working:` line (including setup complete) is nonterminal: do not end the
   turn after it; continue the same stage until a defined `done:` gate under Definition of done.
   Use `paused: {why}` - distinct from `blocked:` - ONLY when you are deliberately idling on a
   known external wait you expect to clear on its own (an upstream release, a rate-limit reset,
   a scheduled window): firstmate then leaves your idle pane alone and rechecks it on a long
   cadence instead of treating it as a possible wedge. Use `blocked:` when you are stuck and need help.
5. If you hit the same obstacle twice, append `blocked: {why}` and stop; firstmate will help.
6. If a decision belongs above the implementation worker (product choices, destructive actions, ask-user findings),
   append `needs-decision: {summary of options}` and stop. Firstmate will apply the configured authority and reply with the decision.
   When firstmate replies or a blocker clears and you resume, append `resolved: {how it was decided or unblocked}` (add the same `[key=<slug>]` if you opened it with one) so the decision or blocker is durably closed and does not keep resurfacing.
7. Never stop, restart, or update the shared `no-mistakes` daemon - it is one instance serving
   every lane/home, so restarting it kills other lanes' in-flight pipeline runs. On ANY no-mistakes
   daemon error, append `blocked: {the daemon error}` and stop; only firstmate manages the daemon.

# Project memory
If `AGENTS.md` or `CLAUDE.md` already exists, or if this task produced durable project-intrinsic knowledge, run `/Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KY8PTHFQF15E335R0SC68763/bin/fm-ensure-agents-md.sh .` in the worktree.
Record only project knowledge useful to almost every future session.
For anything the codebase already shows, prefer a pointer to the authoritative file, command, or doc over copying the detail.
If you touch a project `AGENTS.md` that lacks `## Maintaining this file`, add that short self-governance section from `/Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KY8PTHFQF15E335R0SC68763/bin/fm-ensure-agents-md.sh` in the same pass.
Keep it proportionate: skip `AGENTS.md` edits for trivial tasks that produced no durable project knowledge.

# Definition of done
The task is complete only when committed on your branch.
When you believe it is complete, append `done: {summary}` to the status file and stop.
Firstmate will then instruct you to run /no-mistakes to validate and ship a PR.

You drive no-mistakes by responding to its gates, not by implementing fixes.
Follow the guidance no-mistakes itself provides for the mechanics: it loads when you invoke /no-mistakes, and `no-mistakes axi run --help` plus the `help` lines in each `axi` response are authoritative and version-matched to the installed binary.
Do not hand-edit, commit, or fix findings yourself while a run is active - the pipeline applies every fix.

Two firstmate-specific rules layer on top of that guidance:
- ask-user findings are never yours to answer: escalate to firstmate (rule 6) and stop.
  Firstmate applies the authority contract in its `AGENTS.md` and obtains any required captain decision.
  When the decision comes back, feed it to the gate with `no-mistakes axi respond` and let the pipeline apply it - do not route the question to "the user" or implement the fix yourself.
- Avoid `--yes`: it would silently bypass firstmate's authority check and any required captain escalation.

After /no-mistakes reports CI green (the CI-ready return point - do not wait for it to keep monitoring in the background until merge), append `done: PR {url} checks green` and stop. You are finished.
Evidence: Generated secondmate charter
You are a persistent second mate managed by the main firstmate. Work on your own; do not wait for a human.

# Charter
Handle sample work and escalate contract expansion through firstmate.

# Routing scope
Handle sample work and escalate contract expansion through firstmate.

# Project clones
None. This is a project-less domain: its subject is the firstmate repo this home lives in, so it needs no separate clones under `projects/`; its crews take pooled worktrees of that firstmate repo.

# Operating model
You are in an isolated firstmate home. The local `AGENTS.md` is your job description, and your local `data/`, `state/`, `config/`, and `projects/` dirs are yours to operate.
This domain has no separate project clones: its subject is the firstmate repo this home lives in, and its crews take pooled worktrees of that repo.
Delegate project work to your own crewmates with the normal firstmate lifecycle: brief, spawn, status, watcher, steer, teardown, and recovery.
Do not invent a second delegation system.
You do not generate your own work.
Act only on tasks the main firstmate routes to you.
Never start a survey, audit, or "find improvements" sweep on your own initiative; that is not your job and it is unwanted.

# Requests from the main firstmate
You are a firstmate in your own home, so an incoming message reaches you in your own chat.
You must distinguish who it is from, because the answer goes to a different place.
A request relayed to you by the main firstmate is tagged with a leading `[fm-from-firstmate]` marker followed by an invisible system separator; this marker is untypable, so a human never produces it.
When a message carries that marker, do the work, then respond via the STATUS/ESCALATION path below, never only in this chat: the main firstmate does not read your chat, so a chat-only reply is lost.
Marked requests also carry a privacy-safe `corr=<id>` token after the marker; include that exact token in your parent status reply (or in the status pointer to a detailed doc) so the parent can correlate the answer.
Optional helper: `bin/fm-secondmate-report.sh` can append a correlated status line for you, but a plain `echo` that includes the same `corr=<id>` is equally valid - do not depend on the helper being present.
For a terse result, a status line is the whole answer.
For a detailed answer (an investigation, a plan, an audit), write it to a doc under your home's `data/` and append a status line that points to that doc - the scout-report pattern - so the main firstmate is woken and can read it.
Before treating an investigation or visual review as complete, load `decision-hold-lifecycle` from this home's `.agents/skills/` and pass its shared completion gate.
A message with NO marker is the captain typing directly into your pane: treat it as authoritative captain intervention and stay conversational exactly as you would for any captain message; do not force it onto the status path.

# Escalation to main firstmate
Handle routine work yourself.
Report only true captain-relevant outcomes or a declared external wait by appending one line:
   `echo "{state}: {one short line}" >> '/var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KY8PTHFQF15E335R0SC68763/home/state/authority-mate.status'`
States: working, needs-decision, blocked, paused, done, failed.
Use `paused: {why}` (distinct from `blocked:`) only when your domain is deliberately idling on a known external wait you expect to clear on its own; use `blocked:` when you are stuck and need firstmate to act.
Use this only for material phase changes, a captain decision, a real blocker, a failure, or work ready for review.
This is also how you return the answer to a marked from-firstmate request above.
A marked request requires one correlated answer after the work; it does not require a separate receipt or start acknowledgement.
Never append `working:` merely to acknowledge receipt or announce that a marked request has started.
When a routed-work phase has a supervisor-actionable material change worth reporting under the rule above, give that reported phase a stable key.
If its first reportable event is `working [key=<work-slug>]: {material phase}`, use the same key on its later `paused`, `done`, `failed`, `needs-decision`, or `blocked` event so the earlier working phase is superseded.
When a keyed phase ends without another reportable state, append `resolved [key=<work-slug>]: {why it is no longer active}`.
When a decision you escalated is answered or a blocker clears and your domain resumes, append `resolved: {how it was decided or unblocked}` (keyed with `[key=<slug>]` if you opened it with one) so it is durably closed instead of resurfacing behind later unrelated events.
Routine internal supervision, heartbeats, retries, and crewmate churn stay inside your own home and must not touch that status file.

# Definition of done
You are persistent by default. Do not exit just because your queue is empty.
On startup and restart, run normal firstmate bootstrap and recovery through `bin/fm-session-start.sh` for your own home, but only to RECONCILE work that is already yours: in-flight crewmates, tracked backlog items, and durable watches recorded in this home.
When you have no assigned or in-flight work after that reconciliation, go idle and wait silently for the main firstmate to route you a task.
An empty queue is a healthy resting state, not a cue to invent work: never spawn a survey, audit, or any self-directed "find work" task on your own initiative.
If this charter cannot be carried out, append `blocked: {why}` or `failed: {why}` to the main status file and stop.

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • Reviewed git diff 6b0d21de21fbf840c3752834cc15dec37d91bf33..09a9eb997dc4619975bf501c61dfa4ddf6635ca1 against the authoritative intent.
  • ./bin/fm-test-run.sh tests/fm-ask-user-authority.test.sh tests/fm-afk-return.test.sh
  • ./bin/fm-test-run.sh tests/fm-secondmate-sync.test.sh
  • Generated a primary worker brief with FM_HOME=&lt;evidence-home&gt; FM_ROOT_OVERRIDE=&#34;$PWD&#34; ./bin/fm-brief.sh authority-worker sample and inspected its actual authority instructions.
  • Generated a secondmate charter with FM_SECONDMATE_CHARTER=&#39;Handle sample work and escalate contract expansion through firstmate.&#39; ./bin/fm-brief.sh authority-mate --secondmate --no-projects and inspected its tracked-instruction pointer.
  • Verified git status --short remained empty after testing.
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@kunchenguid
kunchenguid merged commit ec09871 into main Jul 24, 2026
10 checks passed
@kunchenguid
kunchenguid deleted the fm/fm-ask-user-complexity-escalation-policy-r1 branch July 24, 2026 00:25
LoneExile pushed a commit to LoneExile/firstmate that referenced this pull request Jul 24, 2026
* fix: escalate ask-user contract expansion

* no-mistakes(document): Point project management to authority owner

(cherry picked from commit ec09871)
omp-adapt: retarget reread assertion to fm-bootstrap.sh nudge (kunchenguid#809 config-reread deferred)
palinopr pushed a commit to palinopr/firstmate that referenced this pull request Jul 25, 2026
bin/fm-brief.sh failed to parse under bash 3.2.57, the stock macOS bash
that `#!/usr/bin/env bash` resolves to, so no macOS home could scaffold a
ship brief after kunchenguid#945.

Bash 3.2 scans a command substitution for its closing `)` with no
understanding of heredocs, so any odd number of `'` or `"` in a
`VAR=$(cat <<EOF ... EOF)` body is read as an opening quote and swallows
the rest of the file. kunchenguid#945 added the possessive "firstmate's" to the
no-mistakes Definition-of-done block, which armed it. Quoting the
delimiter does not help, and the break was latent for anyone who only
scaffolded scouts, because that path exits before bash reads that far.

The `case` construct is not implicated: the identical `$(cat <<EOF)`
shape in the direct-PR and local-only branches parses fine, and the
failure reproduces with no `case` at all.

Replace the construct rather than the apostrophe. `read_block VAR <<EOF`
attaches the heredoc to the call site instead of nesting it inside
`$(...)`, reproducing the same assignment semantics including stripped
trailing newlines. Heredoc bodies are untouched, so brief content is
byte-identical; "firstmate's" is deliberately kept as a live canary.

Guards, because a `bash -n` on a Linux CI image proves nothing here:

- tests/fm-brief.test.sh bans heredocs nested in `$(...)` with an
  awk scanner that is calibrated against known-bad and known-good
  fixtures on every run. It fails on the unfixed script under any bash.
- The suite parses the script with a real bash 3.x when one is present,
  confirms 3.x still rejects the banned construct, and proves read_block
  matches `$(cat <<EOF)` semantics against the helper extracted from
  source.
- CI's macos-stock-bash job now parses every bin script under 3.2.57 and
  runs the brief suite there.

Fixes kunchenguid#1000. Related: kunchenguid#166, which pinned one apostrophe phrase and so
left the trap armed for the next contributor.
ItsFlow referenced this pull request in ItsFlow/firstmate Jul 26, 2026
* fix: block primary-session delegation outside the fleet (#854)

* 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

* fix: install tasks-axi in portable CI shards (#866)

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(bin): make dispatch profiles quota aware (#867)

* 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

* Add built-in ahoy recap skill (#873)

* fix: preserve trustworthy Bearings data in partial snapshots (#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

* feat(pi): add session-local calm mode (#884)

* 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): prevent redundant watcher re-arms (#885)

* 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 (#895)

* 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 (#898)

* fix: execute every PR body compliance event

* no-mistakes(document): Document independent PR compliance events

* fix: exclude operational injections from ahoy boundaries (#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

* fix: canonically classify operational inputs across harnesses (#909)

* fix: type canonical operational inputs

* no-mistakes(document): Correct canonical operational-input documentation ownership

* fix: avoid generic secondmate start acknowledgements (#926)

* fix: avoid generic secondmate acknowledgements

* no-mistakes(document): Document sparse secondmate acknowledgement behavior

* no-mistakes: apply CI fixes

* fix(pi): make calm mode persistent and gapless (#927)

* fix(pi): preserve calm presentation across sessions

* no-mistakes(review): Fix Calm home fallback persistence

* no-mistakes(document): Clarify Calm gapless and export contracts

* no-mistakes: apply CI fixes

* fix(watch): retire merged PR polls after durable notification (#932)

* fix: retire merged PR polls after notification

* no-mistakes(review): Decouple PR retirement recovery from template updates

* no-mistakes(review): Recover pending PR retirements before poll migration

* no-mistakes(document): Document merged PR poll retirement contracts

* fix: refine scout intake and parallel dispatch (#934)

* Clarify intake evidence and overlap handling

* no-mistakes(review): Align scout guard with intake classification

* no-mistakes(document): Clarify scout documentation and intake ownership

* fix(pi): prevent duplicate assistant replies in Calm (#936)

* fix(pi): preserve operational follow-up semantics in Calm

* no-mistakes(document): Correct Calm operational-row visibility documentation

* perf(bin): shrink the ShellCheck source graph (#939)

* perf(lint): shrink shell source graph

* no-mistakes(review): Ensure lint workers terminate fully on cancellation

* no-mistakes(document): Repair stale lint documentation ownership

* fix(pi): remove Calm hidden-block gaps (#942)

* fix(pi): remove Calm hidden-block gaps

* no-mistakes(review): Validate Calm geometry against current viewport

* no-mistakes(review): Synchronize Calm geometry checks with reload completion

* fix: enforce contract boundaries for ask-user findings (#945)

* fix: escalate ask-user contract expansion

* no-mistakes(document): Point project management to authority owner

* docs: prefer direct operational paths (#946)

* fix(pi): hide operational user rows in Calm mode (#948)

* fix(pi): hide Calm operational user rows

* no-mistakes(review): Narrow Calm operational input suppression

* no-mistakes(review): Avoid Calm replay classifier subprocesses

* no-mistakes(document): docs: point Pi verification to Calm owner

* fix: relaunch missing second mates at session start (#950)

* fix(session-start): relaunch missing second mates

* fix(test): detect completed parallel workers

* no-mistakes(review): Isolate session-start recovery test cleanup

* no-mistakes(review): Complete backend-safe secondmate session recovery

* no-mistakes(review): Resolve Zellij task ownership before recovery

* no-mistakes(review): Recover relocated Zellij ghost tabs safely

* no-mistakes(review): Restore conservative Zellij recovery boundary

* no-mistakes(review): Reject malformed tmux recovery targets

* no-mistakes(document): Align secondmate recovery documentation

* no-mistakes: apply CI fixes

* fix(herdr): reclaim resumed task projections after restart (#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

* Teach Ahoy to surface open decisions (#968)

* Require shipshape routine acknowledgement (#969)

* docs: separate current guidance from verification evidence (#994)

* docs: separate current guides from verification

* no-mistakes(review): Restore Herdr 0.7.5 restart-reclaim verification evidence

* fix: preserve Claude watcher continuity across Stop hooks (#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

* fix(herdr): clean stale projections at session start (#996)

* 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

* feat(inbox): add the captain's decision and review board generator

bin/fm-inbox-view.sh writes one self-contained HTML board from live fleet
state, so every open captain decision, review-ready pull request and stopped
task is answerable from a single surface instead of arriving one chat
interrupt at a time.

It is a read-only projection in the same shape as fm-fleet-view.sh: it shells
out to fm-fleet-snapshot.sh --json and `tasks-axi show --full`, and writes
nothing but the board file. A test proves state/ and data/ stay byte-identical
across a generation.

Three selection defects the existing aggregated surface has are fixed here.
Decisions are selected on the captain hold alone, because the snapshot's own
captain_actionable flag also requires kind == "captain" and therefore hides
every thread gated with `tasks-axi hold --kind captain`, which is where the
oldest waiting decisions live.
Decision text is read through tasks-axi, because the snapshot's metadata
capture stops at the first comma and silently drops the options from any hold
reason that lists them.
A recorded pull request renders as unverified unless --verify-prs live-checks
it, because locally recorded metadata cannot know a pull request was closed
and a closed one must never read as ready to merge.

Decision cards lead with plain English. An optional captain-private cards file
supplies the plain question, what the captain is actually choosing, where it
came from, firstmate's recommendation, a link to research a product or
service, and a collapsed technical section. An item that is firstmate's own
assumption rather than a choice the captain made is flagged as one. A decision
with no card still renders and says so rather than looking complete.

Answers use the Lavish input contract: native radios plus a free-text note,
with exactly one queued prompt per question on submit. This change delivers
the surface only; regenerating on a cadence, relaying answers back, and any
escalation-routing change remain separate captain decisions.

* feat(inbox): free-form answers, discuss path, reliable delivery, overflow fix

Fold in the captain's hard requirements from live use of the board.

Free-form answers are now first-class. A card's predefined options are
optional quick-picks with a clear-selection reset; the answer submits with
just free text and no option selected. Previously the submit required picking
an offered option, so a genuine free-form answer could not be sent at all.

Each card gains a discuss path that sends a question back (DISCUSS <id>: ...)
instead of an answer (DECISION <id>: ...), so a decision can be clarified
rather than forced.

Delivery is made reliable end to end. bin/fm-inbox-serve.sh is one operator
command that regenerates the board, arms the answer relay, serves it on the
Tailscale address, and verifies the link. bin/fm-inbox-arm.sh writes and
registers state/inbox.check.sh, a bounded lavish poll bound by
fm-check-register, so firstmate's supervision cycle relays answers on its
normal check cadence without anyone remembering to poll. The relay fails
closed: when the board is not served the poll errors fast and it stays silent.
Submitting also sends immediately with a visible confirmation, closing the
window where an answer looked silently eaten.

Long titles and ids now wrap instead of overflowing horizontally, which
Lavish's own layout audit flags as an error. No-project items get readable
area names derived from their origin or id instead of a generic badge.

Behavior is covered by tests/fm-inbox-view.test.sh (generator, read-only,
free-form, discuss, immediate send, area names, wrapping) and
tests/fm-inbox-arm.test.sh (the relay is a valid registered fail-closed
check, silent while unserved, rejected after tampering). docs/inbox-board.md
records the design and verification evidence.

* no-mistakes(review): Fix inbox relay safety and PR verification

* no-mistakes(review): Gate inbox relay before polling

* no-mistakes(document): Align inbox board docs

* no-mistakes: apply CI fixes

---------

Co-authored-by: Kun Chen <3233006+kunchenguid@users.noreply.github.com>
hnvivek added a commit to hnvivek/firstmate that referenced this pull request Jul 26, 2026
The three ship-mode Definition-of-done blocks built the brief text with
`DOD=$(cat <<EOF ... EOF)` nested inside `case`. Bash 3.2's lexer loses
quote state across a heredoc-in-$(...) inside `case`, so a single
apostrophe anywhere in the body (reintroduced in kunchenguid#945) aborted the whole
script with "unexpected EOF while looking for matching ')'". The scout
and secondmate paths use a plain `cat > file <<EOF` redirect (no $(...))
and were unaffected.

Rewrite each DOD block as `IFS= read -r -d '' DOD <<EOF || true` plus a
single `${DOD%$'\n'}` strip. Byte-for-byte identical brief output proven
against the bash-5 baseline for no-mistakes, direct-PR, local-only, and
scout scaffolds, under both /bin/bash 3.2 and bash 5.

Also extend the existing macos-stock-bash CI job to loop /bin/bash -n
over every bin/*.sh and bin/backends/*.sh, so any future bash-3.2-only
syntax regression in any tracked shell script fails before merge.
hnvivek added a commit to hnvivek/firstmate that referenced this pull request Jul 26, 2026
The three ship-mode Definition-of-done blocks built the brief text with
`DOD=$(cat <<EOF ... EOF)` nested inside `case`. Bash 3.2's lexer loses
quote state across a heredoc-in-$(...) inside `case`, so a single
apostrophe anywhere in the body (reintroduced in kunchenguid#945) aborted the whole
script with "unexpected EOF while looking for matching ')'". The scout
and secondmate paths use a plain `cat > file <<EOF` redirect (no $(...))
and were unaffected.

Rewrite each DOD block as `IFS= read -r -d '' DOD <<EOF || true` plus a
single `${DOD%$'\n'}` strip. Byte-for-byte identical brief output proven
against the bash-5 baseline for no-mistakes, direct-PR, local-only, and
scout scaffolds, under both /bin/bash 3.2 and bash 5.

Also extend the existing macos-stock-bash CI job to loop /bin/bash -n
over every bin/*.sh and bin/backends/*.sh, so any future bash-3.2-only
syntax regression in any tracked shell script fails before merge.
rega10 added a commit to rega10/firstmate that referenced this pull request Jul 27, 2026
* fix(herdr): group projected children beneath owning parents (kunchenguid#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: keep local no-mistakes tests intent-targeted (kunchenguid#823)

* 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: add canonical timed test runner (kunchenguid#825)

* 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: surface main inventory gaps in Bearings (kunchenguid#830)

* 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 bounded concurrent test isolation proof (kunchenguid#832)

* 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: guard against missed secondmate reports (kunchenguid#834)

* 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: require pinned real-Herdr CI coverage (kunchenguid#838)

* 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

* feat: shard portable tests and add bounded local parallelism (kunchenguid#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

* fix: block primary-session delegation outside the fleet (kunchenguid#854)

* 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

* fix: install tasks-axi in portable CI shards (kunchenguid#866)

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(bin): make dispatch profiles quota aware (kunchenguid#867)

* 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

* Add built-in ahoy recap skill (kunchenguid#873)

* fix: preserve trustworthy Bearings data in partial snapshots (kunchenguid#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

* feat(pi): add session-local calm mode (kunchenguid#884)

* 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): prevent redundant watcher re-arms (kunchenguid#885)

* 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 (kunchenguid#895)

* 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 (kunchenguid#898)

* fix: execute every PR body compliance event

* no-mistakes(document): Document independent PR compliance events

* fix: exclude operational injections from ahoy boundaries (kunchenguid#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

* fix: canonically classify operational inputs across harnesses (kunchenguid#909)

* fix: type canonical operational inputs

* no-mistakes(document): Correct canonical operational-input documentation ownership

* fix: avoid generic secondmate start acknowledgements (kunchenguid#926)

* fix: avoid generic secondmate acknowledgements

* no-mistakes(document): Document sparse secondmate acknowledgement behavior

* no-mistakes: apply CI fixes

* fix(pi): make calm mode persistent and gapless (kunchenguid#927)

* fix(pi): preserve calm presentation across sessions

* no-mistakes(review): Fix Calm home fallback persistence

* no-mistakes(document): Clarify Calm gapless and export contracts

* no-mistakes: apply CI fixes

* fix(watch): retire merged PR polls after durable notification (kunchenguid#932)

* fix: retire merged PR polls after notification

* no-mistakes(review): Decouple PR retirement recovery from template updates

* no-mistakes(review): Recover pending PR retirements before poll migration

* no-mistakes(document): Document merged PR poll retirement contracts

* fix: refine scout intake and parallel dispatch (kunchenguid#934)

* Clarify intake evidence and overlap handling

* no-mistakes(review): Align scout guard with intake classification

* no-mistakes(document): Clarify scout documentation and intake ownership

* fix(pi): prevent duplicate assistant replies in Calm (kunchenguid#936)

* fix(pi): preserve operational follow-up semantics in Calm

* no-mistakes(document): Correct Calm operational-row visibility documentation

* perf(bin): shrink the ShellCheck source graph (kunchenguid#939)

* perf(lint): shrink shell source graph

* no-mistakes(review): Ensure lint workers terminate fully on cancellation

* no-mistakes(document): Repair stale lint documentation ownership

* fix(pi): remove Calm hidden-block gaps (kunchenguid#942)

* fix(pi): remove Calm hidden-block gaps

* no-mistakes(review): Validate Calm geometry against current viewport

* no-mistakes(review): Synchronize Calm geometry checks with reload completion

* fix: enforce contract boundaries for ask-user findings (kunchenguid#945)

* fix: escalate ask-user contract expansion

* no-mistakes(document): Point project management to authority owner

* docs: prefer direct operational paths (kunchenguid#946)

* fix(pi): hide operational user rows in Calm mode (kunchenguid#948)

* fix(pi): hide Calm operational user rows

* no-mistakes(review): Narrow Calm operational input suppression

* no-mistakes(review): Avoid Calm replay classifier subprocesses

* no-mistakes(document): docs: point Pi verification to Calm owner

* fix: relaunch missing second mates at session start (kunchenguid#950)

* fix(session-start): relaunch missing second mates

* fix(test): detect completed parallel workers

* no-mistakes(review): Isolate session-start recovery test cleanup

* no-mistakes(review): Complete backend-safe secondmate session recovery

* no-mistakes(review): Resolve Zellij task ownership before recovery

* no-mistakes(review): Recover relocated Zellij ghost tabs safely

* no-mistakes(review): Restore conservative Zellij recovery boundary

* no-mistakes(review): Reject malformed tmux recovery targets

* no-mistakes(document): Align secondmate recovery documentation

* no-mistakes: apply CI fixes

* fix(herdr): reclaim resumed task projections after restart (kunchenguid#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

* Teach Ahoy to surface open decisions (kunchenguid#968)

* Require shipshape routine acknowledgement (kunchenguid#969)

* docs: separate current guidance from verification evidence (kunchenguid#994)

* docs: separate current guides from verification

* no-mistakes(review): Restore Herdr 0.7.5 restart-reclaim verification evidence

* fix: preserve Claude watcher continuity across Stop hooks (kunchenguid#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

* fix(herdr): clean stale projections at session start (kunchenguid#996)

* 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

* fix: recover Claude supervision without watcher-status gate (kunchenguid#1001)

* fix: recover Claude supervision at session start

* fix: remove Claude watcher-status command gate

* no-mistakes(document): docs: remove stale continuity gate references

* fix: make quota-aware profile selection agent-owned (kunchenguid#1018)

* Replace quota dispatch selector instructions

* no-mistakes(review): Align bootstrap docs with agent-owned dispatch selection

* fix(bin): remove vestigial dispatch selector (kunchenguid#1026)

* 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

* docs(agents): drop superseded interim quota-window rule (kunchenguid#1039)

quota-axi 0.1.13 emits schemaVersion 2 with a quotaSemantics object per
provider, so the successor named in the interim rule has landed and the
rule's own removal condition is satisfied.

Keep the ownership clause so quota-axi remains the single owner of how
model or product windows relate to bounding account windows, and drop
the interim weakest-headroom instruction. The unknown-semantics case is
already covered by the existing requirement to stop and report a
candidate whose applicable quota data or interpretation cannot be
established.

Drop the matching assertion phrase from
tests/fm-instruction-owners.test.sh; the retained ownership phrase still
asserts.

* fix(tmux): scope busy detection and recognize current Claude turns (kunchenguid#1049)

* fix(tmux): scope Claude busy detection by harness

* no-mistakes(review): Separate verified and fallback busy signatures

* no-mistakes(test): Scope busy signatures to supplied harnesses

* no-mistakes(document): Document harness-scoped busy detection

* feat: add verified Kimi crewmate adapter (kunchenguid#1047)

* Add verified Kimi crewmate harness adapter

* no-mistakes(review): Scope Kimi moon detection to spinner lines

* no-mistakes(review): Match only complete Kimi spinner rows

* no-mistakes(review): Resolve Kimi binary portably before pane creation

* no-mistakes(document): Align Kimi adapter documentation

* no-mistakes(lint): Suppress false-positive ShellCheck warning for sourced watcher override

* Fix Kimi busy spinner detection

* no-mistakes(review): Recognize Kimi session-lock ancestry and holders

* no-mistakes(review): Scope pending-reply Kimi busy detection by harness

* no-mistakes(document): Correct Kimi spinner capture documentation

* no-mistakes(document): Clarify optional Kimi spinner whitespace

* no-mistakes(lint): Silence intentional pending-reply test stub warnings

* test: align rebased Kimi busy fixtures

* no-mistakes: apply CI fixes

* Reconcile Kimi busy detection after per-harness scoping

* no-mistakes(review): Clarify observed Kimi spinner whitespace contract

* no-mistakes(document): Clarify Kimi harness documentation

* fix: harden Kimi submission and spinner matching (kunchenguid#1058)

* fix kimi pointer submission and spinner conformance

* no-mistakes(review): Preserve Kimi submit target ownership guard

* feat(bin): add guarded Kimi turn-end wake (kunchenguid#1059)

* Add guarded Kimi turn-end hook

* no-mistakes(review): Require jq before installing Kimi turn-end hook

* no-mistakes(review): Expose jq inside isolated Kimi test fixtures

* no-mistakes(review): Preserve Kimi config boundaries during hook removal

* no-mistakes(review): Document Kimi removal newline safeguard

* no-mistakes(document): Document Kimi shared-home preservation

* fix(tmux): classify bordered composers across all rows (kunchenguid#1066)

* Fix structural tmux composer reading

* Verify Calm compatibility with Pi 0.82

* no-mistakes(review): Harden structural composer classification boundaries

* no-mistakes(review): Refresh composer and Kimi regression fixtures

* no-mistakes(review): Fail closed on unbounded composer edges

* no-mistakes(review): Enforce aligned composer geometry safely

* no-mistakes(review): Make composer ambiguity locale-safe

* no-mistakes(review): Preserve ambiguity through composer submission

* no-mistakes(review): Carry composer proof through retries

* no-mistakes(document): Document structural tmux composer delivery guarantees

* no-mistakes: apply CI fixes

* no-mistakes(review): Captain, serialize Herdr teardown across homes

---------

Co-authored-by: Kun Chen <3233006+kunchenguid@users.noreply.github.com>
Co-authored-by: ItsFlow <florian.eberhart.z@gmail.com>
Co-authored-by: Rene Garza Jr <rega10tam@gmail.com>
hartnoel12 added a commit to hartnoel12/firstmate that referenced this pull request Jul 27, 2026
…ard (#1)

* fix(brief): restore bash 3.2 parsing and guard the heredoc hazard

bin/fm-brief.sh did not parse under bash 3.2, the system bash on macOS, so
brief generation failed for every delivery mode and no work could be dispatched
from a fleet that updated to current main.

Bash 3.2 tracks quote state through a heredoc body while scanning for the
closing `)` of a command substitution, so the apostrophe in "firstmate's" that
kunchenguid#945 moved into the no-mistakes Definition-of-done body opened a quote that
never closed. Bash 5 parses it fine, which is why it shipped.

The same class already shipped once as kunchenguid#166. Escaping the apostrophe silences
`bash -n` but emits a literal backslash into every brief, and quoting the
delimiter (<<'EOF') does not help at all - the parser breaks either way. The
only robust form is to keep the heredoc out of the command substitution, so
every prose-bearing heredoc now lives in a function body and the caller invokes
the function. Prose there can carry apostrophes freely.

Swept all of bin/ rather than the one site: the same latent form was in
fm-fleet-snapshot.sh (two embedded bash readers, two jq filters) and
fm-bootstrap.sh (the X-mode cadence block, moved to fm-x-lib.sh beside its
sibling generator). bin/ now opens no heredoc inside a command substitution.

Guard: bin/fm-bash-syntax-check.sh parse-checks every canonical shell root and
is wired into the existing macos-stock-bash CI lane, the only lane that runs
bash 3.2. Its --require-bash32 flag makes the job fail rather than pass
vacuously under a newer bash - the pre-existing `bash -n` test in
tests/fm-brief.test.sh missed kunchenguid#945 precisely because CI is Linux. The root set
comes from `fm-lint.sh --list-roots` so the two checks cannot drift, and a
structural test keeps the idiom out of bin/ on every lane.

Verified on bash 3.2.57: 199 roots parse cleanly; all three ship modes plus
--scout and --secondmate generate intact briefs; the guard fails when the bug is
reintroduced and passes when reverted.

* no-mistakes(review): broaden heredoc-in-command-substitution ban and count missing roots

* no-mistakes(document): clarify heredoc guard enforcement split and macOS CI lane scope

* no-mistakes(document): document macOS Bash 3.2 lane coverage above its CI job

---------

Co-authored-by: Savory <savory@Savorys-Mac-mini.local>
purple-phoenix added a commit to purple-phoenix/firstmate that referenced this pull request Jul 29, 2026
* fix(pi): distinguish stale locks when arming watcher (kunchenguid#681)

* 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 house vocabulary (kunchenguid#685)

* fix: accept secondmate as house vocabulary

* no-mistakes(test): Update captain vocabulary contract test

* no-mistakes(document): Align secondmate documentation vocabulary

* fix(bin): parse handoff homes after registry parentheticals (kunchenguid#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 (kunchenguid#687)

* feat: add native session-start nudges

* no-mistakes(document): Document nudge script inventory

* docs: call built-in defaults the firstmate repo, not template (kunchenguid#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.

* fix(bin): repair fm-brief.sh parse error and harden set -u array expansion (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>

* fix(bin): keep watcher supervision continuous across child cycles (kunchenguid#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: fetch current PR head for review diffs (kunchenguid#722)

* 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

* docs: resolve five contract contradictions across AGENTS.md, README, and skills (kunchenguid#736)

* docs: resolve five contract contradictions

* no-mistakes(test): align owner-pointer assertions with reworded docs; skip absent shellcheck

* docs(harness): correct Grok exit guidance (kunchenguid#742)

* docs(harness): reverify grok exit command

* no-mistakes(test): Correct Grok exit resume attribution

* fix(watcher): bound stale wakes for parked crew (kunchenguid#743)

* 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

* fix(supervision): distinguish ordinary wakes from recovery (kunchenguid#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 (kunchenguid#745)

* 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

* feat(wake): enrich drained signals with bounded status context (kunchenguid#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 (kunchenguid#784)

* 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

* fix(send): treat opencode busy-queued composer state as submitted (kunchenguid#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

* fix(spawn): require two stable reads before accepting worktree path (kunchenguid#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>

* fix(bin): make watcher process identity immune to Linux wall-clock changes (kunchenguid#752)

* fix(watcher): stabilize Linux process identity

* no-mistakes(document): document FM_PROC_ROOT_OVERRIDE and Linux starttime identity rationale

* fix: prevent AFK idle stalls and stale run attribution (kunchenguid#758)

* 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(bin): allow safe teardown during watcher recovery (kunchenguid#750)

* 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

* feat(herdr): order presentation spaces while preserving focus (kunchenguid#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

* fix(bin): send literal config reread nudges after pushes (kunchenguid#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 (kunchenguid#797)

* 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

* fix(herdr): group projected children beneath owning parents (kunchenguid#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: keep local no-mistakes tests intent-targeted (kunchenguid#823)

* 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: add canonical timed test runner (kunchenguid#825)

* 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: surface main inventory gaps in Bearings (kunchenguid#830)

* 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 bounded concurrent test isolation proof (kunchenguid#832)

* 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: guard against missed secondmate reports (kunchenguid#834)

* 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: require pinned real-Herdr CI coverage (kunchenguid#838)

* 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

* feat: shard portable tests and add bounded local parallelism (kunchenguid#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

* fix: block primary-session delegation outside the fleet (kunchenguid#854)

* 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

* fix: install tasks-axi in portable CI shards (kunchenguid#866)

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(bin): make dispatch profiles quota aware (kunchenguid#867)

* 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

* Add built-in ahoy recap skill (kunchenguid#873)

* fix: preserve trustworthy Bearings data in partial snapshots (kunchenguid#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

* feat(pi): add session-local calm mode (kunchenguid#884)

* 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): prevent redundant watcher re-arms (kunchenguid#885)

* 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 (kunchenguid#895)

* 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 (kunchenguid#898)

* fix: execute every PR body compliance event

* no-mistakes(document): Document independent PR compliance events

* fix: exclude operational injections from ahoy boundaries (kunchenguid#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

* fix: canonically classify operational inputs across harnesses (kunchenguid#909)

* fix: type canonical operational inputs

* no-mistakes(document): Correct canonical operational-input documentation ownership

* fix: avoid generic secondmate start acknowledgements (kunchenguid#926)

* fix: avoid generic secondmate acknowledgements

* no-mistakes(document): Document sparse secondmate acknowledgement behavior

* no-mistakes: apply CI fixes

* fix(pi): make calm mode persistent and gapless (kunchenguid#927)

* fix(pi): preserve calm presentation across sessions

* no-mistakes(review): Fix Calm home fallback persistence

* no-mistakes(document): Clarify Calm gapless and export contracts

* no-mistakes: apply CI fixes

* fix(watch): retire merged PR polls after durable notification (kunchenguid#932)

* fix: retire merged PR polls after notification

* no-mistakes(review): Decouple PR retirement recovery from template updates

* no-mistakes(review): Recover pending PR retirements before poll migration

* no-mistakes(document): Document merged PR poll retirement contracts

* fix: refine scout intake and parallel dispatch (kunchenguid#934)

* Clarify intake evidence and overlap handling

* no-mistakes(review): Align scout guard with intake classification

* no-mistakes(document): Clarify scout documentation and intake ownership

* fix(pi): prevent duplicate assistant replies in Calm (kunchenguid#936)

* fix(pi): preserve operational follow-up semantics in Calm

* no-mistakes(document): Correct Calm operational-row visibility documentation

* perf(bin): shrink the ShellCheck source graph (kunchenguid#939)

* perf(lint): shrink shell source graph

* no-mistakes(review): Ensure lint workers terminate fully on cancellation

* no-mistakes(document): Repair stale lint documentation ownership

* fix(pi): remove Calm hidden-block gaps (kunchenguid#942)

* fix(pi): remove Calm hidden-block gaps

* no-mistakes(review): Validate Calm geometry against current viewport

* no-mistakes(review): Synchronize Calm geometry checks with reload completion

* fix: enforce contract boundaries for ask-user findings (kunchenguid#945)

* fix: escalate ask-user contract expansion

* no-mistakes(document): Point project management to authority owner

* docs: prefer direct operational paths (kunchenguid#946)

* fix(pi): hide operational user rows in Calm mode (kunchenguid#948)

* fix(pi): hide Calm operational user rows

* no-mistakes(review): Narrow Calm operational input suppression

* no-mistakes(review): Avoid Calm replay classifier subprocesses

* no-mistakes(document): docs: point Pi verification to Calm owner

* fix: relaunch missing second mates at session start (kunchenguid#950)

* fix(session-start): relaunch missing second mates

* fix(test): detect completed parallel workers

* no-mistakes(review): Isolate session-start recovery test cleanup

* no-mistakes(review): Complete backend-safe secondmate session recovery

* no-mistakes(review): Resolve Zellij task ownership before recovery

* no-mistakes(review): Recover relocated Zellij ghost tabs safely

* no-mistakes(review): Restore conservative Zellij recovery boundary

* no-mistakes(review): Reject malformed tmux recovery targets

* no-mistakes(document): Align secondmate recovery documentation

* no-mistakes: apply CI fixes

* fix(herdr): reclaim resumed task projections after restart (kunchenguid#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

* Teach Ahoy to surface open decisions (kunchenguid#968)

* Require shipshape routine acknowledgement (kunchenguid#969)

* docs: separate current guidance from verification evidence (kunchenguid#994)

* docs: separate current guides from verification

* no-mistakes(review): Restore Herdr 0.7.5 restart-reclaim verification evidence

* fix: preserve Claude watcher continuity across Stop hooks (kunchenguid#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

* fix(herdr): clean stale projections at session start (kunchenguid#996)

* 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

* fix: recover Claude supervision without watcher-status gate (kunchenguid#1001)

* fix: recover Claude supervision at session start

* fix: remove Claude watcher-status command gate

* no-mistakes(document): docs: remove stale continuity gate references

* fix: make quota-aware profile selection agent-owned (kunchenguid#1018)

* Replace quota dispatch selector instructions

* no-mistakes(review): Align bootstrap docs with agent-owned dispatch selection

* fix(bin): remove vestigial dispatch selector (kunchenguid#1026)

* 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

* docs(agents): drop superseded interim quota-window rule (kunchenguid#1039)

quota-axi 0.1.13 emits schemaVersion 2 with a quotaSemantics object per
provider, so the successor named in the interim rule has landed and the
rule's own removal condition is satisfied.

Keep the ownership clause so quota-axi remains the single owner of how
model or product windows relate to bounding account windows, and drop
the interim weakest-headroom instruction. The unknown-semantics case is
already covered by the existing requirement to stop and report a
candidate whose applicable quota data or interpretation cannot be
established.

Drop the matching assertion phrase from
tests/fm-instruction-owners.test.sh; the retained ownership phrase still
asserts.

* fix(tmux): scope busy detection and recognize current Claude turns (kunchenguid#1049)

* fix(tmux): scope Claude busy detection by harness

* no-mistakes(review): Separate verified and fallback busy signatures

* no-mistakes(test): Scope busy signatures to supplied harnesses

* no-mistakes(document): Document harness-scoped busy detection

* feat: add verified Kimi crewmate adapter (kunchenguid#1047)

* Add verified Kimi crewmate harness adapter

* no-mistakes(review): Scope Kimi moon detection to spinner lines

* no-mistakes(review): Match only complete Kimi spinner rows

* no-mistakes(review): Resolve Kimi binary portably before pane creation

* no-mistakes(document): Align Kimi adapter documentation

* no-mistakes(lint): Suppress false-positive ShellCheck warning for sourced watcher override

* Fix Kimi busy spinner detection

* no-mistakes(review): Recognize Kimi session-lock ancestry and holders

* no-mistakes(review): Scope pending-reply Kimi busy detection by harness

* no-mistakes(document): Correct Kimi spinner capture documentation

* no-mistakes(document): Clarify optional Kimi spinner whitespace

* no-mistakes(lint): Silence intentional pending-reply test stub warnings

* test: align rebased Kimi busy fixtures

* no-mistakes: apply CI fixes

* Reconcile Kimi busy detection after per-harness scoping

* no-mistakes(review): Clarify observed Kimi spinner whitespace contract

* no-mistakes(document): Clarify Kimi harness documentation

* fix: harden Kimi submission and spinner matching (kunchenguid#1058)

* fix kimi pointer submission and spinner conformance

* no-mistakes(review): Preserve Kimi submit target ownership guard

* feat(bin): add guarded Kimi turn-end wake (kunchenguid#1059)

* Add guarded Kimi turn-end hook

* no-mistakes(review): Require jq before installing Kimi turn-end hook

* no-mistakes(review): Expose jq inside isolated Kimi test fixtures

* no-mistakes(review): Preserve Kimi config boundaries during hook removal

* no-mistakes(review): Document Kimi removal newline safeguard

* no-mistakes(document): Document Kimi shared-home preservation

* fix(tmux): classify bordered composers across all rows (kunchenguid#1066)

* Fix structural tmux composer reading

* Verify Calm compatibility with Pi 0.82

* no-mistakes(review): Harden structural composer classification boundaries

* no-mistakes(review): Refresh composer and Kimi regression fixtures

* no-mistakes(review): Fail closed on unbounded composer edges

* no-mistakes(review): Enforce aligned composer geometry safely

* no-mistakes(review): Make composer ambiguity locale-safe

* no-mistakes(review): Preserve ambiguity through composer submission

* no-mistakes(review): Carry composer proof through retries

* no-mistakes(document): Document structural tmux composer delivery guarantees

* no-mistakes: apply CI fixes

* feat(bin): add verified pi-signed runtime adapter (kunchenguid#1145)

* feat: add verified pi-signed adapter

* no-mistakes(review): Correct pi-signed maintainer verification date

* no-mistakes(review): Correct remaining pi-signed verification dates

* no-mistakes(review): Preserve authoritative pi-signed runtime identity

* no-mistakes(document): Document pi-signed shared adapter semantics

* no-mistakes: apply CI fixes

* fix(pi): rearm watcher across session transitions (kunchenguid#1166)

* fix(pi): rearm watcher across same-process session transitions

Pi emits session_shutdown for ordinary /new, /resume, and /fork replacement
as well as terminal quit. The primary watcher extension latched a module-level
stopping flag on every shutdown, so a replacement session in the same process
could not arm monitoring until Pi restarted.

Own arm authority per session generation so only the active live generation
may start, stop, or rearm the child. Replacement sessions can arm again without
restarting Pi, stale prior-generation callbacks cannot mutate the active cycle,
and real quit still blocks late rearm.

* no-mistakes(review): Preserve Pi generation isolation and exit cleanup

* no-mistakes(document): Correct Pi watcher transition documentation

* feat: route crew dispatch using quota-window pace (kunchenguid#1172)

* Consume quota-axi pace signals in dispatch profile array selection.

Add quota-array-dispatch as the single owner of the pace-aware candidate
choice, keep AGENTS.md to the intake boundary and load trigger, and cover
the acceptance cases with sanitized schemaVersion 3 fixtures.

* no-mistakes(review): Stop and report genuine quota dispatch ties

* no-mistakes(document): Document quota pace freshness and uncertainty

* fix(brief): keep Definition-of-done heredocs parseable under stock Bash 3.2

Move each delivery mode's Definition-of-done text into its own function body
instead of an inline DOD=$(cat <<EOF ... EOF) assignment.

Stock macOS Bash 3.2 tracks quote state through a heredoc body while it scans
for the closing paren of a command substitution, so a single apostrophe in that
body makes the entire script unparseable - fm-brief.sh could not generate any
brief on macOS. A heredoc in a plain function body is parsed normally, so this
removes the bug class rather than avoiding apostrophes in the prose, and the
upstream wording that tests/fm-ask-user-authority.test.sh pins is restored
verbatim.

* fix(spawn): fail closed when task metadata cannot be recorded

The metadata write used a bare redirect, and fm-spawn.sh runs under set -u
without set -e, so a failed write was ignored: the script went on to print
'spawned <id> ...' and exit 0 with no state/<id>.meta on disk.

That durable record is what makes a worker supervisable - the watcher, recovery,
and teardown all locate a task through it - so a successful-looking spawn left a
live worker that nothing was tracking. Stop instead; the existing abort cleanup
still owns backend teardown.

tests/fm-backend-orca.test.sh already asserted this and was failing.

* fix(tests): settle merged-tree invariants and a Bash 3.2 concurrency fixture

Three merge-integration fixes and one inherited upstream defect.

Merge integration:
- Upstream's source-graph boundary (kunchenguid#939) pins which tests may carry production
  shellcheck context. This fork's two task-id tests sourced bin/fm-pr-lib.sh with
  a followed directive purely for validator access; both already disabled SC1091,
  so the directive only widened the graph. Switched to the non-following form and
  said why inline.
- Upstream's documentation-audience inventory did not classify this fork's
  capacity and user-journey-audit skills, which upstream never had. Both are
  agent-runtime, matching every other .agents/skills entry.

Inherited defect:
- tests/fm-session-start.test.sh read $BASHPID in each racer subshell. BASHPID
  arrived in Bash 4.0, so under stock macOS Bash 3.2 with set -u every racer
  aborted and the exactly-one-winner assertion always saw zero. The fallback
  execs sh in the substitution subshell so its PPID is that racer's own pid; $$
  would report the shared parent and defeat the race the test exists to prove.

* fix(watcher): reap the arm's child by blocking wait, not a fixed poll

The signal path polled for up to 2s before escalating to SIGKILL, on top of the
ledger-lock wait and the successor check. The HUP regression has an 8s budget, so
under a loaded parallel suite the arm could miss it and the test saw a timeout
instead of exit 129.

Block on the owned child instead: it returns the moment the child dies, and
SIGKILL stays as the backstop for a child that ignores TERM outright. This keeps
the property the poll was added for - the exact child is fully reaped before any
successor check, so a still-fresh dying child cannot read as a healthy successor
and suppress the arm-death alarm - while removing the fixed delay.

Verified with six concurrent runs of tests/fm-watcher-lock.test.sh, the load that
exposed it: 29/29 green in every run.

* fix(watcher): bound the TERM grace so a deferred trap cannot stall arm exit

Measured HUP-to-exit latency was 5-6s against the regression's 8s budget, because
the blocking wait sat through a whole FM_POLL interval: Bash defers the watcher's
TERM trap until its poll sleep returns.

Give TERM a 1s grace, then SIGKILL, then block to reap. The blocking wait still
guarantees the exact owned child is fully reaped before any successor check, so a
still-fresh dying child cannot read as a healthy successor and suppress the
arm-death alarm; the bound just stops a deferred trap from dominating shutdown.

Measured after the change: 1-2s across five runs, a 4-8x margin on the budget.

This replaces the previous commit's unbounded wait, which traded the original
fixed 2s poll for a stall of up to one poll interval - slower, not faster.

* docs(audiences): classify the dashboard-service doc

PR #8 added docs/dashboard-service.md, which upstream's documentation-audience
inventory did not know about, so the merged tree failed its own completeness
check. The doc states that it owns the architecture narrative, trust design, and
verification evidence while pointing at other owners for mechanics, which is the
maintainer-architecture audience rather than operator-current.

* no-mistakes(review): Publish spawn metadata atomically and correct cleanup scope

* no-mistakes(test): Captain: restore spawn metadata publication invariants

* no-mistakes(document): Refresh merged toolbelt and harness documentation

* no-mistakes: apply CI fixes

---------

Co-authored-by: Kun Chen <3233006+kunchenguid@users.noreply.github.com>
Co-authored-by: Johans Ballestar <47162770+Ballestar@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Israel Wilson <31997700+ICGNU3@users.noreply.github.com>
Co-authored-by: Kostadin Pop-Kochev <kostadin.popkocev@gmail.com>
Co-authored-by: Freudator86 <94322668+Freudator86@users.noreply.github.com>
Co-authored-by: Freudator86 <tim@allesknut.de>
Co-authored-by: vvizlan <steve.rule.wilson@gmail.com>
Co-authored-by: jjames27th-eng <jjames27th@gmail.com>
Co-authored-by: nithingm <nithingm31@gmail.com>
Co-authored-by: Christopher McKay <101884182+karotkriss@users.noreply.github.com>
Co-authored-by: ItsFlow <florian.eberhart.z@gmail.com>
ICGNU3 added a commit to ICGNU3/firstmate that referenced this pull request Jul 29, 2026
* docs: resolve five contract contradictions across AGENTS.md, README, and skills (kunchenguid#736)

* docs: resolve five contract contradictions

* no-mistakes(test): align owner-pointer assertions with reworded docs; skip absent shellcheck

* docs(harness): correct Grok exit guidance (kunchenguid#742)

* docs(harness): reverify grok exit command

* no-mistakes(test): Correct Grok exit resume attribution

* fix(watcher): bound stale wakes for parked crew (kunchenguid#743)

* 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

* fix(supervision): distinguish ordinary wakes from recovery (kunchenguid#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 (kunchenguid#745)

* 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

* feat(wake): enrich drained signals with bounded status context (kunchenguid#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 (kunchenguid#784)

* 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

* fix(send): treat opencode busy-queued composer state as submitted (kunchenguid#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

* fix(spawn): require two stable reads before accepting worktree path (kunchenguid#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>

* fix(bin): make watcher process identity immune to Linux wall-clock changes (kunchenguid#752)

* fix(watcher): stabilize Linux process identity

* no-mistakes(document): document FM_PROC_ROOT_OVERRIDE and Linux starttime identity rationale

* fix: prevent AFK idle stalls and stale run attribution (kunchenguid#758)

* 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(bin): allow safe teardown during watcher recovery (kunchenguid#750)

* 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

* feat(herdr): order presentation spaces while preserving focus (kunchenguid#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

* fix(bin): send literal config reread nudges after pushes (kunchenguid#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 (kunchenguid#797)

* 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

* fix(herdr): group projected children beneath owning parents (kunchenguid#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: keep local no-mistakes tests intent-targeted (kunchenguid#823)

* 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: add canonical timed test runner (kunchenguid#825)

* 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: surface main inventory gaps in Bearings (kunchenguid#830)

* 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 bounded concurrent test isolation proof (kunchenguid#832)

* 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: guard against missed secondmate reports (kunchenguid#834)

* 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: require pinned real-Herdr CI coverage (kunchenguid#838)

* 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

* feat: shard portable tests and add bounded local parallelism (kunchenguid#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

* fix: block primary-session delegation outside the fleet (kunchenguid#854)

* 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

* fix: install tasks-axi in portable CI shards (kunchenguid#866)

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(bin): make dispatch profiles quota aware (kunchenguid#867)

* 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

* Add built-in ahoy recap skill (kunchenguid#873)

* fix: preserve trustworthy Bearings data in partial snapshots (kunchenguid#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

* feat(pi): add session-local calm mode (kunchenguid#884)

* 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): prevent redundant watcher re-arms (kunchenguid#885)

* 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 (kunchenguid#895)

* 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 (kunchenguid#898)

* fix: execute every PR body compliance event

* no-mistakes(document): Document independent PR compliance events

* fix: exclude operational injections from ahoy boundaries (kunchenguid#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

* fix: canonically classify operational inputs across harnesses (kunchenguid#909)

* fix: type canonical operational inputs

* no-mistakes(document): Correct canonical operational-input documentation ownership

* fix: avoid generic secondmate start acknowledgements (kunchenguid#926)

* fix: avoid generic secondmate acknowledgements

* no-mistakes(document): Document sparse secondmate acknowledgement behavior

* no-mistakes: apply CI fixes

* fix(pi): make calm mode persistent and gapless (kunchenguid#927)

* fix(pi): preserve calm presentation across sessions

* no-mistakes(review): Fix Calm home fallback persistence

* no-mistakes(document): Clarify Calm gapless and export contracts

* no-mistakes: apply CI fixes

* fix(watch): retire merged PR polls after durable notification (kunchenguid#932)

* fix: retire merged PR polls after notification

* no-mistakes(review): Decouple PR retirement recovery from template updates

* no-mistakes(review): Recover pending PR retirements before poll migration

* no-mistakes(document): Document merged PR poll retirement contracts

* fix: refine scout intake and parallel dispatch (kunchenguid#934)

* Clarify intake evidence and overlap handling

* no-mistakes(review): Align scout guard with intake classification

* no-mistakes(document): Clarify scout documentation and intake ownership

* fix(pi): prevent duplicate assistant replies in Calm (kunchenguid#936)

* fix(pi): preserve operational follow-up semantics in Calm

* no-mistakes(document): Correct Calm operational-row visibility documentation

* perf(bin): shrink the ShellCheck source graph (kunchenguid#939)

* perf(lint): shrink shell source graph

* no-mistakes(review): Ensure lint workers terminate fully on cancellation

* no-mistakes(document): Repair stale lint documentation ownership

* fix(pi): remove Calm hidden-block gaps (kunchenguid#942)

* fix(pi): remove Calm hidden-block gaps

* no-mistakes(review): Validate Calm geometry against current viewport

* no-mistakes(review): Synchronize Calm geometry checks with reload completion

* fix: enforce contract boundaries for ask-user findings (kunchenguid#945)

* fix: escalate ask-user contract expansion

* no-mistakes(document): Point project management to authority owner

* docs: prefer direct operational paths (kunchenguid#946)

* fix(pi): hide operational user rows in Calm mode (kunchenguid#948)

* fix(pi): hide Calm operational user rows

* no-mistakes(review): Narrow Calm operational input suppression

* no-mistakes(review): Avoid Calm replay classifier subprocesses

* no-mistakes(document): docs: point Pi verification to Calm owner

* fix: relaunch missing second mates at session start (kunchenguid#950)

* fix(session-start): relaunch missing second mates

* fix(test): detect completed parallel workers

* no-mistakes(review): Isolate session-start recovery test cleanup

* no-mistakes(review): Complete backend-safe secondmate session recovery

* no-mistakes(review): Resolve Zellij task ownership before recovery

* no-mistakes(review): Recover relocated Zellij ghost tabs safely

* no-mistakes(review): Restore conservative Zellij recovery boundary

* no-mistakes(review): Reject malformed tmux recovery targets

* no-mistakes(document): Align secondmate recovery documentation

* no-mistakes: apply CI fixes

* fix(herdr): reclaim resumed task projections after restart (kunchenguid#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

* Teach Ahoy to surface open decisions (kunchenguid#968)

* Require shipshape routine acknowledgement (kunchenguid#969)

* docs: separate current guidance from verification evidence (kunchenguid#994)

* docs: separate current guides from verification

* no-mistakes(review): Restore Herdr 0.7.5 restart-reclaim verification evidence

* fix: preserve Claude watcher continuity across Stop hooks (kunchenguid#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

* fix(herdr): clean stale projections at session start (kunchenguid#996)

* 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

* fix: recover Claude supervision without watcher-status gate (kunchenguid#1001)

* fix: recover Claude supervision at session start

* fix: remove Claude watcher-status command gate

* no-mistakes(document): docs: remove stale continuity gate references

* fix: make quota-aware profile selection agent-owned (kunchenguid#1018)

* Replace quota dispatch selector instructions

* no-mistakes(review): Align bootstrap docs with agent-owned dispatch selection

* fix(bin): remove vestigial dispatch selector (kunchenguid#1026)

* 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

* docs(agents): drop superseded interim quota-window rule (kunchenguid#1039)

quota-axi 0.1.13 emits schemaVersion 2 with a quotaSemantics object per
provider, so the successor named in the interim rule has landed and the
rule's own removal condition is satisfied.

Keep the ownership clause so quota-axi remains the single owner of how
model or product windows relate to bounding account windows, and drop
the interim weakest-headroom instruction. The unknown-semantics case is
already covered by the existing requirement to stop and report a
candidate whose applicable quota data or interpretation cannot be
established.

Drop the matching assertion phrase from
tests/fm-instruction-owners.test.sh; the retained ownership phrase still
asserts.

* fix(tmux): scope busy detection and recognize current Claude turns (kunchenguid#1049)

* fix(tmux): scope Claude busy detection by harness

* no-mistakes(review): Separate verified and fallback busy signatures

* no-mistakes(test): Scope busy signatures to supplied harnesses

* no-mistakes(document): Document harness-scoped busy detection

* feat: add verified Kimi crewmate adapter (kunchenguid#1047)

* Add verified Kimi crewmate harness adapter

* no-mistakes(review): Scope Kimi moon detection to spinner lines

* no-mistakes(review): Match only complete Kimi spinner rows

* no-mistakes(review): Resolve Kimi binary portably before pane creation

* no-mistakes(document): Align Kimi adapter documentation

* no-mistakes(lint): Suppress false-positive ShellCheck warning for sourced watcher override

* Fix Kimi busy spinner detection

* no-mistakes(review): Recognize Kimi session-lock ancestry and holders

* no-mistakes(review): Scope pending-reply Kimi busy detection by harness

* no-mistakes(document): Correct Kimi spinner capture documentation

* no-mistakes(document): Clarify optional Kimi spinner whitespace

* no-mistakes(lint): Silence intentional pending-reply test stub warnings

* test: align rebased Kimi busy fixtures

* no-mistakes: apply CI fixes

* Reconcile Kimi busy detection after per-harness scoping

* no-mistakes(review): Clarify observed Kimi spinner whitespace contract

* no-mistakes(document): Clarify Kimi harness documentation

* fix: harden Kimi submission and spinner matching (kunchenguid#1058)

* fix kimi pointer submission and spinner conformance

* no-mistakes(review): Preserve Kimi submit target ownership guard

* feat(bin): add guarded Kimi turn-end wake (kunchenguid#1059)

* Add guarded Kimi turn-end hook

* no-mistakes(review): Require jq before installing Kimi turn-end hook

* no-mistakes(review): Expose jq inside isolated Kimi test fixtures

* no-mistakes(review): Preserve Kimi config boundaries during hook removal

* no-mistakes(review): Document Kimi removal newline safeguard

* no-mistakes(document): Document Kimi shared-home preservation

* fix(tmux): classify bordered composers across all rows (kunchenguid#1066)

* Fix structural tmux composer reading

* Verify Calm compatibility with Pi 0.82

* no-mistakes(review): Harden structural composer classification boundaries

* no-mistakes(review): Refresh composer and Kimi regression fixtures

* no-mistakes(review): Fail closed on unbounded composer edges

* no-mistakes(review): Enforce aligned composer geometry safely

* no-mistakes(review): Make composer ambiguity locale-safe

* no-mistakes(review): Preserve ambiguity through composer submission

* no-mistakes(review): Carry composer proof through retries

* no-mistakes(document): Document structural tmux composer delivery guarantees

* no-mistakes: apply CI fixes

* fix(brief): restore bash 3.2 parse of the no-mistakes definition-of-done

bin/fm-brief.sh has not parsed under stock macOS bash since kunchenguid#945:

  bin/fm-brief.sh: line 314: unexpected EOF while looking for matching `''
  bin/fm-brief.sh: line 388: syntax error: unexpected end of file

Bash 3.2 mis-scans a bare apostrophe inside a heredoc nested in $( ),
treating it as an unterminated quote and failing the entire file. The
apostrophe in "firstmate's authority check" is enough to trigger it.

Interpolate it through $APOS so the scanner never sees a bare quote. The
generated brief text is unchanged byte for byte, which matters because
tests/fm-ask-user-authority.test.sh pins that exact sentence.

Scope is one file. bin/fm-teardown.sh and every other script are untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5bvMEeW6SqECT3kg4Utry

---------

Co-authored-by: Kun Chen <3233006+kunchenguid@users.noreply.github.com>
Co-authored-by: Kostadin Pop-Kochev <kostadin.popkocev@gmail.com>
Co-authored-by: Freudator86 <94322668+Freudator86@users.noreply.github.com>
Co-authored-by: Freudator86 <tim@allesknut.de>
Co-authored-by: vvizlan <steve.rule.wilson@gmail.com>
Co-authored-by: jjames27th-eng <jjames27th@gmail.com>
Co-authored-by: nithingm <nithingm31@gmail.com>
Co-authored-by: Christopher McKay <101884182+karotkriss@users.noreply.github.com>
Co-authored-by: ItsFlow <florian.eberhart.z@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
ironerumi added a commit to ironerumi/firstmate that referenced this pull request Jul 29, 2026
* fix: block primary-session delegation outside the fleet (kunchenguid#854)

* 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

* fix: install tasks-axi in portable CI shards (kunchenguid#866)

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(bin): make dispatch profiles quota aware (kunchenguid#867)

* 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

* Add built-in ahoy recap skill (kunchenguid#873)

* fix: preserve trustworthy Bearings data in partial snapshots (kunchenguid#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

* feat(pi): add session-local calm mode (kunchenguid#884)

* 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): prevent redundant watcher re-arms (kunchenguid#885)

* 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 (kunchenguid#895)

* 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 (kunchenguid#898)

* fix: execute every PR body compliance event

* no-mistakes(document): Document independent PR compliance events

* fix: exclude operational injections from ahoy boundaries (kunchenguid#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

* fix: canonically classify operational inputs across harnesses (kunchenguid#909)

* fix: type canonical operational inputs

* no-mistakes(document): Correct canonical operational-input documentation ownership

* fix: avoid generic secondmate start acknowledgements (kunchenguid#926)

* fix: avoid generic secondmate acknowledgements

* no-mistakes(document): Document sparse secondmate acknowledgement behavior

* no-mistakes: apply CI fixes

* fix(pi): make calm mode persistent and gapless (kunchenguid#927)

* fix(pi): preserve calm presentation across sessions

* no-mistakes(review): Fix Calm home fallback persistence

* no-mistakes(document): Clarify Calm gapless and export contracts

* no-mistakes: apply CI fixes

* fix(watch): retire merged PR polls after durable notification (kunchenguid#932)

* fix: retire merged PR polls after notification

* no-mistakes(review): Decouple PR retirement recovery from template updates

* no-mistakes(review): Recover pending PR retirements before poll migration

* no-mistakes(document): Document merged PR poll retirement contracts

* fix: refine scout intake and parallel dispatch (kunchenguid#934)

* Clarify intake evidence and overlap handling

* no-mistakes(review): Align scout guard with intake classification

* no-mistakes(document): Clarify scout documentation and intake ownership

* fix(pi): prevent duplicate assistant replies in Calm (kunchenguid#936)

* fix(pi): preserve operational follow-up semantics in Calm

* no-mistakes(document): Correct Calm operational-row visibility documentation

* perf(bin): shrink the ShellCheck source graph (kunchenguid#939)

* perf(lint): shrink shell source graph

* no-mistakes(review): Ensure lint workers terminate fully on cancellation

* no-mistakes(document): Repair stale lint documentation ownership

* fix(pi): remove Calm hidden-block gaps (kunchenguid#942)

* fix(pi): remove Calm hidden-block gaps

* no-mistakes(review): Validate Calm geometry against current viewport

* no-mistakes(review): Synchronize Calm geometry checks with reload completion

* fix: enforce contract boundaries for ask-user findings (kunchenguid#945)

* fix: escalate ask-user contract expansion

* no-mistakes(document): Point project management to authority owner

* docs: prefer direct operational paths (kunchenguid#946)

* fix(pi): hide operational user rows in Calm mode (kunchenguid#948)

* fix(pi): hide Calm operational user rows

* no-mistakes(review): Narrow Calm operational input suppression

* no-mistakes(review): Avoid Calm replay classifier subprocesses

* no-mistakes(document): docs: point Pi verification to Calm owner

* fix: relaunch missing second mates at session start (kunchenguid#950)

* fix(session-start): relaunch missing second mates

* fix(test): detect completed parallel workers

* no-mistakes(review): Isolate session-start recovery test cleanup

* no-mistakes(review): Complete backend-safe secondmate session recovery

* no-mistakes(review): Resolve Zellij task ownership before recovery

* no-mistakes(review): Recover relocated Zellij ghost tabs safely

* no-mistakes(review): Restore conservative Zellij recovery boundary

* no-mistakes(review): Reject malformed tmux recovery targets

* no-mistakes(document): Align secondmate recovery documentation

* no-mistakes: apply CI fixes

* fix(herdr): reclaim resumed task projections after restart (kunchenguid#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

* Teach Ahoy to surface open decisions (kunchenguid#968)

* Require shipshape routine acknowledgement (kunchenguid#969)

* docs: separate current guidance from verification evidence (kunchenguid#994)

* docs: separate current guides from verification

* no-mistakes(review): Restore Herdr 0.7.5 restart-reclaim verification evidence

* fix: preserve Claude watcher continuity across Stop hooks (kunchenguid#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

* fix(herdr): clean stale projections at session start (kunchenguid#996)

* 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

* fix: recover Claude supervision without watcher-status gate (kunchenguid#1001)

* fix: recover Claude supervision at session start

* fix: remove Claude watcher-status command gate

* no-mistakes(document): docs: remove stale continuity gate references

* fix: make quota-aware profile selection agent-owned (kunchenguid#1018)

* Replace quota dispatch selector instructions

* no-mistakes(review): Align bootstrap docs with agent-owned dispatch selection

* fix(bin): remove vestigial dispatch selector (kunchenguid#1026)

* 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

* docs(agents): drop superseded interim quota-window rule (kunchenguid#1039)

quota-axi 0.1.13 emits schemaVersion 2 with a quotaSemantics object per
provider, so the successor named in the interim rule has landed and the
rule's own removal condition is satisfied.

Keep the ownership clause so quota-axi remains the single owner of how
model or product windows relate to bounding account windows, and drop
the interim weakest-headroom instruction. The unknown-semantics case is
already covered by the existing requirement to stop and report a
candidate whose applicable quota data or interpretation cannot be
established.

Drop the matching assertion phrase from
tests/fm-instruction-owners.test.sh; the retained ownership phrase still
asserts.

* fix(tmux): scope busy detection and recognize current Claude turns (kunchenguid#1049)

* fix(tmux): scope Claude busy detection by harness

* no-mistakes(review): Separate verified and fallback busy signatures

* no-mistakes(test): Scope busy signatures to supplied harnesses

* no-mistakes(document): Document harness-scoped busy detection

* feat: add verified Kimi crewmate adapter (kunchenguid#1047)

* Add verified Kimi crewmate harness adapter

* no-mistakes(review): Scope Kimi moon detection to spinner lines

* no-mistakes(review): Match only complete Kimi spinner rows

* no-mistakes(review): Resolve Kimi binary portably before pane creation

* no-mistakes(document): Align Kimi adapter documentation

* no-mistakes(lint): Suppress false-positive ShellCheck warning for sourced watcher override

* Fix Kimi busy spinner detection

* no-mistakes(review): Recognize Kimi session-lock ancestry and holders

* no-mistakes(review): Scope pending-reply Kimi busy detection by harness

* no-mistakes(document): Correct Kimi spinner capture documentation

* no-mistakes(document): Clarify optional Kimi spinner whitespace

* no-mistakes(lint): Silence intentional pending-reply test stub warnings

* test: align rebased Kimi busy fixtures

* no-mistakes: apply CI fixes

* Reconcile Kimi busy detection after per-harness scoping

* no-mistakes(review): Clarify observed Kimi spinner whitespace contract

* no-mistakes(document): Clarify Kimi harness documentation

* fix: harden Kimi submission and spinner matching (kunchenguid#1058)

* fix kimi pointer submission and spinner conformance

* no-mistakes(review): Preserve Kimi submit target ownership guard

* feat(bin): add guarded Kimi turn-end wake (kunchenguid#1059)

* Add guarded Kimi turn-end hook

* no-mistakes(review): Require jq before installing Kimi turn-end hook

* no-mistakes(review): Expose jq inside isolated Kimi test fixtures

* no-mistakes(review): Preserve Kimi config boundaries during hook removal

* no-mistakes(review): Document Kimi removal newline safeguard

* no-mistakes(document): Document Kimi shared-home preservation

* fix(tmux): classify bordered composers across all rows (kunchenguid#1066)

* Fix structural tmux composer reading

* Verify Calm compatibility with Pi 0.82

* no-mistakes(review): Harden structural composer classification boundaries

* no-mistakes(review): Refresh composer and Kimi regression fixtures

* no-mistakes(review): Fail closed on unbounded composer edges

* no-mistakes(review): Enforce aligned composer geometry safely

* no-mistakes(review): Make composer ambiguity locale-safe

* no-mistakes(review): Preserve ambiguity through composer submission

* no-mistakes(review): Carry composer proof through retries

* no-mistakes(document): Document structural tmux composer delivery guarantees

* no-mistakes: apply CI fixes

* feat(bin): add verified pi-signed runtime adapter (kunchenguid#1145)

* feat: add verified pi-signed adapter

* no-mistakes(review): Correct pi-signed maintainer verification date

* no-mistakes(review): Correct remaining pi-signed verification dates

* no-mistakes(review): Preserve authoritative pi-signed runtime identity

* no-mistakes(document): Document pi-signed shared adapter semantics

* no-mistakes: apply CI fixes

* fix(pi): rearm watcher across session transitions (kunchenguid#1166)

* fix(pi): rearm watcher across same-process session transitions

Pi emits session_shutdown for ordinary /new, /resume, and /fork replacement
as well as terminal quit. The primary watcher extension latched a module-level
stopping flag on every shutdown, so a replacement session in the same process
could not arm monitoring until Pi restarted.

Own arm authority per session generation so only the active live generation
may start, stop, or rearm the child. Replacement sessions can arm again without
restarting Pi, stale prior-generation callbacks cannot mutate the active cycle,
and real quit still blocks late rearm.

* no-mistakes(review): Preserve Pi generation isolation and exit cleanup

* no-mistakes(document): Correct Pi watcher transition documentation

* feat: route crew dispatch using quota-window pace (kunchenguid#1172)

* Consume quota-axi pace signals in dispatch profile array selection.

Add quota-array-dispatch as the single owner of the pace-aware candidate
choice, keep AGENTS.md to the intake boundary and load trigger, and cover
the acceptance cases with sanitized schemaVersion 3 fixtures.

* no-mistakes(review): Stop and report genuine quota dispatch ties

* no-mistakes(document): Document quota pace freshness and uncertainty

* fix: adapt Grok Stop continuation and harden endpoint cleanup (kunchenguid#1171)

* fix(grok): adapt Stop continuation to runtime capability

* no-mistakes(review): Reject ambiguous Grok Stop payloads

* no-mistakes(review): Reject duplicate Grok fields and accept spaced tmux sessions

* no-mistakes(review): Enforce exact tmux cleanup selectors

* no-mistakes(test): Fix historical tmux fixture and validate Grok Stop

* no-mistakes: apply CI fixes

* fix: restore stock macOS Bash 3.2 brief scaffolding (kunchenguid#1093)

* fix(brief): make DOD scaffolding parse-safe on stock macOS Bash 3.2

fm-brief.sh built each Definition-of-done block and the not-enabled
Herdr declaration with `VAR=$(cat <<EOF ... EOF)`. On Bash 3.2 (macOS
/bin/bash) the lexer scans for the command substitution's closing `)`
textually and tracks quote state through the heredoc body, so a single
apostrophe, unbalanced quote, or unbalanced paren in that prose breaks
parsing of the whole script. Every ship-brief scaffold (no-mistakes,
direct-PR, local-only) failed with `unexpected EOF while looking for
matching )`. Bash 4+ parses it fine, so the breakage stayed invisible
everywhere except stock macOS.

Replace all four command-substitution heredocs with
`IFS= read -r -d '' VAR <<EOF || true`. That removes the `$(...)`
wrapper and the entire defect class regardless of future prose, and
preserves the variable expansion the direct-PR and local-only bodies
need. `read` keeps the heredoc's trailing newline that `$(...)` used to
strip, so trim one newline to keep every generated brief byte-identical
to prior output.

Guard the structure, not one historical phrase: a new test rejects any
heredoc nested in a command substitution anywhere in fm-brief.sh, where
the old assertion pinned a single apostrophe phrase and so missed the
reintroduction. Extend the stock-macOS Bash CI job from parsing one
script to the whole maintained shell surface (bin/*.sh,
bin/backends/*.sh, tests/*.sh), matching bin/fm-lint.sh's canonical file
set so parse scope and lint scope cannot drift apart.

* no-mistakes(review): Captain: harden Bash structure and inventory guards

* no-mistakes(document): Align stock macOS Bash contributor checks

* no-mistakes(lint): Suppress deliberate SC2016 literal fixture warnings

* test: stabilize tmux teardown conformance baseline (kunchenguid#1209)

* fix(test): pin teardown tmux baseline to historical kill selectors

merge-base HEAD main collapses to HEAD after the exact-selector change
lands on the default branch, so the old teardown fixture was accidentally
exercising current exact targets. Resolve a content-historical permissive
tmux adapter from first-parent history and force that post-squash topology
inside the conformance case so main and feature branches keep the same
old-vs-new contract.

* no-mistakes(lint): Suppress intentional literal-pattern ShellCheck warnings

* docs: slim quota-array-dispatch to the pace selection core (kunchenguid#1197)

Cut the runtime skill to the compact pace-aware selection procedure plus
minimum owner pointers. Keep every distinct decision rule and move expanded
acceptance scenarios to deterministic fixture ownership assertions.

Size: 170/1374/10187 -> 63/544/4068 (about 63%/60%/60% reduction).

* feat(bin): inherit backend config into secondmate homes (kunchenguid#1219)

* Inherit config/backend into secondmate homes with deliberate-override preservation

Add backend to the shared inheritable config allowlist so launch, locked
bootstrap, and config-push converge a primary pin into secondmate homes as each
home local future-spawn default. Track last-inherited bytes in a private state
provenance marker so deliberate per-home overrides survive present and absent
primary convergence, keep --backend and FM_BACKEND stronger, and extend the
existing inheritance tests plus docs and skill claims.

* no-mistakes(review): Preserve equal unprovenanced backend overrides

* no-mistakes(review): Preserve symlink overrides and verify spawn precedence

* no-mistakes(review): Snapshot backend inheritance for consistent provenance

* no-mistakes(review): Simplify backend inheritance to primary-authoritative convergence

* no-mistakes(document): Document inherited backend override preservation

* fix: restore primary-authoritative backend inheritance after document regression

The document step reintroduced provenance and deliberate per-home override
semantics after review had simplified config/backend to plain primary-authoritative
allowlist membership. Restore the primary-always-wins path: present overwrites,
absent removes, no provenance marker, and docs/tests match that contract.

* no-mistakes(review): Add divergent backend precedence regression fixtures

* no-mistakes(document): Document backend inheritance contract

* fix(pi): remove Calm's upper version ceiling (kunchenguid#1226)

* fix(pi): remove Calm's exclusive Pi upper-version ceiling

tests/fm-calm-pi-extension.test.sh gated on a closed PI_COMPAT_VERSIONS
allowlist ("0.81.1 0.82.0") that refused any other installed Pi, and docs
described that range as "supported" rather than verified evidence. The
Calm CHANGELOG shows no API introduced at either version, so there is no
evidence for a real minimum; the presentation adapters already probe the
exact method they patch rather than checking a version.

Replace the allowlist with dated version evidence that never rejects a
newer Pi, and make each presentation adapter degrade independently with
a diagnostic if a future Pi removes its API, instead of the whole Calm
extension failing to load. Rewrite the feasibility doc's "Pi 0.81.1
through 0.82.0" phrasing to state it as verified evidence, not a
ceiling.

* no-mistakes(review): Probe missing Calm adapter exports safely

* no-mistakes(document): Document Calm's unbounded Pi compatibility

* fix(bin): allow session-local todo tools in the subagent guard (kunchenguid#1204)

* fix(guard): allow session-local todo tools in the primary

The delegation-shape guard denied TaskCreate and TaskUpdate because their
normalized names contain the `task` stem. Those tools write only the harness's
session-local todo list, which has no executor: it spawns no agent, allocates
no worktree, registers no schedule, and starts nothing that outlives the
session. That is not the unaccounted work the guard exists to stop, so the stem
match was a false positive, and the deny text told the primary to run
bin/fm-brief.sh and bin/fm-spawn.sh to create a todo entry.

Add a separately-reasoned PLAN_ONLY_TOOLS exact-name exclusion rather than
widening OBSERVE_ONLY_TOOLS, whose documented contract is tools that only
observe or stop existing work. Both lists stay exact-name so neither can widen
by substring.

Tests cover the two allowed names and six near-miss names that a substring or
shortened-stem widening would release; both mutations were watched red.

* no-mistakes(review): drop session-local todo tools from recommended deny list

* no-mistakes: apply CI fixes

* fix(session-lock): resolve Claude bg-spare ancestry to the outermost claude pid (kunchenguid#1206)

* fix(session-lock): resolve Claude bg-spare ancestry to the outermost claude pid

fm_harness_ancestry_pid() previously returned the first ancestor process
whose command matched a verified harness name. Claude Code's Stop hook
fires as a bg-spare worker several levels below the session's actual
lock-owning claude process (hook shell -> claude bg-spare ->
claude bg-pty-host -> claude -> claude(lock)), so the first match was
the bg-spare worker, not the lock owner. fm_session_lock_owned_by_self()
then never matched state/.lock, and the Claude Stop auto-arm silently
treated its own primary session as an unrelated live owner and never
armed the watcher.

The walk now keeps going past a claude-named match, looking for a still
more ancestral claude-named match, and stops the instant a non-match
follows an already-found match (bounding it to a contiguous run rather
than the literal ancestry top, so an unrelated claude-named process
further up the real process tree is never mistaken for part of this
session's own nested chain). Every other harness keeps the original
first-match-wins behavior, since e.g. Pi's shared signed-wrapper
ancestry actually holds the session at the inner engine pid, not an
outer wrapper pid. Hop limit raised from 8 to 16 to cover the deeper
bg-spare chain.

* no-mistakes(review): Add nested-claude-ancestry regression test; fix nudge doc depth claim

* no-mistakes: apply CI fixes

* fix: conferma l'avvio del watcher su Windows/MSYS (kunchenguid#1212)

* fix: confirm watcher startup on MSYS

* no-mistakes(review): gate MSYS arm ready timeout, cache uname, harden locale test

* no-mistakes(review): validate OpenCode ready timeout, make uname cache internal

* fix(spawn): forward CLAUDE_CONFIG_DIR to claude crewmates (kunchenguid#1195)

* fix(spawn): forward firstmate's CLAUDE_CONFIG_DIR to claude crewmates

Crewmate panes are created by a long-lived tmux/herdr daemon that does not
inherit firstmate's current environment. When firstmate runs under a non-default
CLAUDE_CONFIG_DIR (for example a work-vs-personal subscription split), a bare
`claude` in the crewmate pane fell back to the default ~/.claude store and
launched unauthenticated, blocking the crewmate before it could do any work.

fm-spawn now prefixes the claude launch with firstmate's own resolved
CLAUDE_CONFIG_DIR when set, so the crewmate uses the same credential/config
store firstmate is authenticated with. An unset value is the single-store
default and adds no prefix; non-claude harnesses are unaffected.

Adds three tests in fm-spawn-dispatch-profile.test.sh (forwarded-when-set,
omitted-when-unset, non-claude-ignored) and pins CLAUDE_CONFIG_DIR in the test
helper so launch assertions no longer depend on the developer's environment.

* no-mistakes: apply CI fixes

* no-mistakes(review): harden teardown kind=adhoc endpoint-gate exemption

* no-mistakes(document): document ad-hoc teardown exemption and adhoc meta fields

* no-mistakes(document): pin kind=adhoc cleanup carve-out with regression evidence

---------

Co-authored-by: ItsFlow <florian.eberhart.z@gmail.com>
Co-authored-by: Kun Chen <3233006+kunchenguid@users.noreply.github.com>
Co-authored-by: Christopher McKay <101884182+karotkriss@users.noreply.github.com>
Co-authored-by: Daniel Kuykendall IV <danielkuykendall23@gmail.com>
Co-authored-by: Trillium Smith <Spiteless@gmail.com>
Co-authored-by: Unknownzed <45267749+Unknownzed@users.noreply.github.com>
Co-authored-by: lhalbert <lucashalbert@users.noreply.github.com>
allstargg added a commit to allstargg/firstmate that referenced this pull request Jul 30, 2026
* fix: prevent no-mistakes gate agents from driving the fleet (#518)

* feat: contain no-mistakes gate agents from driving the fleet

Add bin/fm-gate-refuse-lib.sh, sourced at the top of fm-spawn/fm-send/
fm-teardown before any fleet mutation. It fails closed when NO_MISTAKES_GATE
is set, and via an unspoofable git-common-dir backstop when invoked from a
no-mistakes gate worktree (.no-mistakes/repos/*.git) even with the marker
unset. A normal firstmate session has neither signal and is unaffected.

Set disable_project_settings: true in the tracked .no-mistakes.yaml so the
installed pipeline neutralizes gate agents' project instructions for this repo
(trusted-only, honored from the default branch).

firstmate's own suite runs from a gate worktree during validation, so the
shared test helpers set FM_GATE_REFUSE_BYPASS=1 to exempt it; the dedicated
tests/fm-gate-refuse.test.sh strips it to verify real refusal.

* no-mistakes(review): Captain, refuse empty no-mistakes gate markers

* no-mistakes(document): Document no-mistakes gate authority boundary

* fix: guard secondmate primary sessions from blind turn ends (#505)

* fix: guard secondmate own-home turn ends

Remove the .fm-secondmate-home early-exit in fm-turnend-guard.sh so the
'no turn ends blind' backstop fires in a secondmate's own primary session,
matching the cd-guard's scope: the own home is guarded, child crew/scout
worktrees stay exempt via the retained git-dir/git-common-dir test. This
was pure scoping from the guard's primary-only origin and guarded against
no secondmate-specific hazard.

Add secondmate regression tests (blind-turn block, idle-by-default,
stop_hook_active loop guard, deferred-death recovery loop, child-worktree
exemption) and record the autonomous background-notify re-invoke
measurement (Claude Code 2.1.207, 11s) in docs/turnend-guard.md.

* no-mistakes(document): Correct secondmate guard documentation, captain

* fix: force-include marked secondmate homes in turn-end guard

The prior remove-only form (just deleting the .fm-secondmate-home check)
left the DEFAULT secondmate topology unguarded: a treehouse-leased home is
a linked git worktree (git-dir != git-common-dir), which the retained
git-dir exemption still skipped, so its own primary session could still end
a turn blind. Invert the marker: a genuinely-marked home is force-included
as a guarded primary (treehouse-leased linked OR git-cloned plain), and the
git-dir exemption applies only to UNMARKED child worktrees. Marker
validation (regular non-symlink file, non-empty id-token content) blocks a
stray or empty marker from spoofing inclusion.

Add real linked-worktree regression tests: a treehouse-leased LINKED
secondmate home is guarded, a stray/empty marker stays exempt, and the
unmarked child worktree stays exempt - the topology the plain git-init
fixtures masked. Predicates, in-flight gate, and loop guard untouched.

* fix: force ASCII collation in secondmate marker validation

Add a function-scoped local LC_ALL=C in fm_root_is_secondmate_home so the
[A-Za-z0-9._-] id allowlist matches under C collation, not the ambient
locale - a locale-crafted non-ASCII marker id can no longer slip through
the range match and spoof force-inclusion of a linked child worktree.
Add a regression test proving a non-ASCII marker id is rejected and the
linked worktree stays exempt.

* no-mistakes(test): fix backend baseline gate-refusal dependency

* no-mistakes(document): Correct secondmate turn-end guard documentation

* fix: make bootstrap diagnostics backend-aware (#519)

* fix: make bootstrap required-tool detection backend-aware

Bootstrap demanded tmux and treehouse for every backend except orca, so a
herdr/zellij/cmux home with tmux absent was wrongly told MISSING: tmux.

Required tools now follow the resolved backend via the single-owner
fm_backend_required_tools helper (bin/fm-backend.sh): each backend's own
session-provider CLI, jq for the JSON-emitting adapters (herdr/zellij/cmux),
and treehouse for session-provider-only backends (orca owns its worktree).
The treehouse lease-support check is gated to backends that use treehouse.

Adds install hints for herdr/zellij/cmux, regression tests for the full
backend dependency matrix (herdr-without-tmux repro plus each boundary),
and updates the authoritative Toolchain docs.

* no-mistakes(review): Captain, prevent executing Herdr install guidance

* no-mistakes(review): Captain, harden backend-aware bootstrap diagnostics

* no-mistakes(review): Captain, separate manual dependency remediation

* no-mistakes(review): Captain, align bootstrap diagnostic consumers

* no-mistakes(document): Align backend adapter dependency comments

* fix: preserve follow-up platform context after inbox cleanup (#520)

* fix: recover X/Discord follow-up platform after inbox cleanup

A milestone follow-up posted directly by request_id after the inbox was
drained - and with no task link, because one persistent secondmate's single
x_request slot collides across concurrent requests - resolved platform only
from the local inbox, so a >280 Discord reply silently defaulted to the X
280-char budget and threaded as (1/2).

- fm-x-poll records a durable per-request reply context
  (state/x-context/<rid>.json) at stash time, keyed by request_id so
  concurrent requests never overwrite each other; it survives inbox cleanup
  and restart.
- fm-x-reply resolves platform/budget through registry -> inbox -> relay
  (the relay lookup confined to a live follow-up), recovering the original
  platform independent of task-link availability.
- Fail-safe: a follow-up whose platform/budget cannot be authoritatively
  resolved and that would split is refused (exit 8) and held for retry,
  never wrongly split; fm-x-followup keeps the link on that exit.
- fm-x-dismiss clears the durable context for a dismissed mention.

Refactors reply-context extraction into a single owner and adds regression
coverage for all four cases.

* no-mistakes(review): Captain, fail closed on incomplete follow-up context

* no-mistakes(review): Captain, bound X context registry retention

* no-mistakes(review): Captain, align context retention with answer binding

* no-mistakes(document): Align X follow-up context documentation

* no-mistakes(document): Align durable X follow-up documentation

* fix: preserve secondmate routing markers in terminal sends (#533)

* fix: preserve secondmate routing markers

* no-mistakes(review): Captain, preserve trailing newlines in marked secondmate sends

* no-mistakes(test): Captain, tolerate bootstrap timeout elapsed drift

* no-mistakes(document): Refresh Herdr marker documentation

* fix: align Grok effort handling with 0.2.99 (#527)

* fix: align grok effort docs and spawn with 0.2.99 ceiling

grok 0.2.99 accepts only low|medium|high for --reasoning-effort and
rejects both xhigh and max. Omit unsupported values on spawn, flag them
in crew-dispatch validation, and update harness-adapters.

* no-mistakes(test): Captain: refresh gotmp teardown fixture dependencies

* no-mistakes(document): Clarify Grok effort documentation ownership

* fix: derive bearings from authoritative secondmate state (#555)

* fix: make bearings use secondmate home state

* test: anonymize bearings fixtures

* no-mistakes(review): Bound parent activity evidence scans, captain

* no-mistakes(review): Preserve structured secondmate authority and bounds, captain

* no-mistakes(review): Preserve registry completeness and child inventory, captain

* no-mistakes(review): Reconcile parent evidence by verb and key, captain

* no-mistakes(review): Treat unkeyed parent evidence as inconclusive, captain

* no-mistakes(document): Document bearings local snapshot and PR opt-in

* no-mistakes(lint): Fix fleet snapshot ShellCheck findings

* no-mistakes: apply CI fixes

* fix: restore fleet snapshots on stock macOS Bash (#578)

* fix: restore stock macOS snapshot parsing

* no-mistakes(document): Clarify Linux gate and macOS CI coverage

* fix(afk): make Pi escalation and return catch-up reliable (#587)

* fix: close away-mode blocker supervision gap

* no-mistakes(review): Gate teardown retries and verify U+2063 dedupe

* no-mistakes(test): Fail closed on incomplete Pi composer separators

* no-mistakes(document): Document Pi composer recognition and return gating

* feat: support Pi max reasoning profiles (#537)

* support Pi max thinking profiles

* no-mistakes(review): Captain, allow Pi max dispatch profiles

* chore: no-mistakes(document): Clarify yolo response ownership (#595)

* Clarify validation response ownership

* no-mistakes(document): Clarify yolo response ownership

* feat: establish instruction ownership foundation (#619)

* Add instruction owners foundation

* no-mistakes(document): Refresh project-management owner pointers

* fix: compress Firstmate contract and enforce delivery rigor ownership (#626)

* docs: compress firstmate operating contract

* docs: make delivery rigor single-owner

PR B already removed personal and stacked review requirements, but it did not explicitly assign rigor to the selected delivery path or forbid risk-based manual clean gates. That gap still permitted the Hi Bit inversion.

* no-mistakes(review): Honor configured merge authority across faster delivery paths

* no-mistakes(document): Align docs with compressed operating contract

* feat: add durable captain decision holds (#593)

* Add durable captain decision holds

* no-mistakes(review): Validate decision hold retries and origin paths

* no-mistakes(review): Enforce durable decision lifecycle boundaries

* no-mistakes(review): Harden decision display and partial retry recovery

* no-mistakes(test): Update scout teardown fixtures for decision inventory

* no-mistakes(document): Align decision lifecycle and scout teardown documentation

* no-mistakes: apply CI fixes

* no-mistakes(review): Reconcile terminal decision holds

* no-mistakes(document): Align captain decision-hold documentation

* fix(bin): harden PR check artifacts (#556)

* fix: harden PR check artifacts

* fix: close PR check migration gaps

* fix: close PR check retry gaps

* fix: clarify migration outcomes and ESM boundary

* fix: keep failed migrations authoritative

* test: use inert PR validation fixtures

* no-mistakes(review): Reserve noncanonical PR quarantine namespace

* no-mistakes(review): Prevalidate final PR-check teardown artifacts

* no-mistakes(review): Preserve X metadata and validate teardown IDs

* no-mistakes(review): Initialize migration state before watcher exclusion

* no-mistakes(review): Isolate failed poll migrations from bootstrap recovery

* no-mistakes(review): Allow safe polling during incomplete private repairs

* no-mistakes(review): Authenticate watcher checks at execution time

* no-mistakes(review): Preserve custom checks with hash-bound registration

* no-mistakes(review): Clean custom check snapshots on watcher signals

* no-mistakes(review): Stop watcher checks promptly on signals

* no-mistakes(review): Terminate watcher check groups before cleanup

* no-mistakes(document): Correct stale X-mode watcher documentation

* fix: drain returned watcher check groups

* no-mistakes(review): Harden quarantine links and recover validated replacement polls

* no-mistakes(review): Preserve X mode across shim version transitions

* no-mistakes(review): Refresh legacy X shims before marker short-circuits

* no-mistakes(document): Correct persisted PR-check artifact documentation

* no-mistakes(document): Correct stale PR-check documentation

* fix: bind PR poll repair provenance

* no-mistakes(review): Enforce single-link ownership for custom check artifacts

* no-mistakes(review): Preserve private checks, X polling, and lifecycle IDs

* no-mistakes(review): Separate task creation and legacy teardown validation

* no-mistakes(review): Restore safe legacy operations and teardown validation

* no-mistakes(review): Disambiguate migration obligations and preserve legacy retries

* no-mistakes(review): Preserve fail-closed diagnostics and legacy quarantine evidence

* no-mistakes(review): Reconcile legacy migration retries and teardown collisions

* no-mistakes(review): Force legacy namespace reconciliation before marker short-circuits

* no-mistakes(document): Document private poll artifact safety contracts

* no-mistakes(lint): Suppress intentional literal-dollar lint finding

* fix: migrate historical X poll identity

* fix: harden PR check artifacts

* no-mistakes(review): Preserve fail-closed diagnostics and legacy quarantine evidence

* no-mistakes(review): Reconcile legacy migration retries and teardown collisions

* fix: migrate historical X poll identity

* no-mistakes(review): Harden X-mode artifact publication against symlink corruption

* no-mistakes(review): Guard X artifact publication

* no-mistakes(review): Enforce private X artifact reads

* no-mistakes(test): Fix backend compatibility fixture dependencies

* no-mistakes(document): Refresh PR-check documentation

* no-mistakes(lint): Remove unused x-mode test locals

* no-mistakes: apply CI fixes

* fix(bin): compact session-start backlog digest (#636)

* fix: compact session-start backlog digest

* no-mistakes(test): Fix legacy backend fixture helper

* no-mistakes(test): Fix watcher exit wait helper

* no-mistakes(document): Document compact backlog digest

* fix: dedupe stale watcher guard banners (#637)

* fix: dedupe stale watcher guard banner

* no-mistakes(review): Keep read-only guard state nonmutating

* no-mistakes(document): Clarify stale watcher docs

* fix(bin): balance bearings landed baseline (#640)

* fix: balance bearings landed defaults

* no-mistakes(document): Document balanced landed baseline

* no-mistakes: apply CI fixes

* fix: clarify captain-facing translation contract (#644)

* docs: clarify captain-facing translation contract

* no-mistakes(review): Restore runtime fallback mandate

* no-mistakes(document): Align Bearings translation wording

* fix(bin): make bootstrap output and nudges deterministic (#646)

* fix: make bootstrap nudges deterministic

* no-mistakes(review): Honor state override for bootstrap nudges

* no-mistakes(review): Update benign bootstrap documentation labels

* no-mistakes(review): Validate bootstrap nudge retry markers

* no-mistakes(document): Align bootstrap nudge documentation

* no-mistakes: apply CI fixes

* docs(secondmate-provisioning): clarify concise registry ownership (#649)

* Clarify concise secondmate registry contract

* no-mistakes(review): Expand secondmate registry boilerplate coverage

* no-mistakes(document): Point route docs to owner

* fix(bin): strip quoted blocked_by values during decision hold resolve (#654)

* fix(bin): strip quotes on blocked_by in decision-hold resolve

tasks-axi quotes multi-entry blocked_by as "a,b,c", so the comma-boundary
membership test only matched middle elements. Strip surrounding quotes
before matching so first and last hold ids resolve correctly.

* no-mistakes(document): Refresh decision-hold regression evidence

* feat(secondmate): inherit shared captain preferences (#656)

* feat(secondmate): inherit shared captain preferences

* no-mistakes(review): Honor shared captain data overrides

* no-mistakes(review): Honor bootstrap data override registry

* no-mistakes(document): Refresh shared inheritance docs

* no-mistakes(document): Clarify inherited local-material docs

* feat: gate local agent secret injection (#658)

* feat(spawn): gate local agent secret injection

* fix(spawn): align final Keychain slot

* no-mistakes: apply CI fixes

* test: isolate Herdr autodetect smoke sessions (#662)

* test: isolate herdr autodetect smoke session

* no-mistakes(review): Restored autodetect smoke gate bypass

* no-mistakes(test): Harden Herdr lab provisioning

* no-mistakes(document): Refresh Herdr lab docs

* docs: adopt under way for active work (#666)

* Revert "feat: gate local agent secret injection (#658)" (#668)

This reverts commit c27135cd9d35bc4c237d49b3b374da31fbd52eef.

* fix(pi): distinguish stale locks when arming watcher (#681)

* 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 house vocabulary (#685)

* fix: accept secondmate as house vocabulary

* no-mistakes(test): Update captain vocabulary contract test

* no-mistakes(document): Align secondmate documentation vocabulary

* fix(bin): parse handoff homes after registry parentheticals (#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 (#687)

* feat: add native session-start nudges

* no-mistakes(document): Document nudge script inventory

* docs: call built-in defaults the firstmate repo, not template (#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.

* fix(bin): repair fm-brief.sh parse error and harden set -u array expansion (#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>
#166 apostrophe regression cannot return unnoticed.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(bin): keep watcher supervision continuous across child cycles (#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: fetch current PR head for review diffs (#722)

* 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

* docs: resolve five contract contradictions across AGENTS.md, README, and skills (#736)

* docs: resolve five contract contradictions

* no-mistakes(test): align owner-pointer assertions with reworded docs; skip absent shellcheck

* docs(harness): correct Grok exit guidance (#742)

* docs(harness): reverify grok exit command

* no-mistakes(test): Correct Grok exit resume attribution

* fix(watcher): bound stale wakes for parked crew (#743)

* 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

* fix(supervision): distinguish ordinary wakes from recovery (#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 (#745)

* 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

* feat(wake): enrich drained signals with bounded status context (#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 (#784)

* 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

* fix(send): treat opencode busy-queued composer state as submitted (#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

* fix(spawn): require two stable reads before accepting worktree path (#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>

* fix(bin): make watcher process identity immune to Linux wall-clock changes (#752)

* fix(watcher): stabilize Linux process identity

* no-mistakes(document): document FM_PROC_ROOT_OVERRIDE and Linux starttime identity rationale

* fix: prevent AFK idle stalls and stale run attribution (#758)

* 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(bin): allow safe teardown during watcher recovery (#750)

* 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

* feat(herdr): order presentation spaces while preserving focus (#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

* fix(bin): send literal config reread nudges after pushes (#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 (#797)

* 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

* fix(herdr): group projected children beneath owning parents (#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: keep local no-mistakes tests intent-targeted (#823)

* 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: add canonical timed test runner (#825)

* 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: surface main inventory gaps in Bearings (#830)

* 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 bounded concurrent test isolation proof (#832)

* 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: guard against missed secondmate reports (#834)

* 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: require pinned real-Herdr CI coverage (#838)

* 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

* feat: shard portable tests and add bounded local parallelism (#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

* fix: block primary-session delegation outside the fleet (#854)

* 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

* fix: install tasks-axi in portable CI shards (#866)

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(bin): make dispatch profiles quota aware (#867)

* 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

* Add built-in ahoy recap skill (#873)

* fix: preserve trustworthy Bearings data in partial snapshots (#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

* feat(pi): add session-local calm mode (#884)

* 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): prevent redundant watcher re-arms (#885)

* 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 (#895)

* 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 (#898)

* fix: execute every PR body compliance event

* no-mistakes(document): Document independent PR compliance events

* fix: exclude operational injections from ahoy boundaries (#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

* fix: canonically classify operational inputs across harnesses (#909)

* fix: type canonical operational inputs

* no-mistakes(document): Correct canonical operational-input documentation ownership

* fix: avoid generic secondmate start acknowledgements (#926)

* fix: avoid generic secondmate acknowledgements

* no-mistakes(document): Document sparse secondmate acknowledgement behavior

* no-mistakes: apply CI fixes

* fix(pi): make calm mode persistent and gapless (#927)

* fix(pi): preserve calm presentation across sessions

* no-mistakes(review): Fix Calm home fallback persistence

* no-mistakes(document): Clarify Calm gapless and export contracts

* no-mistakes: apply CI fixes

* fix(watch): retire merged PR polls after durable notification (#932)

* fix: retire merged PR polls after notification

* no-mistakes(review): Decouple PR retirement recovery from template updates

* no-mistakes(review): Recover pending PR retirements before poll migration

* no-mistakes(document): Document merged PR poll retirement contracts

* fix: refine scout intake and parallel dispatch (#934)

* Clarify intake evidence and overlap handling

* no-mistakes(review): Align scout guard with intake classification

* no-mistakes(document): Clarify scout documentation and intake ownership

* fix(pi): prevent duplicate assistant replies in Calm (#936)

* fix(pi): preserve operational follow-up semantics in Calm

* no-mistakes(document): Correct Calm operational-row visibility documentation

* perf(bin): shrink the ShellCheck source graph (#939)

* perf(lint): shrink shell source graph

* no-mistakes(review): Ensure lint workers terminate fully on cancellation

* no-mistakes(document): Repair stale lint documentation ownership

* fix(pi): remove Calm hidden-block gaps (#942)

* fix(pi): remove Calm hidden-block gaps

* no-mistakes(review): Validate Calm geometry against current viewport

* no-mistakes(review): Synchronize Calm geometry checks with reload completion

* fix: enforce contract boundaries for ask-user findings (#945)

* fix: escalate ask-user contract expansion

* no-mistakes(document): Point project management to authority owner

* docs: prefer direct operational paths (#946)

* fix(pi): hide operational user rows in Calm mode (#948)

* fix(pi): hide Calm operational user rows

* no-mistakes(review): Narrow Calm operational input suppression

* no-mistakes(review): Avoid Calm replay classifier subprocesses

* no-mistakes(document): docs: point Pi verification to Calm owner

* fix: relaunch missing second mates at session start (#950)

* fix(session-start): relaunch missing second mates

* fix(test): detect completed parallel workers

* no-mistakes(review): Isolate session-start recovery test cleanup

* no-mistakes(review): Complete backend-safe secondmate session recovery

* no-mistakes(review): Resolve Zellij task ownership before recovery

* no-mistakes(review): Recover relocated Zellij ghost tabs safely

* no-mistakes(review): Restore conservative Zellij recovery boundary

* no-mistakes(review): Reject malformed tmux recovery targets

* no-mistakes(document): Align secondmate recovery documentation

* no-mistakes: apply CI fixes

* fix(herdr): reclaim resumed task projections after restart (#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

* Teach Ahoy to surface open decisions (#968)

* Require shipshape routine acknowledgement (#969)

* docs: separate current guidance from verification evidence (#994)

* docs: separate current guides from verification

* no-mistakes(review): Restore Herdr 0.7.5 restart-reclaim verification evidence

* fix: preserve Claude watcher continuity across Stop hooks (#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

* fix(herdr): clean stale projections at session start (#996)

* 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

* fix: recover Claude supervision without watcher-status gate (#1001)

* fix: recover Claude supervision at session start

* fix: remove Claude watcher-status command gate

* no-mistakes(document): docs: remove stale continuity gate references

* fix: make quota-aware profile selection agent-owned (#1018)

* Replace quota dispatch selector instructions

* no-mistakes(review): Align bootstrap docs with agent-owned dispatch selection

* fix(bin): remove vestigial dispatch selector (#1026)

* 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

* docs(agents): drop superseded interim quota-window rule (#1039)

quota-axi 0.1.13 emits schemaVersion 2 with a quotaSemantics object per
provider, so the successor named in the interim rule has landed and the
rule's own removal condition is satisfied.

Keep the ownership clause so quota-axi remains the single owner of how
model or product windows relate to bounding account windows, and drop
the interim weakest-headroom instruction. The unknown-semantics case is
already covered by the existing requirement to stop and report a
candidate whose applicable quota data or interpretation cannot be
established.

Drop the matching assertion phrase from
tests/fm-instruction-owners.test.sh; the retained ownership phrase still
asserts.

* fix(tmux): scope busy detection and recognize current Claude turns (#1049)

* fix(tmux): scope Claude busy detection by harness

* no-mistakes(review): Separate verified and fallback busy signatures

* no-mistakes(test): Scope busy signatures to supplied harnesses

* no-mistakes(document): Document harness-scoped busy detection

* feat: add verified Kimi crewmate adapter (#1047)

* Add verified Kimi crewmate harness adapter

* no-mistakes(review): Scope Kimi moon detection to spinner lines

* no-mistakes(review): Match only complete Kimi spinner rows

* no-mistakes(review): Resolve Kimi binary portably before pane creation

* no-mistakes(document): Align Kimi adapter documentation

* no-mistakes(lint): Suppress false-positive ShellCheck warning for sourced watcher override

* Fix Kimi busy spinner detection

* no-mistakes(review): Recognize Kimi session-lock ancestry and holders

* no-mistakes(review): Scope pending-reply Kimi busy detection by harness

* no-mistakes(document): Correct Kimi spinner capture documentation

* no-mistakes(document): Clarify optional Kimi spinner whitespace

* no-mistakes(lint): Silence intentional pending-reply test stub warnings

* test: align rebased Kimi busy fixtures

* no-mistakes: apply CI fixes

* Reconcile Kimi busy detection after per-harness scoping

* no-mistakes(review): Clarify observed Kimi spinner whitespace contract

* no-mistakes(document): Clarify Kimi harness documentation

* fix: harden Kimi submission and spinner matching (#1058)

* fix kimi pointer submission and spinner conformance

* no-mistakes(review): Preserve Kimi submit target ownership guard

* feat(bin): add guarded Kimi turn-end wake (#1059)

* Add guarded Kimi turn-end hook

* no-mistakes(review): Require jq before installing Kimi turn-end hook

* no-mistakes(review): Expose jq inside isolated Kimi test fixtures

* no-mistakes(review): Preserve Kimi config boundaries during hook removal

* no-mistakes(review): Document Kimi removal newline safeguard

* no-mistakes(document): Document Kimi shared-home preservation

* fix(tmux): classify bordered composers across all rows (#1066)

* Fix structural tmux composer reading

* Verify Calm compatibility with Pi 0.82

* no-mistakes(review): Harden structural composer classification boundaries

* no-mistakes(review): Refresh composer and Kimi regression fixtures

* no-mistakes(review): Fail closed on unbounded composer edges

* no-mistakes(review): Enforce aligned composer geometry safely

* no-mistakes(review): Make composer ambiguity locale-safe

* no-mistakes(review): Preserve ambiguity through composer submission

* no-mistakes(review): Carry composer proof through retries

* no-mistakes(document): Document structural tmux composer delivery guarantees

* no-mistakes: apply CI fixes

* feat(bin): add verified pi-signed runtime adapter (#1145)

* feat: add verified pi-signed adapter

* no-mistakes(review): Correct pi-signed maintainer verification date

* no-mistakes(review): Correct remaining pi-signed verification dates

* no-mistakes(review): Preserve authoritative pi-signed runtime identity

* no-mistakes(document): Document pi-signed shared adapter semantics

* no-mistakes: apply CI fixes

* fix(pi): rearm watcher across session transitions (#1166)

* fix(pi): rearm watcher across same-process session transitions

Pi emits session_shutdown for ordinary /new, /resume, and /fork replacement
as well as terminal quit. The primary watcher extension latched a module-level
stopping flag on every shutdown, so a replacement session in the same process
could not arm monitoring until Pi restarted.

Own arm authority per session generation so only the active live generation
may start, stop, or rearm the child. Replacement sessions can arm again without
restarting Pi, stale prior-generation callbacks cannot mutate the active cycle,
and real quit still blocks late rearm.

* no-mistakes(review): Preserve Pi generation isolation and exit cleanup

* no-mistakes(document): Correct Pi watcher transition documentation

* feat: route crew dispatch using quota-window pace (#1172)

* Consume quota-axi pace signals in dispatch profile array selection.

Add quota-array-dispatch as the single owner of the pace-aware candidate
choice, keep AGENTS.md to the intake boundary and load trigger, and cover
the acceptance cases with sanitized schemaVersion 3 fixtures.

* no-mistakes(review): Stop and report genuine quota dispatch ties

* no-mistakes(document): Document quota pace freshness and uncertainty

* fix: adapt Grok Stop continuation and harden endpoint cleanup (#1171)

* fix(grok): adapt Stop continuation to runtime capability

* no-mistakes(review): Reject ambiguous Grok Stop payloads

* no-mistakes(review): Reject duplicate Grok fields and accept spaced tmux sessions

* no-mistakes(review): Enforce exact tmux cleanup selectors

* no-mistakes(test): Fix historical tmux fixture and validate Grok Stop

* no-mistakes: apply CI fixes

* fix: restore stock macOS Bash 3.2 brief scaffolding (#1093)

* fix(brief): make DOD scaffolding parse-safe on stock macOS Bash 3.2

fm-brief.sh built each Definition-of-done block and the not-enabled
Herdr declaration with `VAR=$(cat <<EOF ... EOF)`. On Bash 3.2 (macOS
/bin/bash) the lexer scans for the command substitution's closing `)`
textually and tracks quote state through the heredoc body, so a single
apostrophe, unbalanced quote, or unbalanced paren in that prose breaks
parsing of the whole script. Every ship-brief scaffold (no-mistakes,
direct-PR, local-only) failed with `unexpected EOF while looking for
matching )`. Bash 4+ parses it fine, so the breakage stayed invisible
everywhere except stock macOS.

Replace all four command-substitution heredocs with
`IFS= read -r -d '' VAR <<EOF || true`. That removes the `$(...)`
wrapper and the entire defect class regardless of future prose, and
preserves the variable expansion the direct-PR and local-only bodies
need. `read` keeps the heredoc's trailing newline that `$(...)` used to
strip, so trim one newline to keep every generated brief byte-identical
to prior output.

Guard the structure, not one historical phrase: a new test rejects any
heredoc nested in a command substitution anywhere in fm-brief.sh, where
the old assertion pinned a single apostrophe phrase and so missed the
reintroduction. Extend the stock-macOS Bash CI job from parsing one
script to the whole maintained shell surface (bin/*.sh,
bin/backends/*.sh, tests/*.sh), matching bin/fm-lint.sh's canonical file
set so parse scope and lint scope cannot drift apart.

* no-mistakes(review): Captain: harden Bash structure and inventory guards

* no-mistakes(document): Align stock macOS Bash contributor checks

* no-mistakes(lint): Suppress deliberate SC2016 literal fixture warnings

* test: stabilize tmux teardown conformance baseline (#1209)

* fix(test): pin teardown tmux baseline to historical kill selectors

merge-base HEAD main collapses to HEAD after the exact-selector change
lands on the default branch, so the old teardown fixture was accidentally
exercising current exact targets. Resolve a content-historical permissive
tmux adapter from first-parent history and force that post-squash topology
inside the conformance case so main and feature branches keep the same
old-vs-new contract.

* no-mistakes(lint): Suppress intentional literal-pattern ShellCheck warnings

* docs: slim quota-array-dispatch to the pace selection core (#1197)

Cut the runtime skill to the compact pace-aware selection procedure plus
minimum owner pointers. Keep every distinct decision rule and move expanded
acceptance scenarios to deterministic fixture ownership assertions.

Size: 170/1374/10187 -> 63/544/4068 (about 63%/60%/60% reduction).

* feat(bin): inherit backend config into secondmate homes (#1219)

* Inherit config/backend into secondmate homes with deliberate-override preservation

Add backend to the shared inheritable config allowlist so launch, locked
bootstrap, and config-push converge a primary pin into secondmate homes as each
home local future-spawn default. Track last-inherited bytes in a private state
provenance marker so deliberate per-home overrides survive present and absent
primary convergence, keep --backend and FM_BACKEND stronger, and extend the
existing inheritance tests plus docs and skill claims.

* no-mistakes(review): Preserve equal unprovenanced backend overrides

* no-mistakes(review): Preserve symlink overrides and verify spawn precedence

* no-mistakes(review): Snapshot backend inheritance for consistent provenance

* no-mistakes(review): Simplify backend inheritance to primary-authoritative convergence

* no-mistakes(document): Document inherited backend override preservation

* fix: restore primary-authoritative backend inheritance after document regression

The document step reintroduced provenance and deliberate per-home override
semantics after review had simplified config/backend to plain primary-authoritative
allowlist membership. Restore the primary-always-wins path: present overwrites,
absent removes, no provenance marker, and docs/tests match that contract.

* no-mistakes(review): Add divergent backend precedence regression fixtures

* no-mistakes(document): Document backend inheritance contract

* fix(pi): remove Calm's upper version ceiling (#1226)

* fix(pi): remove Calm's exclusive Pi upper-version ceiling

tests/fm-calm-pi-extension.test.sh gated on a closed PI_COMPAT_VERSIONS
allowlist ("0.81.1 0.82.0") that refused any other installed Pi, and docs
described that range as "supported" rather than verified evidence. The
Calm CHANGELOG shows no API introduced at either version, so there is no
evidence for a real minimum; the presentation adapters already probe the
exact method they patch rather than checking a version.

Replace the allowlist with dated version evidence that never rejects a
newer Pi, and make each presentation adapter degrade independently with
a diagnostic if a future Pi removes its API, instead of the whole Calm
extension failing to load. Rewrite the feasibility doc's "Pi 0.81.1
through 0.82.0" phrasing to state it as verified evidence, not a
ceiling.

* no-mistakes(review): Probe missing Calm adapter exports safely

* no-mistakes(document): Document Calm's unbounded Pi compatibility

* fix(bin): allow session-local todo tools in the subagent guard (#1204)

* fix(guard): allow session-local todo tools in the primary

The delegation-shape guard denied TaskCreate and TaskUpdate because their
normalized names contain the `task` stem. Those tools write only the harness's
session-local todo list, which has no executor: it spawns no agent, allocates
no worktree, registers no schedule, and starts nothing that outlives the
session. That is not the unaccounted work the guard exists to stop, so the stem
match was a false positive, and the deny text told the primary to run
bin/fm-brief.sh and bin/fm-spawn.sh to create a todo entry.

Add a separately-reasoned PLAN_ONLY_TOOLS exact-name exclusion rather than
widening OBSERVE_ONLY_TOOLS, whose documented contract is tools that only
observe or stop existing work. Both lists stay exact-name so neither can widen
by substring.

Tests cover the two allowed names and six near-miss names that a substring or
shortened-stem widening would release; both mutations were watched red.

* no-mistakes(review): drop session-local todo tools from recommended deny list

* no-mistakes: apply CI fixes

* fix(session-lock): resolve Claude bg-spare ancestry to the outermost claude pid (#1206)

* fix(session-lock): resolve Claude bg-spare ancestry to the outermost claude pid

fm_harness_ancestry_pid() previously returned the first ancestor process
whose command matched a verified harness name. Claude Code's Stop hook
fires as a bg-spare worker several levels below the session's actual
lock-owning claude process (hook shell -> claude bg-spare ->
claude bg-pty-host -> claude -> claude(lock)), so the first match was
the bg-spare worker, not the lock owner. fm_session_lock_owned_by_self()
then never matched state/.lock, and the Claude Stop auto-arm silently
treated its own primary session as an unrelated live owner and never
armed the watcher.

The walk now keeps going past a claude-named match, looking for a still
more ancestral claude-named match, and stops the instant a non-match
follows an already-found match (bounding it to a contiguous run rather
than the literal ancestry top, so an unrelated claude-named process
further up the real process tree is never mistaken for part of this
session's own nested chain). Every other harness keeps the original
first-match-wins behavior, since e.g. Pi's shared signed-wrapper
ancestry actually holds the session at the inner engine pid, not an
outer wrapper pid. Hop limit raised from 8 to 16 to cover the deeper
bg-spare chain.

* no-mistakes(review): Add nested-claude-ancestry regression test; fix nudge doc depth claim

* no-mistakes: apply CI fixes

* fix: conferma l'avvio del watcher su Windows/MSYS (#1212)

* fix: confirm watcher startup on MSYS

* no-mistakes(review): gate MSYS arm ready timeout, cache uname, harden locale test

* no-mistakes(review): validate OpenCode ready timeout, make uname cache internal

* fix(spawn): forward CLAUDE_CONFIG_DIR to claude crewmates (#1195)

* fix(spawn): forward firstmate's CLAUDE_CONFIG_DIR to claude crewmates

Crewmate panes are created by a long-lived tmux/herdr daemon that does not
inherit firstmate's current environment. When firstmate runs under a non-default
CLAUDE_CONFIG_DIR (for example a work-vs-personal subscription split), a bare
`claude` in the crewmate pane fell back to the default ~/.claude store and
launched unauthenticated, blocking the crewmate before it could do any work.

fm-spawn now prefixes the claude launch with firstmate's own resolved
CLAUDE_CONFIG_DIR when set, so the crewmate uses the same credential/config
store firstmate is authenticated with. An unset value is the single-store
default and adds no prefix; non-claude harnesses are unaffected.

Adds three tests in fm-spawn-dispatch-profile.test.sh (forwarded-when-set,
omitted-when-unset, non-claude-ignored) and pins CLAUDE_CONFIG_DIR in the test
helper so launch assertions no longer depend on the developer's environment.

* no-mistakes: apply CI fixes

* fix: preserve dispatch identity across authentication checks (#1233)

* fix: preserve dispatch harness identity

* no-mistakes(review): Fix Grok counterfactual tuple validation

* no-mistakes(document): Scope dispatch authentication to selected tuple

* fix: restore dispatch instruction budget

* no-mistakes(review): Scope dispatch authentication after candidate selection

* fix(bin): normalize relative durable paths (#1256)

* fix(bin): handle dash-leading harness process names (#2)

* fix: handle dash-leading harness process names

* no-mistakes(review): Make dash-leading harness regression hermetic

* fix: preserve secondmate reply routes across relative homes

Resolve relative home, data, and state inputs before durable ch…
ammar00sheikh added a commit to ammar00sheikh/firstmate that referenced this pull request Jul 31, 2026
…es (#1)

* fix(afk): make Pi escalation and return catch-up reliable (#587)

* fix: close away-mode blocker supervision gap

* no-mistakes(review): Gate teardown retries and verify U+2063 dedupe

* no-mistakes(test): Fail closed on incomplete Pi composer separators

* no-mistakes(document): Document Pi composer recognition and return gating

* feat: support Pi max reasoning profiles (#537)

* support Pi max thinking profiles

* no-mistakes(review): Captain, allow Pi max dispatch profiles

* chore: no-mistakes(document): Clarify yolo response ownership (#595)

* Clarify validation response ownership

* no-mistakes(document): Clarify yolo response ownership

* feat: establish instruction ownership foundation (#619)

* Add instruction owners foundation

* no-mistakes(document): Refresh project-management owner pointers

* fix: compress Firstmate contract and enforce delivery rigor ownership (#626)

* docs: compress firstmate operating contract

* docs: make delivery rigor single-owner

PR B already removed personal and stacked review requirements, but it did not explicitly assign rigor to the selected delivery path or forbid risk-based manual clean gates. That gap still permitted the Hi Bit inversion.

* no-mistakes(review): Honor configured merge authority across faster delivery paths

* no-mistakes(document): Align docs with compressed operating contract

* feat: add durable captain decision holds (#593)

* Add durable captain decision holds

* no-mistakes(review): Validate decision hold retries and origin paths

* no-mistakes(review): Enforce durable decision lifecycle boundaries

* no-mistakes(review): Harden decision display and partial retry recovery

* no-mistakes(test): Update scout teardown fixtures for decision inventory

* no-mistakes(document): Align decision lifecycle and scout teardown documentation

* no-mistakes: apply CI fixes

* no-mistakes(review): Reconcile terminal decision holds

* no-mistakes(document): Align captain decision-hold documentation

* fix(bin): harden PR check artifacts (#556)

* fix: harden PR check artifacts

* fix: close PR check migration gaps

* fix: close PR check retry gaps

* fix: clarify migration outcomes and ESM boundary

* fix: keep failed migrations authoritative

* test: use inert PR validation fixtures

* no-mistakes(review): Reserve noncanonical PR quarantine namespace

* no-mistakes(review): Prevalidate final PR-check teardown artifacts

* no-mistakes(review): Preserve X metadata and validate teardown IDs

* no-mistakes(review): Initialize migration state before watcher exclusion

* no-mistakes(review): Isolate failed poll migrations from bootstrap recovery

* no-mistakes(review): Allow safe polling during incomplete private repairs

* no-mistakes(review): Authenticate watcher checks at execution time

* no-mistakes(review): Preserve custom checks with hash-bound registration

* no-mistakes(review): Clean custom check snapshots on watcher signals

* no-mistakes(review): Stop watcher checks promptly on signals

* no-mistakes(review): Terminate watcher check groups before cleanup

* no-mistakes(document): Correct stale X-mode watcher documentation

* fix: drain returned watcher check groups

* no-mistakes(review): Harden quarantine links and recover validated replacement polls

* no-mistakes(review): Preserve X mode across shim version transitions

* no-mistakes(review): Refresh legacy X shims before marker short-circuits

* no-mistakes(document): Correct persisted PR-check artifact documentation

* no-mistakes(document): Correct stale PR-check documentation

* fix: bind PR poll repair provenance

* no-mistakes(review): Enforce single-link ownership for custom check artifacts

* no-mistakes(review): Preserve private checks, X polling, and lifecycle IDs

* no-mistakes(review): Separate task creation and legacy teardown validation

* no-mistakes(review): Restore safe legacy operations and teardown validation

* no-mistakes(review): Disambiguate migration obligations and preserve legacy retries

* no-mistakes(review): Preserve fail-closed diagnostics and legacy quarantine evidence

* no-mistakes(review): Reconcile legacy migration retries and teardown collisions

* no-mistakes(review): Force legacy namespace reconciliation before marker short-circuits

* no-mistakes(document): Document private poll artifact safety contracts

* no-mistakes(lint): Suppress intentional literal-dollar lint finding

* fix: migrate historical X poll identity

* fix: harden PR check artifacts

* no-mistakes(review): Preserve fail-closed diagnostics and legacy quarantine evidence

* no-mistakes(review): Reconcile legacy migration retries and teardown collisions

* fix: migrate historical X poll identity

* no-mistakes(review): Harden X-mode artifact publication against symlink corruption

* no-mistakes(review): Guard X artifact publication

* no-mistakes(review): Enforce private X artifact reads

* no-mistakes(test): Fix backend compatibility fixture dependencies

* no-mistakes(document): Refresh PR-check documentation

* no-mistakes(lint): Remove unused x-mode test locals

* no-mistakes: apply CI fixes

* fix(bin): compact session-start backlog digest (#636)

* fix: compact session-start backlog digest

* no-mistakes(test): Fix legacy backend fixture helper

* no-mistakes(test): Fix watcher exit wait helper

* no-mistakes(document): Document compact backlog digest

* fix: dedupe stale watcher guard banners (#637)

* fix: dedupe stale watcher guard banner

* no-mistakes(review): Keep read-only guard state nonmutating

* no-mistakes(document): Clarify stale watcher docs

* fix(bin): balance bearings landed baseline (#640)

* fix: balance bearings landed defaults

* no-mistakes(document): Document balanced landed baseline

* no-mistakes: apply CI fixes

* fix: clarify captain-facing translation contract (#644)

* docs: clarify captain-facing translation contract

* no-mistakes(review): Restore runtime fallback mandate

* no-mistakes(document): Align Bearings translation wording

* fix(bin): make bootstrap output and nudges deterministic (#646)

* fix: make bootstrap nudges deterministic

* no-mistakes(review): Honor state override for bootstrap nudges

* no-mistakes(review): Update benign bootstrap documentation labels

* no-mistakes(review): Validate bootstrap nudge retry markers

* no-mistakes(document): Align bootstrap nudge documentation

* no-mistakes: apply CI fixes

* docs(secondmate-provisioning): clarify concise registry ownership (#649)

* Clarify concise secondmate registry contract

* no-mistakes(review): Expand secondmate registry boilerplate coverage

* no-mistakes(document): Point route docs to owner

* fix(bin): strip quoted blocked_by values during decision hold resolve (#654)

* fix(bin): strip quotes on blocked_by in decision-hold resolve

tasks-axi quotes multi-entry blocked_by as "a,b,c", so the comma-boundary
membership test only matched middle elements. Strip surrounding quotes
before matching so first and last hold ids resolve correctly.

* no-mistakes(document): Refresh decision-hold regression evidence

* feat(secondmate): inherit shared captain preferences (#656)

* feat(secondmate): inherit shared captain preferences

* no-mistakes(review): Honor shared captain data overrides

* no-mistakes(review): Honor bootstrap data override registry

* no-mistakes(document): Refresh shared inheritance docs

* no-mistakes(document): Clarify inherited local-material docs

* feat: gate local agent secret injection (#658)

* feat(spawn): gate local agent secret injection

* fix(spawn): align final Keychain slot

* no-mistakes: apply CI fixes

* test: isolate Herdr autodetect smoke sessions (#662)

* test: isolate herdr autodetect smoke session

* no-mistakes(review): Restored autodetect smoke gate bypass

* no-mistakes(test): Harden Herdr lab provisioning

* no-mistakes(document): Refresh Herdr lab docs

* docs: adopt under way for active work (#666)

* Revert "feat: gate local agent secret injection (#658)" (#668)

This reverts commit c27135cd9d35bc4c237d49b3b374da31fbd52eef.

* fix(pi): distinguish stale locks when arming watcher (#681)

* 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 house vocabulary (#685)

* fix: accept secondmate as house vocabulary

* no-mistakes(test): Update captain vocabulary contract test

* no-mistakes(document): Align secondmate documentation vocabulary

* fix(bin): parse handoff homes after registry parentheticals (#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 (#687)

* feat: add native session-start nudges

* no-mistakes(document): Document nudge script inventory

* docs: call built-in defaults the firstmate repo, not template (#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.

* fix(bin): repair fm-brief.sh parse error and harden set -u array expansion (#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>
#166 apostrophe regression cannot return unnoticed.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(bin): keep watcher supervision continuous across child cycles (#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: fetch current PR head for review diffs (#722)

* 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

* docs: resolve five contract contradictions across AGENTS.md, README, and skills (#736)

* docs: resolve five contract contradictions

* no-mistakes(test): align owner-pointer assertions with reworded docs; skip absent shellcheck

* docs(harness): correct Grok exit guidance (#742)

* docs(harness): reverify grok exit command

* no-mistakes(test): Correct Grok exit resume attribution

* fix(watcher): bound stale wakes for parked crew (#743)

* 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

* fix(supervision): distinguish ordinary wakes from recovery (#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 (#745)

* 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

* feat(wake): enrich drained signals with bounded status context (#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 (#784)

* 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

* fix(send): treat opencode busy-queued composer state as submitted (#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

* fix(spawn): require two stable reads before accepting worktree path (#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>

* fix(bin): make watcher process identity immune to Linux wall-clock changes (#752)

* fix(watcher): stabilize Linux process identity

* no-mistakes(document): document FM_PROC_ROOT_OVERRIDE and Linux starttime identity rationale

* fix: prevent AFK idle stalls and stale run attribution (#758)

* 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(bin): allow safe teardown during watcher recovery (#750)

* 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

* feat(herdr): order presentation spaces while preserving focus (#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

* fix(bin): send literal config reread nudges after pushes (#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 (#797)

* 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

* fix(herdr): group projected children beneath owning parents (#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: keep local no-mistakes tests intent-targeted (#823)

* 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: add canonical timed test runner (#825)

* 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: surface main inventory gaps in Bearings (#830)

* 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 bounded concurrent test isolation proof (#832)

* 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: guard against missed secondmate reports (#834)

* 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: require pinned real-Herdr CI coverage (#838)

* 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

* feat: shard portable tests and add bounded local parallelism (#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

* fix: block primary-session delegation outside the fleet (#854)

* 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

* fix: install tasks-axi in portable CI shards (#866)

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(bin): make dispatch profiles quota aware (#867)

* 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

* Add built-in ahoy recap skill (#873)

* fix: preserve trustworthy Bearings data in partial snapshots (#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

* feat(pi): add session-local calm mode (#884)

* 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): prevent redundant watcher re-arms (#885)

* 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 (#895)

* 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 (#898)

* fix: execute every PR body compliance event

* no-mistakes(document): Document independent PR compliance events

* fix: exclude operational injections from ahoy boundaries (#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

* fix: canonically classify operational inputs across harnesses (#909)

* fix: type canonical operational inputs

* no-mistakes(document): Correct canonical operational-input documentation ownership

* fix: avoid generic secondmate start acknowledgements (#926)

* fix: avoid generic secondmate acknowledgements

* no-mistakes(document): Document sparse secondmate acknowledgement behavior

* no-mistakes: apply CI fixes

* fix(pi): make calm mode persistent and gapless (#927)

* fix(pi): preserve calm presentation across sessions

* no-mistakes(review): Fix Calm home fallback persistence

* no-mistakes(document): Clarify Calm gapless and export contracts

* no-mistakes: apply CI fixes

* fix(watch): retire merged PR polls after durable notification (#932)

* fix: retire merged PR polls after notification

* no-mistakes(review): Decouple PR retirement recovery from template updates

* no-mistakes(review): Recover pending PR retirements before poll migration

* no-mistakes(document): Document merged PR poll retirement contracts

* fix: refine scout intake and parallel dispatch (#934)

* Clarify intake evidence and overlap handling

* no-mistakes(review): Align scout guard with intake classification

* no-mistakes(document): Clarify scout documentation and intake ownership

* fix(pi): prevent duplicate assistant replies in Calm (#936)

* fix(pi): preserve operational follow-up semantics in Calm

* no-mistakes(document): Correct Calm operational-row visibility documentation

* perf(bin): shrink the ShellCheck source graph (#939)

* perf(lint): shrink shell source graph

* no-mistakes(review): Ensure lint workers terminate fully on cancellation

* no-mistakes(document): Repair stale lint documentation ownership

* fix(pi): remove Calm hidden-block gaps (#942)

* fix(pi): remove Calm hidden-block gaps

* no-mistakes(review): Validate Calm geometry against current viewport

* no-mistakes(review): Synchronize Calm geometry checks with reload completion

* fix: enforce contract boundaries for ask-user findings (#945)

* fix: escalate ask-user contract expansion

* no-mistakes(document): Point project management to authority owner

* docs: prefer direct operational paths (#946)

* fix(pi): hide operational user rows in Calm mode (#948)

* fix(pi): hide Calm operational user rows

* no-mistakes(review): Narrow Calm operational input suppression

* no-mistakes(review): Avoid Calm replay classifier subprocesses

* no-mistakes(document): docs: point Pi verification to Calm owner

* fix: relaunch missing second mates at session start (#950)

* fix(session-start): relaunch missing second mates

* fix(test): detect completed parallel workers

* no-mistakes(review): Isolate session-start recovery test cleanup

* no-mistakes(review): Complete backend-safe secondmate session recovery

* no-mistakes(review): Resolve Zellij task ownership before recovery

* no-mistakes(review): Recover relocated Zellij ghost tabs safely

* no-mistakes(review): Restore conservative Zellij recovery boundary

* no-mistakes(review): Reject malformed tmux recovery targets

* no-mistakes(document): Align secondmate recovery documentation

* no-mistakes: apply CI fixes

* fix(herdr): reclaim resumed task projections after restart (#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

* Teach Ahoy to surface open decisions (#968)

* Require shipshape routine acknowledgement (#969)

* docs: separate current guidance from verification evidence (#994)

* docs: separate current guides from verification

* no-mistakes(review): Restore Herdr 0.7.5 restart-reclaim verification evidence

* fix: preserve Claude watcher continuity across Stop hooks (#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

* fix(herdr): clean stale projections at session start (#996)

* 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

* fix: recover Claude supervision without watcher-status gate (#1001)

* fix: recover Claude supervision at session start

* fix: remove Claude watcher-status command gate

* no-mistakes(document): docs: remove stale continuity gate references

* fix: make quota-aware profile selection agent-owned (#1018)

* Replace quota dispatch selector instructions

* no-mistakes(review): Align bootstrap docs with agent-owned dispatch selection

* fix(bin): remove vestigial dispatch selector (#1026)

* 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

* docs(agents): drop superseded interim quota-window rule (#1039)

quota-axi 0.1.13 emits schemaVersion 2 with a quotaSemantics object per
provider, so the successor named in the interim rule has landed and the
rule's own removal condition is satisfied.

Keep the ownership clause so quota-axi remains the single owner of how
model or product windows relate to bounding account windows, and drop
the interim weakest-headroom instruction. The unknown-semantics case is
already covered by the existing requirement to stop and report a
candidate whose applicable quota data or interpretation cannot be
established.

Drop the matching assertion phrase from
tests/fm-instruction-owners.test.sh; the retained ownership phrase still
asserts.

* fix(tmux): scope busy detection and recognize current Claude turns (#1049)

* fix(tmux): scope Claude busy detection by harness

* no-mistakes(review): Separate verified and fallback busy signatures

* no-mistakes(test): Scope busy signatures to supplied harnesses

* no-mistakes(document): Document harness-scoped busy detection

* feat: add verified Kimi crewmate adapter (#1047)

* Add verified Kimi crewmate harness adapter

* no-mistakes(review): Scope Kimi moon detection to spinner lines

* no-mistakes(review): Match only complete Kimi spinner rows

* no-mistakes(review): Resolve Kimi binary portably before pane creation

* no-mistakes(document): Align Kimi adapter documentation

* no-mistakes(lint): Suppress false-positive ShellCheck warning for sourced watcher override

* Fix Kimi busy spinner detection

* no-mistakes(review): Recognize Kimi session-lock ancestry and holders

* no-mistakes(review): Scope pending-reply Kimi busy detection by harness

* no-mistakes(document): Correct Kimi spinner capture documentation

* no-mistakes(document): Clarify optional Kimi spinner whitespace

* no-mistakes(lint): Silence intentional pending-reply test stub warnings

* test: align rebased Kimi busy fixtures

* no-mistakes: apply CI fixes

* Reconcile Kimi busy detection after per-harness scoping

* no-mistakes(review): Clarify observed Kimi spinner whitespace contract

* no-mistakes(document): Clarify Kimi harness documentation

* fix: harden Kimi submission and spinner matching (#1058)

* fix kimi pointer submission and spinner conformance

* no-mistakes(review): Preserve Kimi submit target ownership guard

* feat(bin): add guarded Kimi turn-end wake (#1059)

* Add guarded Kimi turn-end hook

* no-mistakes(review): Require jq before installing Kimi turn-end hook

* no-mistakes(review): Expose jq inside isolated Kimi test fixtures

* no-mistakes(review): Preserve Kimi config boundaries during hook removal

* no-mistakes(review): Document Kimi removal newline safeguard

* no-mistakes(document): Document Kimi shared-home preservation

* fix(tmux): classify bordered composers across all rows (#1066)

* Fix structural tmux composer reading

* Verify Calm compatibility with Pi 0.82

* no-mistakes(review): Harden structural composer classification boundaries

* no-mistakes(review): Refresh composer and Kimi regression fixtures

* no-mistakes(review): Fail closed on unbounded composer edges

* no-mistakes(review): Enforce aligned composer geometry safely

* no-mistakes(review): Make composer ambiguity locale-safe

* no-mistakes(review): Preserve ambiguity through composer submission

* no-mistakes(review): Carry composer proof through retries

* no-mistakes(document): Document structural tmux composer delivery guarantees

* no-mistakes: apply CI fixes

* feat(bin): add verified pi-signed runtime adapter (#1145)

* feat: add verified pi-signed adapter

* no-mistakes(review): Correct pi-signed maintainer verification date

* no-mistakes(review): Correct remaining pi-signed verification dates

* no-mistakes(review): Preserve authoritative pi-signed runtime identity

* no-mistakes(document): Document pi-signed shared adapter semantics

* no-mistakes: apply CI fixes

* fix(pi): rearm watcher across session transitions (#1166)

* fix(pi): rearm watcher across same-process session transitions

Pi emits session_shutdown for ordinary /new, /resume, and /fork replacement
as well as terminal quit. The primary watcher extension latched a module-level
stopping flag on every shutdown, so a replacement session in the same process
could not arm monitoring until Pi restarted.

Own arm authority per session generation so only the active live generation
may start, stop, or rearm the child. Replacement sessions can arm again without
restarting Pi, stale prior-generation callbacks cannot mutate the active cycle,
and real quit still blocks late rearm.

* no-mistakes(review): Preserve Pi generation isolation and exit cleanup

* no-mistakes(document): Correct Pi watcher transition documentation

* feat: route crew dispatch using quota-window pace (#1172)

* Consume quota-axi pace signals in dispatch profile array selection.

Add quota-array-dispatch as the single owner of the pace-aware candidate
choice, keep AGENTS.md to the intake boundary and load trigger, and cover
the acceptance cases with sanitized schemaVersion 3 fixtures.

* no-mistakes(review): Stop and report genuine quota dispatch ties

* no-mistakes(document): Document quota pace freshness and uncertainty

* fix: adapt Grok Stop continuation and harden endpoint cleanup (#1171)

* fix(grok): adapt Stop continuation to runtime capability

* no-mistakes(review): Reject ambiguous Grok Stop payloads

* no-mistakes(review): Reject duplicate Grok fields and accept spaced tmux sessions

* no-mistakes(review): Enforce exact tmux cleanup selectors

* no-mistakes(test): Fix historical tmux fixture and validate Grok Stop

* no-mistakes: apply CI fixes

* fix: restore stock macOS Bash 3.2 brief scaffolding (#1093)

* fix(brief): make DOD scaffolding parse-safe on stock macOS Bash 3.2

fm-brief.sh built each Definition-of-done block and the not-enabled
Herdr declaration with `VAR=$(cat <<EOF ... EOF)`. On Bash 3.2 (macOS
/bin/bash) the lexer scans for the command substitution's closing `)`
textually and tracks quote state through the heredoc body, so a single
apostrophe, unbalanced quote, or unbalanced paren in that prose breaks
parsing of the whole script. Every ship-brief scaffold (no-mistakes,
direct-PR, local-only) failed with `unexpected EOF while looking for
matching )`. Bash 4+ parses it fine, so the breakage stayed invisible
everywhere except stock macOS.

Replace all four command-substitution heredocs with
`IFS= read -r -d '' VAR <<EOF || true`. That removes the `$(...)`
wrapper and the entire defect class regardless of future prose, and
preserves the variable expansion the direct-PR and local-only bodies
need. `read` keeps the heredoc's trailing newline that `$(...)` used to
strip, so trim one newline to keep every generated brief byte-identical
to prior output.

Guard the structure, not one historical phrase: a new test rejects any
heredoc nested in a command substitution anywhere in fm-brief.sh, where
the old assertion pinned a single apostrophe phrase and so missed the
reintroduction. Extend the stock-macOS Bash CI job from parsing one
script to the whole maintained shell surface (bin/*.sh,
bin/backends/*.sh, tests/*.sh), matching bin/fm-lint.sh's canonical file
set so parse scope and lint scope cannot drift apart.

* no-mistakes(review): Captain: harden Bash structure and inventory guards

* no-mistakes(document): Align stock macOS Bash contributor checks

* no-mistakes(lint): Suppress deliberate SC2016 literal fixture warnings

* test: stabilize tmux teardown conformance baseline (#1209)

* fix(test): pin teardown tmux baseline to historical kill selectors

merge-base HEAD main collapses to HEAD after the exact-selector change
lands on the default branch, so the old teardown fixture was accidentally
exercising current exact targets. Resolve a content-historical permissive
tmux adapter from first-parent history and force that post-squash topology
inside the conformance case so main and feature branches keep the same
old-vs-new contract.

* no-mistakes(lint): Suppress intentional literal-pattern ShellCheck warnings

* docs: slim quota-array-dispatch to the pace selection core (#1197)

Cut the runtime skill to the compact pace-aware selection procedure plus
minimum owner pointers. Keep every distinct decision rule and move expanded
acceptance scenarios to deterministic fixture ownership assertions.

Size: 170/1374/10187 -> 63/544/4068 (about 63%/60%/60% reduction).

* feat(bin): inherit backend config into secondmate homes (#1219)

* Inherit config/backend into secondmate homes with deliberate-override preservation

Add backend to the shared inheritable config allowlist so launch, locked
bootstrap, and config-push converge a primary pin into secondmate homes as each
home local future-spawn default. Track last-inherited bytes in a private state
provenance marker so deliberate per-home overrides survive present and absent
primary convergence, keep --backend and FM_BACKEND stronger, and extend the
existing inheritance tests plus docs and skill claims.

* no-mistakes(review): Preserve equal unprovenanced backend overrides

* no-mistakes(review): Preserve symlink overrides and verify spawn precedence

* no-mistakes(review): Snapshot backend inheritance for consistent provenance

* no-mistakes(review): Simplify backend inheritance to primary-authoritative convergence

* no-mistakes(document): Document inherited backend override preservation

* fix: restore primary-authoritative backend inheritance after document regression

The document step reintroduced provenance and deliberate per-home override
semantics after review had simplified config/backend to plain primary-authoritative
allowlist membership. Restore the primary-always-wins path: present overwrites,
absent removes, no provenance marker, and docs/tests match that contract.

* no-mistakes(review): Add divergent backend precedence regression fixtures

* no-mistakes(document): Document backend inheritance contract

* fix(pi): remove Calm's upper version ceiling (#1226)

* fix(pi): remove Calm's exclusive Pi upper-version ceiling

tests/fm-calm-pi-extension.test.sh gated on a closed PI_COMPAT_VERSIONS
allowlist ("0.81.1 0.82.0") that refused any other installed Pi, and docs
described that range as "supported" rather than verified evidence. The
Calm CHANGELOG shows no API introduced at either version, so there is no
evidence for a real minimum; the presentation adapters already probe the
exact method they patch rather than checking a version.

Replace the allowlist with dated version evidence that never rejects a
newer Pi, and make each presentation adapter degrade independently with
a diagnostic if a future Pi removes its API, instead of the whole Calm
extension failing to load. Rewrite the feasibility doc's "Pi 0.81.1
through 0.82.0" phrasing to state it as verified evidence, not a
ceiling.

* no-mistakes(review): Probe missing Calm adapter exports safely

* no-mistakes(document): Document Calm's unbounded Pi compatibility

* fix(bin): allow session-local todo tools in the subagent guard (#1204)

* fix(guard): allow session-local todo tools in the primary

The delegation-shape guard denied TaskCreate and TaskUpdate because their
normalized names contain the `task` stem. Those tools write only the harness's
session-local todo list, which has no executor: it spawns no agent, allocates
no worktree, registers no schedule, and starts nothing that outlives the
session. That is not the unaccounted work the guard exists to stop, so the stem
match was a false positive, and the deny text told the primary to run
bin/fm-brief.sh and bin/fm-spawn.sh to create a todo entry.

Add a separately-reasoned PLAN_ONLY_TOOLS exact-name exclusion rather than
widening OBSERVE_ONLY_TOOLS, whose documented contract is tools that only
observe or stop existing work. Both lists stay exact-name so neither can widen
by substring.

Tests cover the two allowed names and six near-miss names that a substring or
shortened-stem widening would release; both mutations were watched red.

* no-mistakes(review): drop session-local todo tools from recommended deny list

* no-mistakes: apply CI fixes

* fix(session-lock): resolve Claude bg-spare ancestry to the outermost claude pid (#1206)

* fix(session-lock): resolve Claude bg-spare ancestry to the outermost claude pid

fm_harness_ancestry_pid() previously returned the first ancestor process
whose command matched a verified harness name. Claude Code's Stop hook
fires as a bg-spare worker several levels below the session's actual
lock-owning claude process (hook shell -> claude bg-spare ->
claude bg-pty-host -> claude -> claude(lock)), so the first match was
the bg-spare worker, not the lock owner. fm_session_lock_owned_by_self()
then never matched state/.lock, and the Claude Stop auto-arm silently
treated its own primary session as an unrelated live owner and never
armed the watcher.

The walk now keeps going past a claude-named match, looking for a still
more ancestral claude-named match, and stops the instant a non-match
follows an already-found match (bounding it to a contiguous run rather
than the literal ancestry top, so an unrelated claude-named process
further up the real process tree is never mistaken for part of this
session's own nested chain). Every other harness keeps the original
first-match-wins behavior, since e.g. Pi's shared signed-wrapper
ancestry actually holds the session at the inner engine pid, not an
outer wrapper pid. Hop limit raised from 8 to 16 to cover the deeper
bg-spare chain.

* no-mistakes(review): Add nested-claude-ancestry regression test; fix nudge doc depth claim

* no-mistakes: apply CI fixes

* fix: conferma l'avvio del watcher su Windows/MSYS (#1212)

* fix: confirm watcher startup on MSYS

* no-mistakes(review): gate MSYS arm ready timeout, cache uname, harden locale test

* no-mistakes(review): validate OpenCode ready timeout, make uname cache internal

* fix(spawn): forward CLAUDE_CONFIG_DIR to claude crewmates (#1195)

* fix(spawn): forward firstmate's CLAUDE_CONFIG_DIR to claude crewmates

Crewmate panes are created by a long-lived tmux/herdr daemon that does not
inherit firstmate's current environment. When firstmate runs under a non-default
CLAUDE_CONFIG_DIR (for example a work-vs-personal subscription split), a bare
`claude` in the crewmate pane fell back to the default ~/.claude store and
launched unauthenticated, blocking the crewmate before it could do any work.

fm-spawn now prefixes the claude launch with firstmate's own resolved
CLAUDE_CONFIG_DIR when set, so the crewmate uses the same credential/config
store firstmate is authenticated with. An unset value is the single-store
default and adds no prefix; non-claude harnesses are unaffected.

Adds three tests in fm-spawn-dispatch-profile.test.sh (forwarded-when-set,
omitted-when-unset, non-claude-ignored) and pins CLAUDE_CONFIG_DIR in the test
helper so launch assertions no longer depend on the developer's environment.

* no-mistakes: apply CI fixes

* fix: preserve dispatch identity across authentication checks (#1233)

* fix: preserve dispatch harness identity

* no-mistakes(review): Fix Grok counterfactual tuple validation

* no-mistakes(document): Scope dispatch authentication to selected tuple

* fix: restore dispatch instruction budget

* no-mistakes(review): Scope dispatch authentication after candidate selection

* fix(bin): normalize relative durable paths (#1256)

* fix(bin): handle dash-leading harness process names (#2)

* fix: handle dash-leading harness process names

* no-mistakes(review): Make dash-leading harness regression hermetic

* fix: preserve secondmate reply routes across relative homes

Resolve relative home, data, and state inputs before durable charter generation, and fail when caller-relative directories cannot be resolved.

Use absolute paths at the related spawn, AFK daemon, and X-mode cross-process handoffs so later processes cannot reinterpret them from another working directory.

* no-mistakes(review): Preserve absolute overrides and normalize relative durable paths

* no-mistakes(review): Normalize relative home before deriving durable paths

* no-mistakes(document): Document relative durable-path normalization

* no-mistakes(review): Captain: Ignore inherited CDPATH during relative path normalization

* no-mistakes(lint): Fix empty CDPATH assignments for ShellCheck

* refactor(skills): make Bearings chat-only by default (#1136)

* Add internal status skill

* no-mistakes(document): register /status skill in documentation-audiences inventory

* no-mistakes(lint): replace grep|wc -l with grep -c in status skill test

* test: silence literal status skill patterns

* Refactor bearings default to chat-only

---------

Co-authored-by: Kun Chen <3233006+kunchenguid@users.noreply.github.com>

* Clarify follow-up routing during validation (#1277)

* fix: honor concrete approval for project operations (#1272)

* docs: add captain-approved project operation exception to hard rule 1

Firstmate stays read-only over projects by default, but when the captain
clearly approves a concrete project operation and scope in the moment,
firstmate may perform exactly that approved operation with its own tools.
The approval is never inferred, broadened, or standing, and it does not
relax the existing force, discard, unlanded-work, or merge-authority
boundaries.

* no-mistakes(review): Clarify captain-approved project operation boundaries

* no-mistakes(document): Clarify captain-approved project operation scope

* docs: cover directories and preserve the operation-or-scope alternative

Widen the captain-approved project operation exception in AGENTS.md to
files or directories, and restore the explicit operation-or-scope
alternative that a prior pipeline auto-fix had collapsed into "and".

Rework project-management SKILL.md's Remove section, which previously
told firstmate to refuse project removal until a guarded helper existed;
that helper was never built, so the text directly contradicted the new
instruction-only exception. It now points at the exception plus the
existing removal preflight it still requires unchanged.

Update the one instruction-owners test assertion that hard-coded the
sentence removed above, so the suite tracks current, not obsolete, text.

* docs: add captain-approved project operation exception to hard rule 1

Firstmate stays read-only over projects by default, but when the captain
clearly approves a concrete project operation and scope in the moment,
firstmate may perform exactly that approved operation with its own tools.
The approval is never inferred, broadened, or standing, and it does not
relax the existing force, discard, unlanded-work, or merge-authority
boundaries.

* no-mistakes(review): Clarify captain-approved project operation boundaries

* no-mistakes(document): Clarify captain-approved project operation scope

* docs: cover directories and preserve the operation-or-scope alternative

Widen the captain-approved project operation exception in AGENTS.md to
files or directories, and restore the explicit operation-or-scope
alternative that a prior pipeline auto-fix had collapsed into "and".

Rework project-management SKILL.md's Remove section, which previously
told firstmate to refuse project removal until a guarded helper existed;
that helper was never built, so the text directly contradicted the new
instruction-only exception. It now points at the exception plus the
existing removal preflight it still requires unchanged.

Update the one instruction-owners test assertion that hard-coded the
sentence removed above, so the suite tracks current, not obsolete, text.

* no-mistakes(review): Align project removal preflight with approved exception

* no-mistakes(document): Align project removal documentation with approved exception

* fix: restore removal test byte-for-byte and preserve the default sentence

tests/fm-instruction-owners.test.sh had been changed to assert different
text; restore it byte-for-byte to origin/main. project-management SKILL.md's
Remove section now keeps the exact default "Never issue a raw removal
command from Firstmate." sentence that test still asserts, immediately
followed by the already-approved captain-operation-or-scope exception, so
the default and the exception both stay explicit and consistent.

* no-mistakes(document): Align project-write boundary documentation

* fix(skills): route new project intake through secondmate scopes (#1275)

* Route project intake through secondmate scopes

* no-mistakes(test): Guard all main-home project registry mutations

* no-mistakes(document): Consolidate secondmate routing documentation

* no-mistakes: apply CI fixes

* Restore new-project routing scope

* no-mistakes(document): Clarify secondmate routing for new-project intake

* no-mistakes: apply CI fixes

* fix: scope validation corrections by accepted behavior (#1281)

* fix: scope validation corrections by accepted behavior

* no-mistakes(review): Classify stale delivery evidence as an autonomous correction

* test: replace source assertions with behavioral coverage (#1282)

* test: remove source-content assertions

* no-mistakes(review): Replace source assertions with runtime behavior coverage

* no-mistakes(review): Isolate Kimi task temp runtime coverage

* no-mistakes(document): Refresh test cleanup documentation

* no-mistakes: apply CI fixes

* fix(watch): escalate busy workers with no completed turn (#1286)

* fix(watch): bound how long a busy pane may run with no completed turn

A busy pane (backend busy state or the harness's rendered footer) was
unconditional, unbounded proof of liveness in every escalation path, so a
hung foreground tool call behind a busy signature could run for hours
undetected (2026-07 hibit-agent-focus-nonsteal-r1 incident: a catastrophic-
backtracking regex hung one bash call for 25h behind an unchanging
"Working..." footer).

FM_BUSY_TURN_MAX_SECS (default 3600s) now bounds how long a busy pane may
run with no completed turn (state/<id>.turn-ended, or its spawn record
before any turn has completed). Past the bound, busy_turn_over_age routes
the pane through the existing wedge_timer_check, reusing the identical
stale reason, escalation counter, and demand-deep-inspection marker for
human inspection only - never an automatic interrupt, signal, or restart
of the worker or its tool process. A completed turn resets the age.

Reproduced end-to-end against the real installed Pi TUI: a foreground
`sleep 999999` bash call with no timeout renders the actual busy footer,
and two captures ~15s apart show the elapsed counter changing the pane
hash while the same turn stays unfinished. Running the pre-fix watcher
against the real captures showed it never starts a wedge timer no matter
how long the pane stays busy; the fixed watcher starts and escalates the
timer through the same mechanism, while the real hung process remained
untouched and alive throughout.

* no-mistakes(review): fix: parse enriched AFK stale reasons

* no-mistakes(review): fix: preserve enriched wedges during AFK supervision

* no-mistakes(review): fix: route all enriched AFK wedges

* no-mistakes(document): Clarify busy-turn age supervision documentation

* fix(gitignore): ignore config/ as a directory, not by exact filename (#1261)

A name-by-name list of config/ entries silently stops ignoring any new or
home-local file placed there, which makes the working tree read as dirty and
blocks guarded sync paths that refuse to touch a dirty home. AGENTS.md
already documents config/ as captain-private and gitignored as a category;
this makes .gitignore match that contract.

* fix(tests): replace source-content .gitignore assertion w…
levelupself added a commit to levelupself/firstmate that referenced this pull request Aug 3, 2026
* fix: prevent no-mistakes gate agents from driving the fleet (#518)

* feat: contain no-mistakes gate agents from driving the fleet

Add bin/fm-gate-refuse-lib.sh, sourced at the top of fm-spawn/fm-send/
fm-teardown before any fleet mutation. It fails closed when NO_MISTAKES_GATE
is set, and via an unspoofable git-common-dir backstop when invoked from a
no-mistakes gate worktree (.no-mistakes/repos/*.git) even with the marker
unset. A normal firstmate session has neither signal and is unaffected.

Set disable_project_settings: true in the tracked .no-mistakes.yaml so the
installed pipeline neutralizes gate agents' project instructions for this repo
(trusted-only, honored from the default branch).

firstmate's own suite runs from a gate worktree during validation, so the
shared test helpers set FM_GATE_REFUSE_BYPASS=1 to exempt it; the dedicated
tests/fm-gate-refuse.test.sh strips it to verify real refusal.

* no-mistakes(review): Captain, refuse empty no-mistakes gate markers

* no-mistakes(document): Document no-mistakes gate authority boundary

* fix: guard secondmate primary sessions from blind turn ends (#505)

* fix: guard secondmate own-home turn ends

Remove the .fm-secondmate-home early-exit in fm-turnend-guard.sh so the
'no turn ends blind' backstop fires in a secondmate's own primary session,
matching the cd-guard's scope: the own home is guarded, child crew/scout
worktrees stay exempt via the retained git-dir/git-common-dir test. This
was pure scoping from the guard's primary-only origin and guarded against
no secondmate-specific hazard.

Add secondmate regression tests (blind-turn block, idle-by-default,
stop_hook_active loop guard, deferred-death recovery loop, child-worktree
exemption) and record the autonomous background-notify re-invoke
measurement (Claude Code 2.1.207, 11s) in docs/turnend-guard.md.

* no-mistakes(document): Correct secondmate guard documentation, captain

* fix: force-include marked secondmate homes in turn-end guard

The prior remove-only form (just deleting the .fm-secondmate-home check)
left the DEFAULT secondmate topology unguarded: a treehouse-leased home is
a linked git worktree (git-dir != git-common-dir), which the retained
git-dir exemption still skipped, so its own primary session could still end
a turn blind. Invert the marker: a genuinely-marked home is force-included
as a guarded primary (treehouse-leased linked OR git-cloned plain), and the
git-dir exemption applies only to UNMARKED child worktrees. Marker
validation (regular non-symlink file, non-empty id-token content) blocks a
stray or empty marker from spoofing inclusion.

Add real linked-worktree regression tests: a treehouse-leased LINKED
secondmate home is guarded, a stray/empty marker stays exempt, and the
unmarked child worktree stays exempt - the topology the plain git-init
fixtures masked. Predicates, in-flight gate, and loop guard untouched.

* fix: force ASCII collation in secondmate marker validation

Add a function-scoped local LC_ALL=C in fm_root_is_secondmate_home so the
[A-Za-z0-9._-] id allowlist matches under C collation, not the ambient
locale - a locale-crafted non-ASCII marker id can no longer slip through
the range match and spoof force-inclusion of a linked child worktree.
Add a regression test proving a non-ASCII marker id is rejected and the
linked worktree stays exempt.

* no-mistakes(test): fix backend baseline gate-refusal dependency

* no-mistakes(document): Correct secondmate turn-end guard documentation

* fix: make bootstrap diagnostics backend-aware (#519)

* fix: make bootstrap required-tool detection backend-aware

Bootstrap demanded tmux and treehouse for every backend except orca, so a
herdr/zellij/cmux home with tmux absent was wrongly told MISSING: tmux.

Required tools now follow the resolved backend via the single-owner
fm_backend_required_tools helper (bin/fm-backend.sh): each backend's own
session-provider CLI, jq for the JSON-emitting adapters (herdr/zellij/cmux),
and treehouse for session-provider-only backends (orca owns its worktree).
The treehouse lease-support check is gated to backends that use treehouse.

Adds install hints for herdr/zellij/cmux, regression tests for the full
backend dependency matrix (herdr-without-tmux repro plus each boundary),
and updates the authoritative Toolchain docs.

* no-mistakes(review): Captain, prevent executing Herdr install guidance

* no-mistakes(review): Captain, harden backend-aware bootstrap diagnostics

* no-mistakes(review): Captain, separate manual dependency remediation

* no-mistakes(review): Captain, align bootstrap diagnostic consumers

* no-mistakes(document): Align backend adapter dependency comments

* fix: preserve follow-up platform context after inbox cleanup (#520)

* fix: recover X/Discord follow-up platform after inbox cleanup

A milestone follow-up posted directly by request_id after the inbox was
drained - and with no task link, because one persistent secondmate's single
x_request slot collides across concurrent requests - resolved platform only
from the local inbox, so a >280 Discord reply silently defaulted to the X
280-char budget and threaded as (1/2).

- fm-x-poll records a durable per-request reply context
  (state/x-context/<rid>.json) at stash time, keyed by request_id so
  concurrent requests never overwrite each other; it survives inbox cleanup
  and restart.
- fm-x-reply resolves platform/budget through registry -> inbox -> relay
  (the relay lookup confined to a live follow-up), recovering the original
  platform independent of task-link availability.
- Fail-safe: a follow-up whose platform/budget cannot be authoritatively
  resolved and that would split is refused (exit 8) and held for retry,
  never wrongly split; fm-x-followup keeps the link on that exit.
- fm-x-dismiss clears the durable context for a dismissed mention.

Refactors reply-context extraction into a single owner and adds regression
coverage for all four cases.

* no-mistakes(review): Captain, fail closed on incomplete follow-up context

* no-mistakes(review): Captain, bound X context registry retention

* no-mistakes(review): Captain, align context retention with answer binding

* no-mistakes(document): Align X follow-up context documentation

* no-mistakes(document): Align durable X follow-up documentation

* fix: preserve secondmate routing markers in terminal sends (#533)

* fix: preserve secondmate routing markers

* no-mistakes(review): Captain, preserve trailing newlines in marked secondmate sends

* no-mistakes(test): Captain, tolerate bootstrap timeout elapsed drift

* no-mistakes(document): Refresh Herdr marker documentation

* fix: align Grok effort handling with 0.2.99 (#527)

* fix: align grok effort docs and spawn with 0.2.99 ceiling

grok 0.2.99 accepts only low|medium|high for --reasoning-effort and
rejects both xhigh and max. Omit unsupported values on spawn, flag them
in crew-dispatch validation, and update harness-adapters.

* no-mistakes(test): Captain: refresh gotmp teardown fixture dependencies

* no-mistakes(document): Clarify Grok effort documentation ownership

* fix: derive bearings from authoritative secondmate state (#555)

* fix: make bearings use secondmate home state

* test: anonymize bearings fixtures

* no-mistakes(review): Bound parent activity evidence scans, captain

* no-mistakes(review): Preserve structured secondmate authority and bounds, captain

* no-mistakes(review): Preserve registry completeness and child inventory, captain

* no-mistakes(review): Reconcile parent evidence by verb and key, captain

* no-mistakes(review): Treat unkeyed parent evidence as inconclusive, captain

* no-mistakes(document): Document bearings local snapshot and PR opt-in

* no-mistakes(lint): Fix fleet snapshot ShellCheck findings

* no-mistakes: apply CI fixes

* fix: restore fleet snapshots on stock macOS Bash (#578)

* fix: restore stock macOS snapshot parsing

* no-mistakes(document): Clarify Linux gate and macOS CI coverage

* fix(afk): make Pi escalation and return catch-up reliable (#587)

* fix: close away-mode blocker supervision gap

* no-mistakes(review): Gate teardown retries and verify U+2063 dedupe

* no-mistakes(test): Fail closed on incomplete Pi composer separators

* no-mistakes(document): Document Pi composer recognition and return gating

* feat: support Pi max reasoning profiles (#537)

* support Pi max thinking profiles

* no-mistakes(review): Captain, allow Pi max dispatch profiles

* chore: no-mistakes(document): Clarify yolo response ownership (#595)

* Clarify validation response ownership

* no-mistakes(document): Clarify yolo response ownership

* feat: establish instruction ownership foundation (#619)

* Add instruction owners foundation

* no-mistakes(document): Refresh project-management owner pointers

* fix: compress Firstmate contract and enforce delivery rigor ownership (#626)

* docs: compress firstmate operating contract

* docs: make delivery rigor single-owner

PR B already removed personal and stacked review requirements, but it did not explicitly assign rigor to the selected delivery path or forbid risk-based manual clean gates. That gap still permitted the Hi Bit inversion.

* no-mistakes(review): Honor configured merge authority across faster delivery paths

* no-mistakes(document): Align docs with compressed operating contract

* feat: add durable captain decision holds (#593)

* Add durable captain decision holds

* no-mistakes(review): Validate decision hold retries and origin paths

* no-mistakes(review): Enforce durable decision lifecycle boundaries

* no-mistakes(review): Harden decision display and partial retry recovery

* no-mistakes(test): Update scout teardown fixtures for decision inventory

* no-mistakes(document): Align decision lifecycle and scout teardown documentation

* no-mistakes: apply CI fixes

* no-mistakes(review): Reconcile terminal decision holds

* no-mistakes(document): Align captain decision-hold documentation

* fix(bin): harden PR check artifacts (#556)

* fix: harden PR check artifacts

* fix: close PR check migration gaps

* fix: close PR check retry gaps

* fix: clarify migration outcomes and ESM boundary

* fix: keep failed migrations authoritative

* test: use inert PR validation fixtures

* no-mistakes(review): Reserve noncanonical PR quarantine namespace

* no-mistakes(review): Prevalidate final PR-check teardown artifacts

* no-mistakes(review): Preserve X metadata and validate teardown IDs

* no-mistakes(review): Initialize migration state before watcher exclusion

* no-mistakes(review): Isolate failed poll migrations from bootstrap recovery

* no-mistakes(review): Allow safe polling during incomplete private repairs

* no-mistakes(review): Authenticate watcher checks at execution time

* no-mistakes(review): Preserve custom checks with hash-bound registration

* no-mistakes(review): Clean custom check snapshots on watcher signals

* no-mistakes(review): Stop watcher checks promptly on signals

* no-mistakes(review): Terminate watcher check groups before cleanup

* no-mistakes(document): Correct stale X-mode watcher documentation

* fix: drain returned watcher check groups

* no-mistakes(review): Harden quarantine links and recover validated replacement polls

* no-mistakes(review): Preserve X mode across shim version transitions

* no-mistakes(review): Refresh legacy X shims before marker short-circuits

* no-mistakes(document): Correct persisted PR-check artifact documentation

* no-mistakes(document): Correct stale PR-check documentation

* fix: bind PR poll repair provenance

* no-mistakes(review): Enforce single-link ownership for custom check artifacts

* no-mistakes(review): Preserve private checks, X polling, and lifecycle IDs

* no-mistakes(review): Separate task creation and legacy teardown validation

* no-mistakes(review): Restore safe legacy operations and teardown validation

* no-mistakes(review): Disambiguate migration obligations and preserve legacy retries

* no-mistakes(review): Preserve fail-closed diagnostics and legacy quarantine evidence

* no-mistakes(review): Reconcile legacy migration retries and teardown collisions

* no-mistakes(review): Force legacy namespace reconciliation before marker short-circuits

* no-mistakes(document): Document private poll artifact safety contracts

* no-mistakes(lint): Suppress intentional literal-dollar lint finding

* fix: migrate historical X poll identity

* fix: harden PR check artifacts

* no-mistakes(review): Preserve fail-closed diagnostics and legacy quarantine evidence

* no-mistakes(review): Reconcile legacy migration retries and teardown collisions

* fix: migrate historical X poll identity

* no-mistakes(review): Harden X-mode artifact publication against symlink corruption

* no-mistakes(review): Guard X artifact publication

* no-mistakes(review): Enforce private X artifact reads

* no-mistakes(test): Fix backend compatibility fixture dependencies

* no-mistakes(document): Refresh PR-check documentation

* no-mistakes(lint): Remove unused x-mode test locals

* no-mistakes: apply CI fixes

* fix(bin): compact session-start backlog digest (#636)

* fix: compact session-start backlog digest

* no-mistakes(test): Fix legacy backend fixture helper

* no-mistakes(test): Fix watcher exit wait helper

* no-mistakes(document): Document compact backlog digest

* fix: dedupe stale watcher guard banners (#637)

* fix: dedupe stale watcher guard banner

* no-mistakes(review): Keep read-only guard state nonmutating

* no-mistakes(document): Clarify stale watcher docs

* fix(bin): balance bearings landed baseline (#640)

* fix: balance bearings landed defaults

* no-mistakes(document): Document balanced landed baseline

* no-mistakes: apply CI fixes

* fix: clarify captain-facing translation contract (#644)

* docs: clarify captain-facing translation contract

* no-mistakes(review): Restore runtime fallback mandate

* no-mistakes(document): Align Bearings translation wording

* fix(bin): make bootstrap output and nudges deterministic (#646)

* fix: make bootstrap nudges deterministic

* no-mistakes(review): Honor state override for bootstrap nudges

* no-mistakes(review): Update benign bootstrap documentation labels

* no-mistakes(review): Validate bootstrap nudge retry markers

* no-mistakes(document): Align bootstrap nudge documentation

* no-mistakes: apply CI fixes

* docs(secondmate-provisioning): clarify concise registry ownership (#649)

* Clarify concise secondmate registry contract

* no-mistakes(review): Expand secondmate registry boilerplate coverage

* no-mistakes(document): Point route docs to owner

* fix(bin): strip quoted blocked_by values during decision hold resolve (#654)

* fix(bin): strip quotes on blocked_by in decision-hold resolve

tasks-axi quotes multi-entry blocked_by as "a,b,c", so the comma-boundary
membership test only matched middle elements. Strip surrounding quotes
before matching so first and last hold ids resolve correctly.

* no-mistakes(document): Refresh decision-hold regression evidence

* feat(secondmate): inherit shared captain preferences (#656)

* feat(secondmate): inherit shared captain preferences

* no-mistakes(review): Honor shared captain data overrides

* no-mistakes(review): Honor bootstrap data override registry

* no-mistakes(document): Refresh shared inheritance docs

* no-mistakes(document): Clarify inherited local-material docs

* feat: gate local agent secret injection (#658)

* feat(spawn): gate local agent secret injection

* fix(spawn): align final Keychain slot

* no-mistakes: apply CI fixes

* test: isolate Herdr autodetect smoke sessions (#662)

* test: isolate herdr autodetect smoke session

* no-mistakes(review): Restored autodetect smoke gate bypass

* no-mistakes(test): Harden Herdr lab provisioning

* no-mistakes(document): Refresh Herdr lab docs

* docs: adopt under way for active work (#666)

* Revert "feat: gate local agent secret injection (#658)" (#668)

This reverts commit c27135cd9d35bc4c237d49b3b374da31fbd52eef.

* fix(pi): distinguish stale locks when arming watcher (#681)

* 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 house vocabulary (#685)

* fix: accept secondmate as house vocabulary

* no-mistakes(test): Update captain vocabulary contract test

* no-mistakes(document): Align secondmate documentation vocabulary

* fix(bin): parse handoff homes after registry parentheticals (#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 (#687)

* feat: add native session-start nudges

* no-mistakes(document): Document nudge script inventory

* docs: call built-in defaults the firstmate repo, not template (#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.

* fix(bin): repair fm-brief.sh parse error and harden set -u array expansion (#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>
#166 apostrophe regression cannot return unnoticed.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(bin): keep watcher supervision continuous across child cycles (#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: fetch current PR head for review diffs (#722)

* 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

* docs: resolve five contract contradictions across AGENTS.md, README, and skills (#736)

* docs: resolve five contract contradictions

* no-mistakes(test): align owner-pointer assertions with reworded docs; skip absent shellcheck

* docs(harness): correct Grok exit guidance (#742)

* docs(harness): reverify grok exit command

* no-mistakes(test): Correct Grok exit resume attribution

* fix(watcher): bound stale wakes for parked crew (#743)

* 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

* fix(supervision): distinguish ordinary wakes from recovery (#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 (#745)

* 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

* feat(wake): enrich drained signals with bounded status context (#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 (#784)

* 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

* fix(send): treat opencode busy-queued composer state as submitted (#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

* fix(spawn): require two stable reads before accepting worktree path (#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>

* fix(bin): make watcher process identity immune to Linux wall-clock changes (#752)

* fix(watcher): stabilize Linux process identity

* no-mistakes(document): document FM_PROC_ROOT_OVERRIDE and Linux starttime identity rationale

* fix: prevent AFK idle stalls and stale run attribution (#758)

* 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(bin): allow safe teardown during watcher recovery (#750)

* 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

* feat(herdr): order presentation spaces while preserving focus (#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

* fix(bin): send literal config reread nudges after pushes (#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 (#797)

* 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

* fix(herdr): group projected children beneath owning parents (#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: keep local no-mistakes tests intent-targeted (#823)

* 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: add canonical timed test runner (#825)

* 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: surface main inventory gaps in Bearings (#830)

* 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 bounded concurrent test isolation proof (#832)

* 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: guard against missed secondmate reports (#834)

* 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: require pinned real-Herdr CI coverage (#838)

* 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

* feat: shard portable tests and add bounded local parallelism (#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

* fix: block primary-session delegation outside the fleet (#854)

* 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

* fix: install tasks-axi in portable CI shards (#866)

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(bin): make dispatch profiles quota aware (#867)

* 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

* Add built-in ahoy recap skill (#873)

* fix: preserve trustworthy Bearings data in partial snapshots (#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

* feat(pi): add session-local calm mode (#884)

* 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): prevent redundant watcher re-arms (#885)

* 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 (#895)

* 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 (#898)

* fix: execute every PR body compliance event

* no-mistakes(document): Document independent PR compliance events

* fix: exclude operational injections from ahoy boundaries (#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

* fix: canonically classify operational inputs across harnesses (#909)

* fix: type canonical operational inputs

* no-mistakes(document): Correct canonical operational-input documentation ownership

* fix: avoid generic secondmate start acknowledgements (#926)

* fix: avoid generic secondmate acknowledgements

* no-mistakes(document): Document sparse secondmate acknowledgement behavior

* no-mistakes: apply CI fixes

* fix(pi): make calm mode persistent and gapless (#927)

* fix(pi): preserve calm presentation across sessions

* no-mistakes(review): Fix Calm home fallback persistence

* no-mistakes(document): Clarify Calm gapless and export contracts

* no-mistakes: apply CI fixes

* fix(watch): retire merged PR polls after durable notification (#932)

* fix: retire merged PR polls after notification

* no-mistakes(review): Decouple PR retirement recovery from template updates

* no-mistakes(review): Recover pending PR retirements before poll migration

* no-mistakes(document): Document merged PR poll retirement contracts

* fix: refine scout intake and parallel dispatch (#934)

* Clarify intake evidence and overlap handling

* no-mistakes(review): Align scout guard with intake classification

* no-mistakes(document): Clarify scout documentation and intake ownership

* fix(pi): prevent duplicate assistant replies in Calm (#936)

* fix(pi): preserve operational follow-up semantics in Calm

* no-mistakes(document): Correct Calm operational-row visibility documentation

* perf(bin): shrink the ShellCheck source graph (#939)

* perf(lint): shrink shell source graph

* no-mistakes(review): Ensure lint workers terminate fully on cancellation

* no-mistakes(document): Repair stale lint documentation ownership

* fix(pi): remove Calm hidden-block gaps (#942)

* fix(pi): remove Calm hidden-block gaps

* no-mistakes(review): Validate Calm geometry against current viewport

* no-mistakes(review): Synchronize Calm geometry checks with reload completion

* fix: enforce contract boundaries for ask-user findings (#945)

* fix: escalate ask-user contract expansion

* no-mistakes(document): Point project management to authority owner

* docs: prefer direct operational paths (#946)

* fix(pi): hide operational user rows in Calm mode (#948)

* fix(pi): hide Calm operational user rows

* no-mistakes(review): Narrow Calm operational input suppression

* no-mistakes(review): Avoid Calm replay classifier subprocesses

* no-mistakes(document): docs: point Pi verification to Calm owner

* fix: relaunch missing second mates at session start (#950)

* fix(session-start): relaunch missing second mates

* fix(test): detect completed parallel workers

* no-mistakes(review): Isolate session-start recovery test cleanup

* no-mistakes(review): Complete backend-safe secondmate session recovery

* no-mistakes(review): Resolve Zellij task ownership before recovery

* no-mistakes(review): Recover relocated Zellij ghost tabs safely

* no-mistakes(review): Restore conservative Zellij recovery boundary

* no-mistakes(review): Reject malformed tmux recovery targets

* no-mistakes(document): Align secondmate recovery documentation

* no-mistakes: apply CI fixes

* fix(herdr): reclaim resumed task projections after restart (#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

* Teach Ahoy to surface open decisions (#968)

* Require shipshape routine acknowledgement (#969)

* docs: separate current guidance from verification evidence (#994)

* docs: separate current guides from verification

* no-mistakes(review): Restore Herdr 0.7.5 restart-reclaim verification evidence

* fix: preserve Claude watcher continuity across Stop hooks (#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

* fix(herdr): clean stale projections at session start (#996)

* 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

* fix: recover Claude supervision without watcher-status gate (#1001)

* fix: recover Claude supervision at session start

* fix: remove Claude watcher-status command gate

* no-mistakes(document): docs: remove stale continuity gate references

* fix: make quota-aware profile selection agent-owned (#1018)

* Replace quota dispatch selector instructions

* no-mistakes(review): Align bootstrap docs with agent-owned dispatch selection

* fix(bin): remove vestigial dispatch selector (#1026)

* 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

* docs(agents): drop superseded interim quota-window rule (#1039)

quota-axi 0.1.13 emits schemaVersion 2 with a quotaSemantics object per
provider, so the successor named in the interim rule has landed and the
rule's own removal condition is satisfied.

Keep the ownership clause so quota-axi remains the single owner of how
model or product windows relate to bounding account windows, and drop
the interim weakest-headroom instruction. The unknown-semantics case is
already covered by the existing requirement to stop and report a
candidate whose applicable quota data or interpretation cannot be
established.

Drop the matching assertion phrase from
tests/fm-instruction-owners.test.sh; the retained ownership phrase still
asserts.

* fix(tmux): scope busy detection and recognize current Claude turns (#1049)

* fix(tmux): scope Claude busy detection by harness

* no-mistakes(review): Separate verified and fallback busy signatures

* no-mistakes(test): Scope busy signatures to supplied harnesses

* no-mistakes(document): Document harness-scoped busy detection

* feat: add verified Kimi crewmate adapter (#1047)

* Add verified Kimi crewmate harness adapter

* no-mistakes(review): Scope Kimi moon detection to spinner lines

* no-mistakes(review): Match only complete Kimi spinner rows

* no-mistakes(review): Resolve Kimi binary portably before pane creation

* no-mistakes(document): Align Kimi adapter documentation

* no-mistakes(lint): Suppress false-positive ShellCheck warning for sourced watcher override

* Fix Kimi busy spinner detection

* no-mistakes(review): Recognize Kimi session-lock ancestry and holders

* no-mistakes(review): Scope pending-reply Kimi busy detection by harness

* no-mistakes(document): Correct Kimi spinner capture documentation

* no-mistakes(document): Clarify optional Kimi spinner whitespace

* no-mistakes(lint): Silence intentional pending-reply test stub warnings

* test: align rebased Kimi busy fixtures

* no-mistakes: apply CI fixes

* Reconcile Kimi busy detection after per-harness scoping

* no-mistakes(review): Clarify observed Kimi spinner whitespace contract

* no-mistakes(document): Clarify Kimi harness documentation

* fix: harden Kimi submission and spinner matching (#1058)

* fix kimi pointer submission and spinner conformance

* no-mistakes(review): Preserve Kimi submit target ownership guard

* feat(bin): add guarded Kimi turn-end wake (#1059)

* Add guarded Kimi turn-end hook

* no-mistakes(review): Require jq before installing Kimi turn-end hook

* no-mistakes(review): Expose jq inside isolated Kimi test fixtures

* no-mistakes(review): Preserve Kimi config boundaries during hook removal

* no-mistakes(review): Document Kimi removal newline safeguard

* no-mistakes(document): Document Kimi shared-home preservation

* fix(tmux): classify bordered composers across all rows (#1066)

* Fix structural tmux composer reading

* Verify Calm compatibility with Pi 0.82

* no-mistakes(review): Harden structural composer classification boundaries

* no-mistakes(review): Refresh composer and Kimi regression fixtures

* no-mistakes(review): Fail closed on unbounded composer edges

* no-mistakes(review): Enforce aligned composer geometry safely

* no-mistakes(review): Make composer ambiguity locale-safe

* no-mistakes(review): Preserve ambiguity through composer submission

* no-mistakes(review): Carry composer proof through retries

* no-mistakes(document): Document structural tmux composer delivery guarantees

* no-mistakes: apply CI fixes

* feat(bin): add verified pi-signed runtime adapter (#1145)

* feat: add verified pi-signed adapter

* no-mistakes(review): Correct pi-signed maintainer verification date

* no-mistakes(review): Correct remaining pi-signed verification dates

* no-mistakes(review): Preserve authoritative pi-signed runtime identity

* no-mistakes(document): Document pi-signed shared adapter semantics

* no-mistakes: apply CI fixes

* fix(pi): rearm watcher across session transitions (#1166)

* fix(pi): rearm watcher across same-process session transitions

Pi emits session_shutdown for ordinary /new, /resume, and /fork replacement
as well as terminal quit. The primary watcher extension latched a module-level
stopping flag on every shutdown, so a replacement session in the same process
could not arm monitoring until Pi restarted.

Own arm authority per session generation so only the active live generation
may start, stop, or rearm the child. Replacement sessions can arm again without
restarting Pi, stale prior-generation callbacks cannot mutate the active cycle,
and real quit still blocks late rearm.

* no-mistakes(review): Preserve Pi generation isolation and exit cleanup

* no-mistakes(document): Correct Pi watcher transition documentation

* feat: route crew dispatch using quota-window pace (#1172)

* Consume quota-axi pace signals in dispatch profile array selection.

Add quota-array-dispatch as the single owner of the pace-aware candidate
choice, keep AGENTS.md to the intake boundary and load trigger, and cover
the acceptance cases with sanitized schemaVersion 3 fixtures.

* no-mistakes(review): Stop and report genuine quota dispatch ties

* no-mistakes(document): Document quota pace freshness and uncertainty

* fix: adapt Grok Stop continuation and harden endpoint cleanup (#1171)

* fix(grok): adapt Stop continuation to runtime capability

* no-mistakes(review): Reject ambiguous Grok Stop payloads

* no-mistakes(review): Reject duplicate Grok fields and accept spaced tmux sessions

* no-mistakes(review): Enforce exact tmux cleanup selectors

* no-mistakes(test): Fix historical tmux fixture and validate Grok Stop

* no-mistakes: apply CI fixes

* fix: restore stock macOS Bash 3.2 brief scaffolding (#1093)

* fix(brief): make DOD scaffolding parse-safe on stock macOS Bash 3.2

fm-brief.sh built each Definition-of-done block and the not-enabled
Herdr declaration with `VAR=$(cat <<EOF ... EOF)`. On Bash 3.2 (macOS
/bin/bash) the lexer scans for the command substitution's closing `)`
textually and tracks quote state through the heredoc body, so a single
apostrophe, unbalanced quote, or unbalanced paren in that prose breaks
parsing of the whole script. Every ship-brief scaffold (no-mistakes,
direct-PR, local-only) failed with `unexpected EOF while looking for
matching )`. Bash 4+ parses it fine, so the breakage stayed invisible
everywhere except stock macOS.

Replace all four command-substitution heredocs with
`IFS= read -r -d '' VAR <<EOF || true`. That removes the `$(...)`
wrapper and the entire defect class regardless of future prose, and
preserves the variable expansion the direct-PR and local-only bodies
need. `read` keeps the heredoc's trailing newline that `$(...)` used to
strip, so trim one newline to keep every generated brief byte-identical
to prior output.

Guard the structure, not one historical phrase: a new test rejects any
heredoc nested in a command substitution anywhere in fm-brief.sh, where
the old assertion pinned a single apostrophe phrase and so missed the
reintroduction. Extend the stock-macOS Bash CI job from parsing one
script to the whole maintained shell surface (bin/*.sh,
bin/backends/*.sh, tests/*.sh), matching bin/fm-lint.sh's canonical file
set so parse scope and lint scope cannot drift apart.

* no-mistakes(review): Captain: harden Bash structure and inventory guards

* no-mistakes(document): Align stock macOS Bash contributor checks

* no-mistakes(lint): Suppress deliberate SC2016 literal fixture warnings

* test: stabilize tmux teardown conformance baseline (#1209)

* fix(test): pin teardown tmux baseline to historical kill selectors

merge-base HEAD main collapses to HEAD after the exact-selector change
lands on the default branch, so the old teardown fixture was accidentally
exercising current exact targets. Resolve a content-historical permissive
tmux adapter from first-parent history and force that post-squash topology
inside the conformance case so main and feature branches keep the same
old-vs-new contract.

* no-mistakes(lint): Suppress intentional literal-pattern ShellCheck warnings

* docs: slim quota-array-dispatch to the pace selection core (#1197)

Cut the runtime skill to the compact pace-aware selection procedure plus
minimum owner pointers. Keep every distinct decision rule and move expanded
acceptance scenarios to deterministic fixture ownership assertions.

Size: 170/1374/10187 -> 63/544/4068 (about 63%/60%/60% reduction).

* feat(bin): inherit backend config into secondmate homes (#1219)

* Inherit config/backend into secondmate homes with deliberate-override preservation

Add backend to the shared inheritable config allowlist so launch, locked
bootstrap, and config-push converge a primary pin into secondmate homes as each
home local future-spawn default. Track last-inherited bytes in a private state
provenance marker so deliberate per-home overrides survive present and absent
primary convergence, keep --backend and FM_BACKEND stronger, and extend the
existing inheritance tests plus docs and skill claims.

* no-mistakes(review): Preserve equal unprovenanced backend overrides

* no-mistakes(review): Preserve symlink overrides and verify spawn precedence

* no-mistakes(review): Snapshot backend inheritance for consistent provenance

* no-mistakes(review): Simplify backend inheritance to primary-authoritative convergence

* no-mistakes(document): Document inherited backend override preservation

* fix: restore primary-authoritative backend inheritance after document regression

The document step reintroduced provenance and deliberate per-home override
semantics after review had simplified config/backend to plain primary-authoritative
allowlist membership. Restore the primary-always-wins path: present overwrites,
absent removes, no provenance marker, and docs/tests match that contract.

* no-mistakes(review): Add divergent backend precedence regression fixtures

* no-mistakes(document): Document backend inheritance contract

* fix(pi): remove Calm's upper version ceiling (#1226)

* fix(pi): remove Calm's exclusive Pi upper-version ceiling

tests/fm-calm-pi-extension.test.sh gated on a closed PI_COMPAT_VERSIONS
allowlist ("0.81.1 0.82.0") that refused any other installed Pi, and docs
described that range as "supported" rather than verified evidence. The
Calm CHANGELOG shows no API introduced at either version, so there is no
evidence for a real minimum; the presentation adapters already probe the
exact method they patch rather than checking a version.

Replace the allowlist with dated version evidence that never rejects a
newer Pi, and make each presentation adapter degrade independently with
a diagnostic if a future Pi removes its API, instead of the whole Calm
extension failing to load. Rewrite the feasibility doc's "Pi 0.81.1
through 0.82.0" phrasing to state it as verified evidence, not a
ceiling.

* no-mistakes(review): Probe missing Calm adapter exports safely

* no-mistakes(document): Document Calm's unbounded Pi compatibility

* fix(bin): allow session-local todo tools in the subagent guard (#1204)

* fix(guard): allow session-local todo tools in the primary

The delegation-shape guard denied TaskCreate and TaskUpdate because their
normalized names contain the `task` stem. Those tools write only the harness's
session-local todo list, which has no executor: it spawns no agent, allocates
no worktree, registers no schedule, and starts nothing that outlives the
session. That is not the unaccounted work the guard exists to stop, so the stem
match was a false positive, and the deny text told the primary to run
bin/fm-brief.sh and bin/fm-spawn.sh to create a todo entry.

Add a separately-reasoned PLAN_ONLY_TOOLS exact-name exclusion rather than
widening OBSERVE_ONLY_TOOLS, whose documented contract is tools that only
observe or stop existing work. Both lists stay exact-name so neither can widen
by substring.

Tests cover the two allowed names and six near-miss names that a substring or
shortened-stem widening would release; both mutations were watched red.

* no-mistakes(review): drop session-local todo tools from recommended deny list

* no-mistakes: apply CI fixes

* fix(session-lock): resolve Claude bg-spare ancestry to the outermost claude pid (#1206)

* fix(session-lock): resolve Claude bg-spare ancestry to the outermost claude pid

fm_harness_ancestry_pid() previously returned the first ancestor process
whose command matched a verified harness name. Claude Code's Stop hook
fires as a bg-spare worker several levels below the session's actual
lock-owning claude process (hook shell -> claude bg-spare ->
claude bg-pty-host -> claude -> claude(lock)), so the first match was
the bg-spare worker, not the lock owner. fm_session_lock_owned_by_self()
then never matched state/.lock, and the Claude Stop auto-arm silently
treated its own primary session as an unrelated live owner and never
armed the watcher.

The walk now keeps going past a claude-named match, looking for a still
more ancestral claude-named match, and stops the instant a non-match
follows an already-found match (bounding it to a contiguous run rather
than the literal ancestry top, so an unrelated claude-named process
further up the real process tree is never mistaken for part of this
session's own nested chain). Every other harness keeps the original
first-match-wins behavior, since e.g. Pi's shared signed-wrapper
ancestry actually holds the session at the inner engine pid, not an
outer wrapper pid. Hop limit raised from 8 to 16 to cover the deeper
bg-spare chain.

* no-mistakes(review): Add nested-claude-ancestry regression test; fix nudge doc depth claim

* no-mistakes: apply CI fixes

* fix: conferma l'avvio del watcher su Windows/MSYS (#1212)

* fix: confirm watcher startup on MSYS

* no-mistakes(review): gate MSYS arm ready timeout, cache uname, harden locale test

* no-mistakes(review): validate OpenCode ready timeout, make uname cache internal

* fix(spawn): forward CLAUDE_CONFIG_DIR to claude crewmates (#1195)

* fix(spawn): forward firstmate's CLAUDE_CONFIG_DIR to claude crewmates

Crewmate panes are created by a long-lived tmux/herdr daemon that does not
inherit firstmate's current environment. When firstmate runs under a non-default
CLAUDE_CONFIG_DIR (for example a work-vs-personal subscription split), a bare
`claude` in the crewmate pane fell back to the default ~/.claude store and
launched unauthenticated, blocking the crewmate before it could do any work.

fm-spawn now prefixes the claude launch with firstmate's own resolved
CLAUDE_CONFIG_DIR when set, so the crewmate uses the same credential/config
store firstmate is authenticated with. An unset value is the single-store
default and adds no prefix; non-claude harnesses are unaffected.

Adds three tests in fm-spawn-dispatch-profile.test.sh (forwarded-when-set,
omitted-when-unset, non-claude-ignored) and pins CLAUDE_CONFIG_DIR in the test
helper so launch assertions no longer depend on the developer's environment.

* no-mistakes: apply CI fixes

* fix: preserve dispatch identity across authentication checks (#1233)

* fix: preserve dispatch harness identity

* no-mistakes(review): Fix Grok counterfactual tuple validation

* no-mistakes(document): Scope dispatch authentication to selected tuple

* fix: restore dispatch instruction budget

* no-mistakes(review): Scope dispatch authentication after candidate selection

* fix(bin): normalize relative durable paths (#1256)

* fix(bin): handle dash-leading harness process names (#2)

* fix: handle dash-leading harness process names

* no-mistakes(review): Make dash-leading harness regression hermetic

* fix: preserve secondmate reply routes across relative homes

Resolve relative home, data, and state inputs before …
vipentti pushed a commit to vipentti/firstmate that referenced this pull request Aug 5, 2026
* fix: escalate ask-user contract expansion

* no-mistakes(document): Point project management to authority owner
elixlabssolutions added a commit to xLabs-OS/firstmate that referenced this pull request Aug 6, 2026
…ing both fork commits (#4)

* fix: compress Firstmate contract and enforce delivery rigor ownership (#626)

* docs: compress firstmate operating contract

* docs: make delivery rigor single-owner

PR B already removed personal and stacked review requirements, but it did not explicitly assign rigor to the selected delivery path or forbid risk-based manual clean gates. That gap still permitted the Hi Bit inversion.

* no-mistakes(review): Honor configured merge authority across faster delivery paths

* no-mistakes(document): Align docs with compressed operating contract

* feat: add durable captain decision holds (#593)

* Add durable captain decision holds

* no-mistakes(review): Validate decision hold retries and origin paths

* no-mistakes(review): Enforce durable decision lifecycle boundaries

* no-mistakes(review): Harden decision display and partial retry recovery

* no-mistakes(test): Update scout teardown fixtures for decision inventory

* no-mistakes(document): Align decision lifecycle and scout teardown documentation

* no-mistakes: apply CI fixes

* no-mistakes(review): Reconcile terminal decision holds

* no-mistakes(document): Align captain decision-hold documentation

* fix(bin): harden PR check artifacts (#556)

* fix: harden PR check artifacts

* fix: close PR check migration gaps

* fix: close PR check retry gaps

* fix: clarify migration outcomes and ESM boundary

* fix: keep failed migrations authoritative

* test: use inert PR validation fixtures

* no-mistakes(review): Reserve noncanonical PR quarantine namespace

* no-mistakes(review): Prevalidate final PR-check teardown artifacts

* no-mistakes(review): Preserve X metadata and validate teardown IDs

* no-mistakes(review): Initialize migration state before watcher exclusion

* no-mistakes(review): Isolate failed poll migrations from bootstrap recovery

* no-mistakes(review): Allow safe polling during incomplete private repairs

* no-mistakes(review): Authenticate watcher checks at execution time

* no-mistakes(review): Preserve custom checks with hash-bound registration

* no-mistakes(review): Clean custom check snapshots on watcher signals

* no-mistakes(review): Stop watcher checks promptly on signals

* no-mistakes(review): Terminate watcher check groups before cleanup

* no-mistakes(document): Correct stale X-mode watcher documentation

* fix: drain returned watcher check groups

* no-mistakes(review): Harden quarantine links and recover validated replacement polls

* no-mistakes(review): Preserve X mode across shim version transitions

* no-mistakes(review): Refresh legacy X shims before marker short-circuits

* no-mistakes(document): Correct persisted PR-check artifact documentation

* no-mistakes(document): Correct stale PR-check documentation

* fix: bind PR poll repair provenance

* no-mistakes(review): Enforce single-link ownership for custom check artifacts

* no-mistakes(review): Preserve private checks, X polling, and lifecycle IDs

* no-mistakes(review): Separate task creation and legacy teardown validation

* no-mistakes(review): Restore safe legacy operations and teardown validation

* no-mistakes(review): Disambiguate migration obligations and preserve legacy retries

* no-mistakes(review): Preserve fail-closed diagnostics and legacy quarantine evidence

* no-mistakes(review): Reconcile legacy migration retries and teardown collisions

* no-mistakes(review): Force legacy namespace reconciliation before marker short-circuits

* no-mistakes(document): Document private poll artifact safety contracts

* no-mistakes(lint): Suppress intentional literal-dollar lint finding

* fix: migrate historical X poll identity

* fix: harden PR check artifacts

* no-mistakes(review): Preserve fail-closed diagnostics and legacy quarantine evidence

* no-mistakes(review): Reconcile legacy migration retries and teardown collisions

* fix: migrate historical X poll identity

* no-mistakes(review): Harden X-mode artifact publication against symlink corruption

* no-mistakes(review): Guard X artifact publication

* no-mistakes(review): Enforce private X artifact reads

* no-mistakes(test): Fix backend compatibility fixture dependencies

* no-mistakes(document): Refresh PR-check documentation

* no-mistakes(lint): Remove unused x-mode test locals

* no-mistakes: apply CI fixes

* fix(bin): compact session-start backlog digest (#636)

* fix: compact session-start backlog digest

* no-mistakes(test): Fix legacy backend fixture helper

* no-mistakes(test): Fix watcher exit wait helper

* no-mistakes(document): Document compact backlog digest

* fix: dedupe stale watcher guard banners (#637)

* fix: dedupe stale watcher guard banner

* no-mistakes(review): Keep read-only guard state nonmutating

* no-mistakes(document): Clarify stale watcher docs

* fix(bin): balance bearings landed baseline (#640)

* fix: balance bearings landed defaults

* no-mistakes(document): Document balanced landed baseline

* no-mistakes: apply CI fixes

* fix: clarify captain-facing translation contract (#644)

* docs: clarify captain-facing translation contract

* no-mistakes(review): Restore runtime fallback mandate

* no-mistakes(document): Align Bearings translation wording

* fix(bin): make bootstrap output and nudges deterministic (#646)

* fix: make bootstrap nudges deterministic

* no-mistakes(review): Honor state override for bootstrap nudges

* no-mistakes(review): Update benign bootstrap documentation labels

* no-mistakes(review): Validate bootstrap nudge retry markers

* no-mistakes(document): Align bootstrap nudge documentation

* no-mistakes: apply CI fixes

* docs(secondmate-provisioning): clarify concise registry ownership (#649)

* Clarify concise secondmate registry contract

* no-mistakes(review): Expand secondmate registry boilerplate coverage

* no-mistakes(document): Point route docs to owner

* fix(bin): strip quoted blocked_by values during decision hold resolve (#654)

* fix(bin): strip quotes on blocked_by in decision-hold resolve

tasks-axi quotes multi-entry blocked_by as "a,b,c", so the comma-boundary
membership test only matched middle elements. Strip surrounding quotes
before matching so first and last hold ids resolve correctly.

* no-mistakes(document): Refresh decision-hold regression evidence

* feat(secondmate): inherit shared captain preferences (#656)

* feat(secondmate): inherit shared captain preferences

* no-mistakes(review): Honor shared captain data overrides

* no-mistakes(review): Honor bootstrap data override registry

* no-mistakes(document): Refresh shared inheritance docs

* no-mistakes(document): Clarify inherited local-material docs

* feat: gate local agent secret injection (#658)

* feat(spawn): gate local agent secret injection

* fix(spawn): align final Keychain slot

* no-mistakes: apply CI fixes

* test: isolate Herdr autodetect smoke sessions (#662)

* test: isolate herdr autodetect smoke session

* no-mistakes(review): Restored autodetect smoke gate bypass

* no-mistakes(test): Harden Herdr lab provisioning

* no-mistakes(document): Refresh Herdr lab docs

* docs: adopt under way for active work (#666)

* Revert "feat: gate local agent secret injection (#658)" (#668)

This reverts commit c27135cd9d35bc4c237d49b3b374da31fbd52eef.

* fix(pi): distinguish stale locks when arming watcher (#681)

* 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 house vocabulary (#685)

* fix: accept secondmate as house vocabulary

* no-mistakes(test): Update captain vocabulary contract test

* no-mistakes(document): Align secondmate documentation vocabulary

* fix(bin): parse handoff homes after registry parentheticals (#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 (#687)

* feat: add native session-start nudges

* no-mistakes(document): Document nudge script inventory

* docs: call built-in defaults the firstmate repo, not template (#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.

* fix(bin): repair fm-brief.sh parse error and harden set -u array expansion (#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>
#166 apostrophe regression cannot return unnoticed.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(bin): keep watcher supervision continuous across child cycles (#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: fetch current PR head for review diffs (#722)

* 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

* docs: resolve five contract contradictions across AGENTS.md, README, and skills (#736)

* docs: resolve five contract contradictions

* no-mistakes(test): align owner-pointer assertions with reworded docs; skip absent shellcheck

* docs(harness): correct Grok exit guidance (#742)

* docs(harness): reverify grok exit command

* no-mistakes(test): Correct Grok exit resume attribution

* fix(watcher): bound stale wakes for parked crew (#743)

* 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

* fix(supervision): distinguish ordinary wakes from recovery (#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 (#745)

* 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

* feat(wake): enrich drained signals with bounded status context (#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 (#784)

* 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

* fix(send): treat opencode busy-queued composer state as submitted (#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

* fix(spawn): require two stable reads before accepting worktree path (#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>

* fix(bin): make watcher process identity immune to Linux wall-clock changes (#752)

* fix(watcher): stabilize Linux process identity

* no-mistakes(document): document FM_PROC_ROOT_OVERRIDE and Linux starttime identity rationale

* fix: prevent AFK idle stalls and stale run attribution (#758)

* 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(bin): allow safe teardown during watcher recovery (#750)

* 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

* feat(herdr): order presentation spaces while preserving focus (#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

* fix(bin): send literal config reread nudges after pushes (#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 (#797)

* 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

* fix(herdr): group projected children beneath owning parents (#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: keep local no-mistakes tests intent-targeted (#823)

* 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: add canonical timed test runner (#825)

* 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: surface main inventory gaps in Bearings (#830)

* 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 bounded concurrent test isolation proof (#832)

* 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: guard against missed secondmate reports (#834)

* 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: require pinned real-Herdr CI coverage (#838)

* 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

* feat: shard portable tests and add bounded local parallelism (#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

* fix: block primary-session delegation outside the fleet (#854)

* 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

* fix: install tasks-axi in portable CI shards (#866)

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(bin): make dispatch profiles quota aware (#867)

* 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

* Add built-in ahoy recap skill (#873)

* fix: preserve trustworthy Bearings data in partial snapshots (#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

* feat(pi): add session-local calm mode (#884)

* 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): prevent redundant watcher re-arms (#885)

* 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 (#895)

* 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 (#898)

* fix: execute every PR body compliance event

* no-mistakes(document): Document independent PR compliance events

* fix: exclude operational injections from ahoy boundaries (#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

* fix: canonically classify operational inputs across harnesses (#909)

* fix: type canonical operational inputs

* no-mistakes(document): Correct canonical operational-input documentation ownership

* fix: avoid generic secondmate start acknowledgements (#926)

* fix: avoid generic secondmate acknowledgements

* no-mistakes(document): Document sparse secondmate acknowledgement behavior

* no-mistakes: apply CI fixes

* fix(pi): make calm mode persistent and gapless (#927)

* fix(pi): preserve calm presentation across sessions

* no-mistakes(review): Fix Calm home fallback persistence

* no-mistakes(document): Clarify Calm gapless and export contracts

* no-mistakes: apply CI fixes

* fix(watch): retire merged PR polls after durable notification (#932)

* fix: retire merged PR polls after notification

* no-mistakes(review): Decouple PR retirement recovery from template updates

* no-mistakes(review): Recover pending PR retirements before poll migration

* no-mistakes(document): Document merged PR poll retirement contracts

* fix: refine scout intake and parallel dispatch (#934)

* Clarify intake evidence and overlap handling

* no-mistakes(review): Align scout guard with intake classification

* no-mistakes(document): Clarify scout documentation and intake ownership

* fix(pi): prevent duplicate assistant replies in Calm (#936)

* fix(pi): preserve operational follow-up semantics in Calm

* no-mistakes(document): Correct Calm operational-row visibility documentation

* perf(bin): shrink the ShellCheck source graph (#939)

* perf(lint): shrink shell source graph

* no-mistakes(review): Ensure lint workers terminate fully on cancellation

* no-mistakes(document): Repair stale lint documentation ownership

* fix(pi): remove Calm hidden-block gaps (#942)

* fix(pi): remove Calm hidden-block gaps

* no-mistakes(review): Validate Calm geometry against current viewport

* no-mistakes(review): Synchronize Calm geometry checks with reload completion

* fix: enforce contract boundaries for ask-user findings (#945)

* fix: escalate ask-user contract expansion

* no-mistakes(document): Point project management to authority owner

* docs: prefer direct operational paths (#946)

* fix(pi): hide operational user rows in Calm mode (#948)

* fix(pi): hide Calm operational user rows

* no-mistakes(review): Narrow Calm operational input suppression

* no-mistakes(review): Avoid Calm replay classifier subprocesses

* no-mistakes(document): docs: point Pi verification to Calm owner

* fix: relaunch missing second mates at session start (#950)

* fix(session-start): relaunch missing second mates

* fix(test): detect completed parallel workers

* no-mistakes(review): Isolate session-start recovery test cleanup

* no-mistakes(review): Complete backend-safe secondmate session recovery

* no-mistakes(review): Resolve Zellij task ownership before recovery

* no-mistakes(review): Recover relocated Zellij ghost tabs safely

* no-mistakes(review): Restore conservative Zellij recovery boundary

* no-mistakes(review): Reject malformed tmux recovery targets

* no-mistakes(document): Align secondmate recovery documentation

* no-mistakes: apply CI fixes

* fix(herdr): reclaim resumed task projections after restart (#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

* Teach Ahoy to surface open decisions (#968)

* Require shipshape routine acknowledgement (#969)

* docs: separate current guidance from verification evidence (#994)

* docs: separate current guides from verification

* no-mistakes(review): Restore Herdr 0.7.5 restart-reclaim verification evidence

* fix: preserve Claude watcher continuity across Stop hooks (#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

* fix(herdr): clean stale projections at session start (#996)

* 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

* fix: recover Claude supervision without watcher-status gate (#1001)

* fix: recover Claude supervision at session start

* fix: remove Claude watcher-status command gate

* no-mistakes(document): docs: remove stale continuity gate references

* fix: make quota-aware profile selection agent-owned (#1018)

* Replace quota dispatch selector instructions

* no-mistakes(review): Align bootstrap docs with agent-owned dispatch selection

* fix(bin): remove vestigial dispatch selector (#1026)

* 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

* docs(agents): drop superseded interim quota-window rule (#1039)

quota-axi 0.1.13 emits schemaVersion 2 with a quotaSemantics object per
provider, so the successor named in the interim rule has landed and the
rule's own removal condition is satisfied.

Keep the ownership clause so quota-axi remains the single owner of how
model or product windows relate to bounding account windows, and drop
the interim weakest-headroom instruction. The unknown-semantics case is
already covered by the existing requirement to stop and report a
candidate whose applicable quota data or interpretation cannot be
established.

Drop the matching assertion phrase from
tests/fm-instruction-owners.test.sh; the retained ownership phrase still
asserts.

* fix(tmux): scope busy detection and recognize current Claude turns (#1049)

* fix(tmux): scope Claude busy detection by harness

* no-mistakes(review): Separate verified and fallback busy signatures

* no-mistakes(test): Scope busy signatures to supplied harnesses

* no-mistakes(document): Document harness-scoped busy detection

* feat: add verified Kimi crewmate adapter (#1047)

* Add verified Kimi crewmate harness adapter

* no-mistakes(review): Scope Kimi moon detection to spinner lines

* no-mistakes(review): Match only complete Kimi spinner rows

* no-mistakes(review): Resolve Kimi binary portably before pane creation

* no-mistakes(document): Align Kimi adapter documentation

* no-mistakes(lint): Suppress false-positive ShellCheck warning for sourced watcher override

* Fix Kimi busy spinner detection

* no-mistakes(review): Recognize Kimi session-lock ancestry and holders

* no-mistakes(review): Scope pending-reply Kimi busy detection by harness

* no-mistakes(document): Correct Kimi spinner capture documentation

* no-mistakes(document): Clarify optional Kimi spinner whitespace

* no-mistakes(lint): Silence intentional pending-reply test stub warnings

* test: align rebased Kimi busy fixtures

* no-mistakes: apply CI fixes

* Reconcile Kimi busy detection after per-harness scoping

* no-mistakes(review): Clarify observed Kimi spinner whitespace contract

* no-mistakes(document): Clarify Kimi harness documentation

* fix: harden Kimi submission and spinner matching (#1058)

* fix kimi pointer submission and spinner conformance

* no-mistakes(review): Preserve Kimi submit target ownership guard

* feat(bin): add guarded Kimi turn-end wake (#1059)

* Add guarded Kimi turn-end hook

* no-mistakes(review): Require jq before installing Kimi turn-end hook

* no-mistakes(review): Expose jq inside isolated Kimi test fixtures

* no-mistakes(review): Preserve Kimi config boundaries during hook removal

* no-mistakes(review): Document Kimi removal newline safeguard

* no-mistakes(document): Document Kimi shared-home preservation

* fix(tmux): classify bordered composers across all rows (#1066)

* Fix structural tmux composer reading

* Verify Calm compatibility with Pi 0.82

* no-mistakes(review): Harden structural composer classification boundaries

* no-mistakes(review): Refresh composer and Kimi regression fixtures

* no-mistakes(review): Fail closed on unbounded composer edges

* no-mistakes(review): Enforce aligned composer geometry safely

* no-mistakes(review): Make composer ambiguity locale-safe

* no-mistakes(review): Preserve ambiguity through composer submission

* no-mistakes(review): Carry composer proof through retries

* no-mistakes(document): Document structural tmux composer delivery guarantees

* no-mistakes: apply CI fixes

* feat(bin): add verified pi-signed runtime adapter (#1145)

* feat: add verified pi-signed adapter

* no-mistakes(review): Correct pi-signed maintainer verification date

* no-mistakes(review): Correct remaining pi-signed verification dates

* no-mistakes(review): Preserve authoritative pi-signed runtime identity

* no-mistakes(document): Document pi-signed shared adapter semantics

* no-mistakes: apply CI fixes

* fix(pi): rearm watcher across session transitions (#1166)

* fix(pi): rearm watcher across same-process session transitions

Pi emits session_shutdown for ordinary /new, /resume, and /fork replacement
as well as terminal quit. The primary watcher extension latched a module-level
stopping flag on every shutdown, so a replacement session in the same process
could not arm monitoring until Pi restarted.

Own arm authority per session generation so only the active live generation
may start, stop, or rearm the child. Replacement sessions can arm again without
restarting Pi, stale prior-generation callbacks cannot mutate the active cycle,
and real quit still blocks late rearm.

* no-mistakes(review): Preserve Pi generation isolation and exit cleanup

* no-mistakes(document): Correct Pi watcher transition documentation

* feat: route crew dispatch using quota-window pace (#1172)

* Consume quota-axi pace signals in dispatch profile array selection.

Add quota-array-dispatch as the single owner of the pace-aware candidate
choice, keep AGENTS.md to the intake boundary and load trigger, and cover
the acceptance cases with sanitized schemaVersion 3 fixtures.

* no-mistakes(review): Stop and report genuine quota dispatch ties

* no-mistakes(document): Document quota pace freshness and uncertainty

* fix: adapt Grok Stop continuation and harden endpoint cleanup (#1171)

* fix(grok): adapt Stop continuation to runtime capability

* no-mistakes(review): Reject ambiguous Grok Stop payloads

* no-mistakes(review): Reject duplicate Grok fields and accept spaced tmux sessions

* no-mistakes(review): Enforce exact tmux cleanup selectors

* no-mistakes(test): Fix historical tmux fixture and validate Grok Stop

* no-mistakes: apply CI fixes

* fix: restore stock macOS Bash 3.2 brief scaffolding (#1093)

* fix(brief): make DOD scaffolding parse-safe on stock macOS Bash 3.2

fm-brief.sh built each Definition-of-done block and the not-enabled
Herdr declaration with `VAR=$(cat <<EOF ... EOF)`. On Bash 3.2 (macOS
/bin/bash) the lexer scans for the command substitution's closing `)`
textually and tracks quote state through the heredoc body, so a single
apostrophe, unbalanced quote, or unbalanced paren in that prose breaks
parsing of the whole script. Every ship-brief scaffold (no-mistakes,
direct-PR, local-only) failed with `unexpected EOF while looking for
matching )`. Bash 4+ parses it fine, so the breakage stayed invisible
everywhere except stock macOS.

Replace all four command-substitution heredocs with
`IFS= read -r -d '' VAR <<EOF || true`. That removes the `$(...)`
wrapper and the entire defect class regardless of future prose, and
preserves the variable expansion the direct-PR and local-only bodies
need. `read` keeps the heredoc's trailing newline that `$(...)` used to
strip, so trim one newline to keep every generated brief byte-identical
to prior output.

Guard the structure, not one historical phrase: a new test rejects any
heredoc nested in a command substitution anywhere in fm-brief.sh, where
the old assertion pinned a single apostrophe phrase and so missed the
reintroduction. Extend the stock-macOS Bash CI job from parsing one
script to the whole maintained shell surface (bin/*.sh,
bin/backends/*.sh, tests/*.sh), matching bin/fm-lint.sh's canonical file
set so parse scope and lint scope cannot drift apart.

* no-mistakes(review): Captain: harden Bash structure and inventory guards

* no-mistakes(document): Align stock macOS Bash contributor checks

* no-mistakes(lint): Suppress deliberate SC2016 literal fixture warnings

* test: stabilize tmux teardown conformance baseline (#1209)

* fix(test): pin teardown tmux baseline to historical kill selectors

merge-base HEAD main collapses to HEAD after the exact-selector change
lands on the default branch, so the old teardown fixture was accidentally
exercising current exact targets. Resolve a content-historical permissive
tmux adapter from first-parent history and force that post-squash topology
inside the conformance case so main and feature branches keep the same
old-vs-new contract.

* no-mistakes(lint): Suppress intentional literal-pattern ShellCheck warnings

* docs: slim quota-array-dispatch to the pace selection core (#1197)

Cut the runtime skill to the compact pace-aware selection procedure plus
minimum owner pointers. Keep every distinct decision rule and move expanded
acceptance scenarios to deterministic fixture ownership assertions.

Size: 170/1374/10187 -> 63/544/4068 (about 63%/60%/60% reduction).

* feat(bin): inherit backend config into secondmate homes (#1219)

* Inherit config/backend into secondmate homes with deliberate-override preservation

Add backend to the shared inheritable config allowlist so launch, locked
bootstrap, and config-push converge a primary pin into secondmate homes as each
home local future-spawn default. Track last-inherited bytes in a private state
provenance marker so deliberate per-home overrides survive present and absent
primary convergence, keep --backend and FM_BACKEND stronger, and extend the
existing inheritance tests plus docs and skill claims.

* no-mistakes(review): Preserve equal unprovenanced backend overrides

* no-mistakes(review): Preserve symlink overrides and verify spawn precedence

* no-mistakes(review): Snapshot backend inheritance for consistent provenance

* no-mistakes(review): Simplify backend inheritance to primary-authoritative convergence

* no-mistakes(document): Document inherited backend override preservation

* fix: restore primary-authoritative backend inheritance after document regression

The document step reintroduced provenance and deliberate per-home override
semantics after review had simplified config/backend to plain primary-authoritative
allowlist membership. Restore the primary-always-wins path: present overwrites,
absent removes, no provenance marker, and docs/tests match that contract.

* no-mistakes(review): Add divergent backend precedence regression fixtures

* no-mistakes(document): Document backend inheritance contract

* fix(pi): remove Calm's upper version ceiling (#1226)

* fix(pi): remove Calm's exclusive Pi upper-version ceiling

tests/fm-calm-pi-extension.test.sh gated on a closed PI_COMPAT_VERSIONS
allowlist ("0.81.1 0.82.0") that refused any other installed Pi, and docs
described that range as "supported" rather than verified evidence. The
Calm CHANGELOG shows no API introduced at either version, so there is no
evidence for a real minimum; the presentation adapters already probe the
exact method they patch rather than checking a version.

Replace the allowlist with dated version evidence that never rejects a
newer Pi, and make each presentation adapter degrade independently with
a diagnostic if a future Pi removes its API, instead of the whole Calm
extension failing to load. Rewrite the feasibility doc's "Pi 0.81.1
through 0.82.0" phrasing to state it as verified evidence, not a
ceiling.

* no-mistakes(review): Probe missing Calm adapter exports safely

* no-mistakes(document): Document Calm's unbounded Pi compatibility

* fix(bin): allow session-local todo tools in the subagent guard (#1204)

* fix(guard): allow session-local todo tools in the primary

The delegation-shape guard denied TaskCreate and TaskUpdate because their
normalized names contain the `task` stem. Those tools write only the harness's
session-local todo list, which has no executor: it spawns no agent, allocates
no worktree, registers no schedule, and starts nothing that outlives the
session. That is not the unaccounted work the guard exists to stop, so the stem
match was a false positive, and the deny text told the primary to run
bin/fm-brief.sh and bin/fm-spawn.sh to create a todo entry.

Add a separately-reasoned PLAN_ONLY_TOOLS exact-name exclusion rather than
widening OBSERVE_ONLY_TOOLS, whose documented contract is tools that only
observe or stop existing work. Both lists stay exact-name so neither can widen
by substring.

Tests cover the two allowed names and six near-miss names that a substring or
shortened-stem widening would release; both mutations were watched red.

* no-mistakes(review): drop session-local todo tools from recommended deny list

* no-mistakes: apply CI fixes

* fix(session-lock): resolve Claude bg-spare ancestry to the outermost claude pid (#1206)

* fix(session-lock): resolve Claude bg-spare ancestry to the outermost claude pid

fm_harness_ancestry_pid() previously returned the first ancestor process
whose command matched a verified harness name. Claude Code's Stop hook
fires as a bg-spare worker several levels below the session's actual
lock-owning claude process (hook shell -> claude bg-spare ->
claude bg-pty-host -> claude -> claude(lock)), so the first match was
the bg-spare worker, not the lock owner. fm_session_lock_owned_by_self()
then never matched state/.lock, and the Claude Stop auto-arm silently
treated its own primary session as an unrelated live owner and never
armed the watcher.

The walk now keeps going past a claude-named match, looking for a still
more ancestral claude-named match, and stops the instant a non-match
follows an already-found match (bounding it to a contiguous run rather
than the literal ancestry top, so an unrelated claude-named process
further up the real process tree is never mistaken for part of this
session's own nested chain). Every other harness keeps the original
first-match-wins behavior, since e.g. Pi's shared signed-wrapper
ancestry actually holds the session at the inner engine pid, not an
outer wrapper pid. Hop limit raised from 8 to 16 to cover the deeper
bg-spare chain.

* no-mistakes(review): Add nested-claude-ancestry regression test; fix nudge doc depth claim

* no-mistakes: apply CI fixes

* fix: conferma l'avvio del watcher su Windows/MSYS (#1212)

* fix: confirm watcher startup on MSYS

* no-mistakes(review): gate MSYS arm ready timeout, cache uname, harden locale test

* no-mistakes(review): validate OpenCode ready timeout, make uname cache internal

* fix(spawn): forward CLAUDE_CONFIG_DIR to claude crewmates (#1195)

* fix(spawn): forward firstmate's CLAUDE_CONFIG_DIR to claude crewmates

Crewmate panes are created by a long-lived tmux/herdr daemon that does not
inherit firstmate's current environment. When firstmate runs under a non-default
CLAUDE_CONFIG_DIR (for example a work-vs-personal subscription split), a bare
`claude` in the crewmate pane fell back to the default ~/.claude store and
launched unauthenticated, blocking the crewmate before it could do any work.

fm-spawn now prefixes the claude launch with firstmate's own resolved
CLAUDE_CONFIG_DIR when set, so the crewmate uses the same credential/config
store firstmate is authenticated with. An unset value is the single-store
default and adds no prefix; non-claude harnesses are unaffected.

Adds three tests in fm-spawn-dispatch-profile.test.sh (forwarded-when-set,
omitted-when-unset, non-claude-ignored) and pins CLAUDE_CONFIG_DIR in the test
helper so launch assertions no longer depend on the developer's environment.

* no-mistakes: apply CI fixes

* fix: preserve dispatch identity across authentication checks (#1233)

* fix: preserve dispatch harness identity

* no-mistakes(review): Fix Grok counterfactual tuple validation

* no-mistakes(document): Scope dispatch authentication to selected tuple

* fix: restore dispatch instruction budget

* no-mistakes(review): Scope dispatch authentication after candidate selection

* fix(bin): normalize relative durable paths (#1256)

* fix(bin): handle dash-leading harness process names (#2)

* fix: handle dash-leading harness process names

* no-mistakes(review): Make dash-leading harness regression hermetic

* fix: preserve secondmate reply routes across relative homes

Resolve relative home, data, and state inputs before durable charter generation, and fail when caller-relative directories cannot be resolved.

Use absolute paths at the related spawn, AFK daemon, and X-mode cross-process handoffs so later processes cannot reinterpret them from another working directory.

* no-mistakes(review): Preserve absolute overrides and normalize relative durable paths

* no-mistakes(review): Normalize relative home before deriving durable paths

* no-mistakes(document): Document relative durable-path normalization

* no-mistakes(review): Captain: Ignore inherited CDPATH during relative path normalization

* no-mistakes(lint): Fix empty CDPATH assignments for ShellCheck

* refactor(skills): make Bearings chat-only by default (#1136)

* Add internal status skill

* no-mistakes(document): register /status skill in documentation-audiences inventory

* no-mistakes(lint): replace grep|wc -l with grep -c in status skill test

* test: silence literal status skill patterns

* Refactor bearings default to chat-only

---------

Co-authored-by: Kun Chen <3233006+kunchenguid@users.noreply.github.com>

* Clarify follow-up routing during validation (#1277)

* fix: honor concrete approval for project operations (#1272)

* docs: add captain-approved project operation exception to hard rule 1

Firstmate stays read-only over projects by default, but when the captain
clearly approves a concrete project operation and scope in the moment,
firstmate may perform exactly that approved operation with its own tools.
The approval is never inferred, broadened, or standing, and it does not
relax the existing force, discard, unlanded-work, or merge-authority
boundaries.

* no-mistakes(review): Clarify captain-approved project operation boundaries

* no-mistakes(document): Clarify captain-approved project operation scope

* docs: cover directories and preserve the operation-or-scope alternative

Widen the captain-approved project operation exception in AGENTS.md to
files or directories, and restore the explicit operation-or-scope
alternative that a prior pipeline auto-fix had collapsed into "and".

Rework project-management SKILL.md's Remove section, which previously
told firstmate to refuse project removal until a guarded helper existed;
that helper was never built, so the text directly contradicted the new
instruction-only exception. It now points at the exception plus the
existing removal preflight it still requires unchanged.

Update the one instruction-owners test assertion that hard-coded the
sentence removed above, so the suite tracks current, not obsolete, text.

* docs: add captain-approved project operation exception to hard rule 1

Firstmate stays read-only over projects by default, but when the captain
clearly approves a concrete project operation and scope in the moment,
firstmate may perform exactly that approved operation with its own tools.
The approval is never inferred, broadened, or standing, and it does not
relax the existing force, discard, unlanded-work, or merge-authority
boundaries.

* no-mistakes(review): Clarify captain-approved project operation boundaries

* no-mistakes(document): Clarify captain-approved project operation scope

* docs: cover directories and preserve the operation-or-scope alternative

Widen the captain-approved project operation exception in AGENTS.md to
files or directories, and restore the explicit operation-or-scope
alternative that a prior pipeline auto-fix had collapsed into "and".

Rework project-management SKILL.md's Remove section, which previously
told firstmate to refuse project removal until a guarded helper existed;
that helper was never built, so the text directly contradicted the new
instruction-only exception. It now points at the exception plus the
existing removal preflight it still requires unchanged.

Update the one instruction-owners test assertion that hard-coded the
sentence removed above, so the suite tracks current, not obsolete, text.

* no-mistakes(review): Align project removal preflight with approved exception

* no-mistakes(document): Align project removal documentation with approved exception

* fix: restore removal test byte-for-byte and preserve the default sentence

tests/fm-instruction-owners.test.sh had been changed to assert different
text; restore it byte-for-byte to origin/main. project-management SKILL.md's
Remove section now keeps the exact default "Never issue a raw removal
command from Firstmate." sentence that test still asserts, immediately
followed by the already-approved captain-operation-or-scope exception, so
the default and the exception both stay explicit and consistent.

* no-mistakes(document): Align project-write boundary documentation

* fix(skills): route new project intake through secondmate scopes (#1275)

* Route project intake through secondmate scopes

* no-mistakes(test): Guard all main-home project registry mutations

* no-mistakes(document): Consolidate secondmate routing documentation

* no-mistakes: apply CI fixes

* Restore new-project routing scope

* no-mistakes(document): Clarify secondmate routing for new-project intake

* no-mistakes: apply CI fixes

* fix: scope validation corrections by accepted behavior (#1281)

* fix: scope validation corrections by accepted behavior

* no-mistakes(review): Classify stale delivery evidence as an autonomous correction

* test: replace source assertions with behavioral coverage (#1282)

* test: remove source-content assertions

* no-mistakes(review): Replace source assertions with runtime behavior coverage

* no-mistakes(review): Isolate Kimi task temp runtime coverage

* no-mistakes(document): Refresh test cleanup documentation

* no-mistakes: apply CI fixes

* fix(watch): escalate busy workers with no completed turn (#1286)

* fix(watch): bound how long a busy pane may run with no completed turn

A busy pane (backend busy state or the harness's rendered footer) was
unconditional, unbounded proof of liveness in every escalation path, so a
hung foreground tool call behind a busy signature could run for hours
undetected (2026-07 hibit-agent-focus-nonsteal-r1 incident: a catastrophic-
backtracking regex hung one bash call for 25h behind an unchanging
"Working..." footer).

FM_BUSY_TURN_MAX_SECS (default 3600s) now bounds how long a busy pane may
run with no completed turn (state/<id>.turn-ended, or its spawn record
before any turn has completed). Past the bound, busy_turn_over_age routes
the pane through the existing wedge_timer_check, reusing the identical
stale reason, escalation counter, and demand-deep-inspection marker for
human inspection only - never an automatic interrupt, signal, or restart
of the worker or its tool process. A completed turn resets the age.

Reproduced end-to-end against the real installed Pi TUI: a foreground
`sleep 999999` bash call with no timeout renders the actual busy footer,
and two captures ~15s apart show the elapsed counter changing the pane
hash while the same turn stays unfinished. Running the pre-fix watcher
against the real captures showed it never starts a wedge timer no matter
how long the pane stays busy; the fixed watcher starts and escalates the
timer through the same mechanism, while the real hung process remained
untouched and alive throughout.

* no-mistakes(review): fix: parse enriched AFK stale reasons

* no-mistakes(review): fix: preserve enriched wedges during AFK supervision

* no-mistakes(review): fix: route all enriched AFK wedges

* no-mistakes(document): Clarify busy-turn age supervision documentation

* fix(gitignore): ignore config/ as a directory, not by exact filename (#1261)

A name-by-name list of config/ entries silently stops ignoring any new or
home-local file placed there, which makes the working tree read as dirty and
blocks guarded sync paths that refuse to touch a dirty home. AGENTS.md
already documents config/ as captain-private and gitignored as a category;
this makes .gitignore match that contract.

* fix(tests): replace source-content .gitignore assertion with behavioral coverage (#1304)

The second assertion in fm-gitignore-config.test.sh (added by #1261) greps
.gitignore for a specific spelling of the config/ ignore pattern. It fails
on a semantically equivalent pattern like config/** and does not prove Git
actually ignores anything, per the completed source-content-test audit.

Replace it with a real git check-ignore control test on a generated
unrelated path, and strengthen the existing directory-coverage test with
generated unpredictable direct and nested config/ paths.

* feat: bound and consolidate startup memory during stow (#1303)

* Add bounded startup memory curation

* no-mistakes(review): Record reproducible stow verification evidence

* no-mistakes(review): Validate inherited secondmate stow evidence

* no-mistakes(document): Do…
4mb1t10n pushed a commit to 4mb1t10n/firstmate that referenced this pull request Aug 11, 2026
* fix: escalate ask-user contract expansion

* no-mistakes(document): Point project management to authority owner
rega10 added a commit to rega10/firstmate that referenced this pull request Aug 13, 2026
* fix(herdr): group projected children beneath owning parents (#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: keep local no-mistakes tests intent-targeted (#823)

* 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: add canonical timed test runner (#825)

* 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: surface main inventory gaps in Bearings (#830)

* 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 bounded concurrent test isolation proof (#832)

* 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: guard against missed secondmate reports (#834)

* 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: require pinned real-Herdr CI coverage (#838)

* 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

* feat: shard portable tests and add bounded local parallelism (#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

* fix: block primary-session delegation outside the fleet (#854)

* 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

* fix: install tasks-axi in portable CI shards (#866)

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(bin): make dispatch profiles quota aware (#867)

* 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

* Add built-in ahoy recap skill (#873)

* fix: preserve trustworthy Bearings data in partial snapshots (#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

* feat(pi): add session-local calm mode (#884)

* 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): prevent redundant watcher re-arms (#885)

* 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 (#895)

* 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 (#898)

* fix: execute every PR body compliance event

* no-mistakes(document): Document independent PR compliance events

* fix: exclude operational injections from ahoy boundaries (#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

* fix: canonically classify operational inputs across harnesses (#909)

* fix: type canonical operational inputs

* no-mistakes(document): Correct canonical operational-input documentation ownership

* fix: avoid generic secondmate start acknowledgements (#926)

* fix: avoid generic secondmate acknowledgements

* no-mistakes(document): Document sparse secondmate acknowledgement behavior

* no-mistakes: apply CI fixes

* fix(pi): make calm mode persistent and gapless (#927)

* fix(pi): preserve calm presentation across sessions

* no-mistakes(review): Fix Calm home fallback persistence

* no-mistakes(document): Clarify Calm gapless and export contracts

* no-mistakes: apply CI fixes

* fix(watch): retire merged PR polls after durable notification (#932)

* fix: retire merged PR polls after notification

* no-mistakes(review): Decouple PR retirement recovery from template updates

* no-mistakes(review): Recover pending PR retirements before poll migration

* no-mistakes(document): Document merged PR poll retirement contracts

* fix: refine scout intake and parallel dispatch (#934)

* Clarify intake evidence and overlap handling

* no-mistakes(review): Align scout guard with intake classification

* no-mistakes(document): Clarify scout documentation and intake ownership

* fix(pi): prevent duplicate assistant replies in Calm (#936)

* fix(pi): preserve operational follow-up semantics in Calm

* no-mistakes(document): Correct Calm operational-row visibility documentation

* perf(bin): shrink the ShellCheck source graph (#939)

* perf(lint): shrink shell source graph

* no-mistakes(review): Ensure lint workers terminate fully on cancellation

* no-mistakes(document): Repair stale lint documentation ownership

* fix(pi): remove Calm hidden-block gaps (#942)

* fix(pi): remove Calm hidden-block gaps

* no-mistakes(review): Validate Calm geometry against current viewport

* no-mistakes(review): Synchronize Calm geometry checks with reload completion

* fix: enforce contract boundaries for ask-user findings (#945)

* fix: escalate ask-user contract expansion

* no-mistakes(document): Point project management to authority owner

* docs: prefer direct operational paths (#946)

* fix(pi): hide operational user rows in Calm mode (#948)

* fix(pi): hide Calm operational user rows

* no-mistakes(review): Narrow Calm operational input suppression

* no-mistakes(review): Avoid Calm replay classifier subprocesses

* no-mistakes(document): docs: point Pi verification to Calm owner

* fix: relaunch missing second mates at session start (#950)

* fix(session-start): relaunch missing second mates

* fix(test): detect completed parallel workers

* no-mistakes(review): Isolate session-start recovery test cleanup

* no-mistakes(review): Complete backend-safe secondmate session recovery

* no-mistakes(review): Resolve Zellij task ownership before recovery

* no-mistakes(review): Recover relocated Zellij ghost tabs safely

* no-mistakes(review): Restore conservative Zellij recovery boundary

* no-mistakes(review): Reject malformed tmux recovery targets

* no-mistakes(document): Align secondmate recovery documentation

* no-mistakes: apply CI fixes

* fix(herdr): reclaim resumed task projections after restart (#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

* Teach Ahoy to surface open decisions (#968)

* Require shipshape routine acknowledgement (#969)

* docs: separate current guidance from verification evidence (#994)

* docs: separate current guides from verification

* no-mistakes(review): Restore Herdr 0.7.5 restart-reclaim verification evidence

* fix: preserve Claude watcher continuity across Stop hooks (#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

* fix(herdr): clean stale projections at session start (#996)

* 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

* fix: recover Claude supervision without watcher-status gate (#1001)

* fix: recover Claude supervision at session start

* fix: remove Claude watcher-status command gate

* no-mistakes(document): docs: remove stale continuity gate references

* fix: make quota-aware profile selection agent-owned (#1018)

* Replace quota dispatch selector instructions

* no-mistakes(review): Align bootstrap docs with agent-owned dispatch selection

* fix(bin): remove vestigial dispatch selector (#1026)

* 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

* docs(agents): drop superseded interim quota-window rule (#1039)

quota-axi 0.1.13 emits schemaVersion 2 with a quotaSemantics object per
provider, so the successor named in the interim rule has landed and the
rule's own removal condition is satisfied.

Keep the ownership clause so quota-axi remains the single owner of how
model or product windows relate to bounding account windows, and drop
the interim weakest-headroom instruction. The unknown-semantics case is
already covered by the existing requirement to stop and report a
candidate whose applicable quota data or interpretation cannot be
established.

Drop the matching assertion phrase from
tests/fm-instruction-owners.test.sh; the retained ownership phrase still
asserts.

* fix(tmux): scope busy detection and recognize current Claude turns (#1049)

* fix(tmux): scope Claude busy detection by harness

* no-mistakes(review): Separate verified and fallback busy signatures

* no-mistakes(test): Scope busy signatures to supplied harnesses

* no-mistakes(document): Document harness-scoped busy detection

* feat: add verified Kimi crewmate adapter (#1047)

* Add verified Kimi crewmate harness adapter

* no-mistakes(review): Scope Kimi moon detection to spinner lines

* no-mistakes(review): Match only complete Kimi spinner rows

* no-mistakes(review): Resolve Kimi binary portably before pane creation

* no-mistakes(document): Align Kimi adapter documentation

* no-mistakes(lint): Suppress false-positive ShellCheck warning for sourced watcher override

* Fix Kimi busy spinner detection

* no-mistakes(review): Recognize Kimi session-lock ancestry and holders

* no-mistakes(review): Scope pending-reply Kimi busy detection by harness

* no-mistakes(document): Correct Kimi spinner capture documentation

* no-mistakes(document): Clarify optional Kimi spinner whitespace

* no-mistakes(lint): Silence intentional pending-reply test stub warnings

* test: align rebased Kimi busy fixtures

* no-mistakes: apply CI fixes

* Reconcile Kimi busy detection after per-harness scoping

* no-mistakes(review): Clarify observed Kimi spinner whitespace contract

* no-mistakes(document): Clarify Kimi harness documentation

* fix: harden Kimi submission and spinner matching (#1058)

* fix kimi pointer submission and spinner conformance

* no-mistakes(review): Preserve Kimi submit target ownership guard

* feat(bin): add guarded Kimi turn-end wake (#1059)

* Add guarded Kimi turn-end hook

* no-mistakes(review): Require jq before installing Kimi turn-end hook

* no-mistakes(review): Expose jq inside isolated Kimi test fixtures

* no-mistakes(review): Preserve Kimi config boundaries during hook removal

* no-mistakes(review): Document Kimi removal newline safeguard

* no-mistakes(document): Document Kimi shared-home preservation

* fix(tmux): classify bordered composers across all rows (#1066)

* Fix structural tmux composer reading

* Verify Calm compatibility with Pi 0.82

* no-mistakes(review): Harden structural composer classification boundaries

* no-mistakes(review): Refresh composer and Kimi regression fixtures

* no-mistakes(review): Fail closed on unbounded composer edges

* no-mistakes(review): Enforce aligned composer geometry safely

* no-mistakes(review): Make composer ambiguity locale-safe

* no-mistakes(review): Preserve ambiguity through composer submission

* no-mistakes(review): Carry composer proof through retries

* no-mistakes(document): Document structural tmux composer delivery guarantees

* no-mistakes: apply CI fixes

* feat(bin): add verified pi-signed runtime adapter (#1145)

* feat: add verified pi-signed adapter

* no-mistakes(review): Correct pi-signed maintainer verification date

* no-mistakes(review): Correct remaining pi-signed verification dates

* no-mistakes(review): Preserve authoritative pi-signed runtime identity

* no-mistakes(document): Document pi-signed shared adapter semantics

* no-mistakes: apply CI fixes

* fix(pi): rearm watcher across session transitions (#1166)

* fix(pi): rearm watcher across same-process session transitions

Pi emits session_shutdown for ordinary /new, /resume, and /fork replacement
as well as terminal quit. The primary watcher extension latched a module-level
stopping flag on every shutdown, so a replacement session in the same process
could not arm monitoring until Pi restarted.

Own arm authority per session generation so only the active live generation
may start, stop, or rearm the child. Replacement sessions can arm again without
restarting Pi, stale prior-generation callbacks cannot mutate the active cycle,
and real quit still blocks late rearm.

* no-mistakes(review): Preserve Pi generation isolation and exit cleanup

* no-mistakes(document): Correct Pi watcher transition documentation

* feat: route crew dispatch using quota-window pace (#1172)

* Consume quota-axi pace signals in dispatch profile array selection.

Add quota-array-dispatch as the single owner of the pace-aware candidate
choice, keep AGENTS.md to the intake boundary and load trigger, and cover
the acceptance cases with sanitized schemaVersion 3 fixtures.

* no-mistakes(review): Stop and report genuine quota dispatch ties

* no-mistakes(document): Document quota pace freshness and uncertainty

* fix: adapt Grok Stop continuation and harden endpoint cleanup (#1171)

* fix(grok): adapt Stop continuation to runtime capability

* no-mistakes(review): Reject ambiguous Grok Stop payloads

* no-mistakes(review): Reject duplicate Grok fields and accept spaced tmux sessions

* no-mistakes(review): Enforce exact tmux cleanup selectors

* no-mistakes(test): Fix historical tmux fixture and validate Grok Stop

* no-mistakes: apply CI fixes

* fix: restore stock macOS Bash 3.2 brief scaffolding (#1093)

* fix(brief): make DOD scaffolding parse-safe on stock macOS Bash 3.2

fm-brief.sh built each Definition-of-done block and the not-enabled
Herdr declaration with `VAR=$(cat <<EOF ... EOF)`. On Bash 3.2 (macOS
/bin/bash) the lexer scans for the command substitution's closing `)`
textually and tracks quote state through the heredoc body, so a single
apostrophe, unbalanced quote, or unbalanced paren in that prose breaks
parsing of the whole script. Every ship-brief scaffold (no-mistakes,
direct-PR, local-only) failed with `unexpected EOF while looking for
matching )`. Bash 4+ parses it fine, so the breakage stayed invisible
everywhere except stock macOS.

Replace all four command-substitution heredocs with
`IFS= read -r -d '' VAR <<EOF || true`. That removes the `$(...)`
wrapper and the entire defect class regardless of future prose, and
preserves the variable expansion the direct-PR and local-only bodies
need. `read` keeps the heredoc's trailing newline that `$(...)` used to
strip, so trim one newline to keep every generated brief byte-identical
to prior output.

Guard the structure, not one historical phrase: a new test rejects any
heredoc nested in a command substitution anywhere in fm-brief.sh, where
the old assertion pinned a single apostrophe phrase and so missed the
reintroduction. Extend the stock-macOS Bash CI job from parsing one
script to the whole maintained shell surface (bin/*.sh,
bin/backends/*.sh, tests/*.sh), matching bin/fm-lint.sh's canonical file
set so parse scope and lint scope cannot drift apart.

* no-mistakes(review): Captain: harden Bash structure and inventory guards

* no-mistakes(document): Align stock macOS Bash contributor checks

* no-mistakes(lint): Suppress deliberate SC2016 literal fixture warnings

* test: stabilize tmux teardown conformance baseline (#1209)

* fix(test): pin teardown tmux baseline to historical kill selectors

merge-base HEAD main collapses to HEAD after the exact-selector change
lands on the default branch, so the old teardown fixture was accidentally
exercising current exact targets. Resolve a content-historical permissive
tmux adapter from first-parent history and force that post-squash topology
inside the conformance case so main and feature branches keep the same
old-vs-new contract.

* no-mistakes(lint): Suppress intentional literal-pattern ShellCheck warnings

* docs: slim quota-array-dispatch to the pace selection core (#1197)

Cut the runtime skill to the compact pace-aware selection procedure plus
minimum owner pointers. Keep every distinct decision rule and move expanded
acceptance scenarios to deterministic fixture ownership assertions.

Size: 170/1374/10187 -> 63/544/4068 (about 63%/60%/60% reduction).

* feat(bin): inherit backend config into secondmate homes (#1219)

* Inherit config/backend into secondmate homes with deliberate-override preservation

Add backend to the shared inheritable config allowlist so launch, locked
bootstrap, and config-push converge a primary pin into secondmate homes as each
home local future-spawn default. Track last-inherited bytes in a private state
provenance marker so deliberate per-home overrides survive present and absent
primary convergence, keep --backend and FM_BACKEND stronger, and extend the
existing inheritance tests plus docs and skill claims.

* no-mistakes(review): Preserve equal unprovenanced backend overrides

* no-mistakes(review): Preserve symlink overrides and verify spawn precedence

* no-mistakes(review): Snapshot backend inheritance for consistent provenance

* no-mistakes(review): Simplify backend inheritance to primary-authoritative convergence

* no-mistakes(document): Document inherited backend override preservation

* fix: restore primary-authoritative backend inheritance after document regression

The document step reintroduced provenance and deliberate per-home override
semantics after review had simplified config/backend to plain primary-authoritative
allowlist membership. Restore the primary-always-wins path: present overwrites,
absent removes, no provenance marker, and docs/tests match that contract.

* no-mistakes(review): Add divergent backend precedence regression fixtures

* no-mistakes(document): Document backend inheritance contract

* fix(pi): remove Calm's upper version ceiling (#1226)

* fix(pi): remove Calm's exclusive Pi upper-version ceiling

tests/fm-calm-pi-extension.test.sh gated on a closed PI_COMPAT_VERSIONS
allowlist ("0.81.1 0.82.0") that refused any other installed Pi, and docs
described that range as "supported" rather than verified evidence. The
Calm CHANGELOG shows no API introduced at either version, so there is no
evidence for a real minimum; the presentation adapters already probe the
exact method they patch rather than checking a version.

Replace the allowlist with dated version evidence that never rejects a
newer Pi, and make each presentation adapter degrade independently with
a diagnostic if a future Pi removes its API, instead of the whole Calm
extension failing to load. Rewrite the feasibility doc's "Pi 0.81.1
through 0.82.0" phrasing to state it as verified evidence, not a
ceiling.

* no-mistakes(review): Probe missing Calm adapter exports safely

* no-mistakes(document): Document Calm's unbounded Pi compatibility

* fix(bin): allow session-local todo tools in the subagent guard (#1204)

* fix(guard): allow session-local todo tools in the primary

The delegation-shape guard denied TaskCreate and TaskUpdate because their
normalized names contain the `task` stem. Those tools write only the harness's
session-local todo list, which has no executor: it spawns no agent, allocates
no worktree, registers no schedule, and starts nothing that outlives the
session. That is not the unaccounted work the guard exists to stop, so the stem
match was a false positive, and the deny text told the primary to run
bin/fm-brief.sh and bin/fm-spawn.sh to create a todo entry.

Add a separately-reasoned PLAN_ONLY_TOOLS exact-name exclusion rather than
widening OBSERVE_ONLY_TOOLS, whose documented contract is tools that only
observe or stop existing work. Both lists stay exact-name so neither can widen
by substring.

Tests cover the two allowed names and six near-miss names that a substring or
shortened-stem widening would release; both mutations were watched red.

* no-mistakes(review): drop session-local todo tools from recommended deny list

* no-mistakes: apply CI fixes

* fix(session-lock): resolve Claude bg-spare ancestry to the outermost claude pid (#1206)

* fix(session-lock): resolve Claude bg-spare ancestry to the outermost claude pid

fm_harness_ancestry_pid() previously returned the first ancestor process
whose command matched a verified harness name. Claude Code's Stop hook
fires as a bg-spare worker several levels below the session's actual
lock-owning claude process (hook shell -> claude bg-spare ->
claude bg-pty-host -> claude -> claude(lock)), so the first match was
the bg-spare worker, not the lock owner. fm_session_lock_owned_by_self()
then never matched state/.lock, and the Claude Stop auto-arm silently
treated its own primary session as an unrelated live owner and never
armed the watcher.

The walk now keeps going past a claude-named match, looking for a still
more ancestral claude-named match, and stops the instant a non-match
follows an already-found match (bounding it to a contiguous run rather
than the literal ancestry top, so an unrelated claude-named process
further up the real process tree is never mistaken for part of this
session's own nested chain). Every other harness keeps the original
first-match-wins behavior, since e.g. Pi's shared signed-wrapper
ancestry actually holds the session at the inner engine pid, not an
outer wrapper pid. Hop limit raised from 8 to 16 to cover the deeper
bg-spare chain.

* no-mistakes(review): Add nested-claude-ancestry regression test; fix nudge doc depth claim

* no-mistakes: apply CI fixes

* fix: conferma l'avvio del watcher su Windows/MSYS (#1212)

* fix: confirm watcher startup on MSYS

* no-mistakes(review): gate MSYS arm ready timeout, cache uname, harden locale test

* no-mistakes(review): validate OpenCode ready timeout, make uname cache internal

* fix(spawn): forward CLAUDE_CONFIG_DIR to claude crewmates (#1195)

* fix(spawn): forward firstmate's CLAUDE_CONFIG_DIR to claude crewmates

Crewmate panes are created by a long-lived tmux/herdr daemon that does not
inherit firstmate's current environment. When firstmate runs under a non-default
CLAUDE_CONFIG_DIR (for example a work-vs-personal subscription split), a bare
`claude` in the crewmate pane fell back to the default ~/.claude store and
launched unauthenticated, blocking the crewmate before it could do any work.

fm-spawn now prefixes the claude launch with firstmate's own resolved
CLAUDE_CONFIG_DIR when set, so the crewmate uses the same credential/config
store firstmate is authenticated with. An unset value is the single-store
default and adds no prefix; non-claude harnesses are unaffected.

Adds three tests in fm-spawn-dispatch-profile.test.sh (forwarded-when-set,
omitted-when-unset, non-claude-ignored) and pins CLAUDE_CONFIG_DIR in the test
helper so launch assertions no longer depend on the developer's environment.

* no-mistakes: apply CI fixes

* fix: preserve dispatch identity across authentication checks (#1233)

* fix: preserve dispatch harness identity

* no-mistakes(review): Fix Grok counterfactual tuple validation

* no-mistakes(document): Scope dispatch authentication to selected tuple

* fix: restore dispatch instruction budget

* no-mistakes(review): Scope dispatch authentication after candidate selection

* fix(bin): normalize relative durable paths (#1256)

* fix(bin): handle dash-leading harness process names (#2)

* fix: handle dash-leading harness process names

* no-mistakes(review): Make dash-leading harness regression hermetic

* fix: preserve secondmate reply routes across relative homes

Resolve relative home, data, and state inputs before durable charter generation, and fail when caller-relative directories cannot be resolved.

Use absolute paths at the related spawn, AFK daemon, and X-mode cross-process handoffs so later processes cannot reinterpret them from another working directory.

* no-mistakes(review): Preserve absolute overrides and normalize relative durable paths

* no-mistakes(review): Normalize relative home before deriving durable paths

* no-mistakes(document): Document relative durable-path normalization

* no-mistakes(review): Captain: Ignore inherited CDPATH during relative path normalization

* no-mistakes(lint): Fix empty CDPATH assignments for ShellCheck

* refactor(skills): make Bearings chat-only by default (#1136)

* Add internal status skill

* no-mistakes(document): register /status skill in documentation-audiences inventory

* no-mistakes(lint): replace grep|wc -l with grep -c in status skill test

* test: silence literal status skill patterns

* Refactor bearings default to chat-only

---------

Co-authored-by: Kun Chen <3233006+kunchenguid@users.noreply.github.com>

* Clarify follow-up routing during validation (#1277)

* fix: honor concrete approval for project operations (#1272)

* docs: add captain-approved project operation exception to hard rule 1

Firstmate stays read-only over projects by default, but when the captain
clearly approves a concrete project operation and scope in the moment,
firstmate may perform exactly that approved operation with its own tools.
The approval is never inferred, broadened, or standing, and it does not
relax the existing force, discard, unlanded-work, or merge-authority
boundaries.

* no-mistakes(review): Clarify captain-approved project operation boundaries

* no-mistakes(document): Clarify captain-approved project operation scope

* docs: cover directories and preserve the operation-or-scope alternative

Widen the captain-approved project operation exception in AGENTS.md to
files or directories, and restore the explicit operation-or-scope
alternative that a prior pipeline auto-fix had collapsed into "and".

Rework project-management SKILL.md's Remove section, which previously
told firstmate to refuse project removal until a guarded helper existed;
that helper was never built, so the text directly contradicted the new
instruction-only exception. It now points at the exception plus the
existing removal preflight it still requires unchanged.

Update the one instruction-owners test assertion that hard-coded the
sentence removed above, so the suite tracks current, not obsolete, text.

* docs: add captain-approved project operation exception to hard rule 1

Firstmate stays read-only over projects by default, but when the captain
clearly approves a concrete project operation and scope in the moment,
firstmate may perform exactly that approved operation with its own tools.
The approval is never inferred, broadened, or standing, and it does not
relax the existing force, discard, unlanded-work, or merge-authority
boundaries.

* no-mistakes(review): Clarify captain-approved project operation boundaries

* no-mistakes(document): Clarify captain-approved project operation scope

* docs: cover directories and preserve the operation-or-scope alternative

Widen the captain-approved project operation exception in AGENTS.md to
files or directories, and restore the explicit operation-or-scope
alternative that a prior pipeline auto-fix had collapsed into "and".

Rework project-management SKILL.md's Remove section, which previously
told firstmate to refuse project removal until a guarded helper existed;
that helper was never built, so the text directly contradicted the new
instruction-only exception. It now points at the exception plus the
existing removal preflight it still requires unchanged.

Update the one instruction-owners test assertion that hard-coded the
sentence removed above, so the suite tracks current, not obsolete, text.

* no-mistakes(review): Align project removal preflight with approved exception

* no-mistakes(document): Align project removal documentation with approved exception

* fix: restore removal test byte-for-byte and preserve the default sentence

tests/fm-instruction-owners.test.sh had been changed to assert different
text; restore it byte-for-byte to origin/main. project-management SKILL.md's
Remove section now keeps the exact default "Never issue a raw removal
command from Firstmate." sentence that test still asserts, immediately
followed by the already-approved captain-operation-or-scope exception, so
the default and the exception both stay explicit and consistent.

* no-mistakes(document): Align project-write boundary documentation

* fix(skills): route new project intake through secondmate scopes (#1275)

* Route project intake through secondmate scopes

* no-mistakes(test): Guard all main-home project registry mutations

* no-mistakes(document): Consolidate secondmate routing documentation

* no-mistakes: apply CI fixes

* Restore new-project routing scope

* no-mistakes(document): Clarify secondmate routing for new-project intake

* no-mistakes: apply CI fixes

* fix: scope validation corrections by accepted behavior (#1281)

* fix: scope validation corrections by accepted behavior

* no-mistakes(review): Classify stale delivery evidence as an autonomous correction

* test: replace source assertions with behavioral coverage (#1282)

* test: remove source-content assertions

* no-mistakes(review): Replace source assertions with runtime behavior coverage

* no-mistakes(review): Isolate Kimi task temp runtime coverage

* no-mistakes(document): Refresh test cleanup documentation

* no-mistakes: apply CI fixes

* fix(watch): escalate busy workers with no completed turn (#1286)

* fix(watch): bound how long a busy pane may run with no completed turn

A busy pane (backend busy state or the harness's rendered footer) was
unconditional, unbounded proof of liveness in every escalation path, so a
hung foreground tool call behind a busy signature could run for hours
undetected (2026-07 hibit-agent-focus-nonsteal-r1 incident: a catastrophic-
backtracking regex hung one bash call for 25h behind an unchanging
"Working..." footer).

FM_BUSY_TURN_MAX_SECS (default 3600s) now bounds how long a busy pane may
run with no completed turn (state/<id>.turn-ended, or its spawn record
before any turn has completed). Past the bound, busy_turn_over_age routes
the pane through the existing wedge_timer_check, reusing the identical
stale reason, escalation counter, and demand-deep-inspection marker for
human inspection only - never an automatic interrupt, signal, or restart
of the worker or its tool process. A completed turn resets the age.

Reproduced end-to-end against the real installed Pi TUI: a foreground
`sleep 999999` bash call with no timeout renders the actual busy footer,
and two captures ~15s apart show the elapsed counter changing the pane
hash while the same turn stays unfinished. Running the pre-fix watcher
against the real captures showed it never starts a wedge timer no matter
how long the pane stays busy; the fixed watcher starts and escalates the
timer through the same mechanism, while the real hung process remained
untouched and alive throughout.

* no-mistakes(review): fix: parse enriched AFK stale reasons

* no-mistakes(review): fix: preserve enriched wedges during AFK supervision

* no-mistakes(review): fix: route all enriched AFK wedges

* no-mistakes(document): Clarify busy-turn age supervision documentation

* fix(gitignore): ignore config/ as a directory, not by exact filename (#1261)

A name-by-name list of config/ entries silently stops ignoring any new or
home-local file placed there, which makes the working tree read as dirty and
blocks guarded sync paths that refuse to touch a dirty home. AGENTS.md
already documents config/ as captain-private and gitignored as a category;
this makes .gitignore match that contract.

* fix(tests): replace source-content .gitignore assertion with behavioral coverage (#1304)

The second assertion in fm-gitignore-config.test.sh (added by #1261) greps
.gitignore for a specific spelling of the config/ ignore pattern. It fails
on a semantically equivalent pattern like config/** and does not prove Git
actually ignores anything, per the completed source-content-test audit.

Replace it with a real git check-ignore control test on a generated
unrelated path, and strengthen the existing directory-coverage test with
generated unpredictable direct and nested config/ paths.

* feat: bound and consolidate startup memory during stow (#1303)

* Add bounded startup memory curation

* no-mistakes(review): Record reproducible stow verification evidence

* no-mistakes(review): Validate inherited secondmate stow evidence

* no-mistakes(document): Document editable startup-memory budget propagation

* fix(herdr): place workers in the launching workspace (#1328)

* fix(herdr): place workers in the launching agent's exact workspace

Herdr enforces no workspace-label uniqueness, and spawn resolved its
container by taking the FIRST workspace whose label matched the home
label. With two workspaces both labeled "firstmate", a worker launched
from the second one was created in the first, so it appeared in a
different space than the Firstmate the captain was watching.

Reproduced end to end on Herdr 0.7.5 protocol 17 by running the real
bin/fm-spawn.sh inside a launcher pane in the second "firstmate"
workspace: the worker landed in w1 while its launcher was in w2, with an
unrelated third workspace focused throughout, which also rules out any
dependence on the focused workspace.

Placement now binds to the launching process's own Herdr identity. Herdr
injects HERDR_PANE_ID, HERDR_SESSION, and HERDR_SOCKET_PATH into every
process it manages a pane for, and fm_backend_herdr_launcher_identity
resolves that pane's current owning tab and workspace live from Herdr,
cross-checking the pane against its tab and confirming the workspace
exists exactly once in the session. The injected HERDR_TAB_ID and
HERDR_WORKSPACE_ID are creation-time snapshots and are deliberately not
read as current identity. Labels are no longer placement authority.

A claimed parent identity that is unreadable, contradictory, stale, or
from another named session or Herdr server stops the spawn before any
worker endpoint exists, rather than degrading to a label search. A
launcher with no Herdr ancestry has no workspace to inherit and keeps
the per-home labeled container, which must now resolve to exactly one
workspace; two same-labeled candidates refuse instead of adopting
either. A --secondmate launch keeps standing up that home's own
workspace by design.

With presentation spaces enabled, the projected child is created and
bound under that same exact parent and anchors its ordering on it, so a
duplicated home label no longer makes the layout ambiguous. Projection,
focus restoration, restart binding, and quarantine rules are unchanged,
and children are never collapsed into the parent. tmux, Zellij, cmux,
Orca, and the away-mode daemon terminal were each inspected and are not
affected: none resolves a container by searching mutable labels.

tests/fm-backend-herdr-launcher-workspace-e2e.test.sh drives the real
spawn and teardown against an isolated Herdr lab, with its headline case
running fm-spawn.sh inside a real Herdr pane so the identity comes from
Herdr's own injection. The refusal matrix and the ordering anchor are
covered deterministically in tests/fm-backend-herdr.test.sh.

Eight existing real-Herdr suites inherited the developer terminal's own
Herdr pane into their isolated lab sessions, which the new cross-session
check correctly refuses. tests/herdr-test-safety.sh now owns
herdr_forget_inherited_pane and those suites call it, so what they assert
no longer depends on where they were launched from.

Two unrelated fixes found along the way. tests/fm-secondmate-harness.test.sh
had the same class of environment leak through CLAUDECODE, which outranks
PI_CODING_AGENT in bin/fm-harness.sh and made its pi-signed ancestry case
resolve "claude" whenever the suite ran inside Claude Code. And
fm-spawn.sh's usage() printed a fixed line range that had already been
truncating its own help mid-sentence.

* no-mistakes(review): Enforce exact Herdr launcher and projection identity

* no-mistakes(document): Document exact Herdr launcher workspace placement

* fix(calm): refine Calm working boat animation (#1339)

* feat(calm): replace Pi's working row with an animated ship while Calm is on

While Calm is active and one logical agent run is under way, Calm now hides
Pi's built-in working row and renders a small two-row SSHHIP-derived boat in
its place. When Calm is off, Pi's stock working row is left untouched.

The presentation uses only public Pi extension API: setWorkingVisible(false)
plus a temporary setWidget() component whose render(width) owns the responsive
geometry and whose timer requests a TUI render. Visibility follows agent_start
through agent_settled, so the boat does not flicker between tool calls,
automatic continuations, retries, or compaction inside the same run, and
settle, abort, and failure all reach the same cleanup.

fm-calm.ts stays the sole owner of the presentation choice and the only caller
of setWorkingVisible(); the new lib owns the sprite geometry and widget.

* no-mistakes(review): Guarded Calm-off lifecycle visibility writes; focused tests pass

* no-mistakes(test): Fixed Calm E2E wait to include tmux scrollback

* no-mistakes(document): Document Calm working boat behavior

* no-mistakes: apply CI fixes

* feat(calm): slow the Calm boat, animate blue water, and make the sail directional

The boat now moves one column every 880ms while a bounded fixed-cell water phase
advances every 220ms, so the water ripples several times between boat steps and
the presentation reads as calm. One scheduler drives both clocks and disposing
the widget stops them together; ticks rather than wall-clock timestamps drive
every state change, so tests seek animation time exactly.

Colors are standard ANSI foreground codes instead of theme lookups: blue for
every water cell and yellow for the complete boat, each run closed with a
default-foreground reset so nothing bleeds into padding or later frames. ANSI
bytes never enter geometry, so visible width stays exact.

The mainsail is directional and trails aft of the mast: <| travelling right and
|> travelling left. Direction reverses the moment the boat lands on an endpoint,
so the endpoint frame already shows the new heading and no frame at or after a
bounce shows the previous sail.

* test(calm): wait for the Ctrl+O expansion redraw this block asserts

* docs(calm): record the revised working-presentation verification evidence

* no-mistakes(document): Fix Calm feasibility document EOF whitespace

* fix(dispatch): preflight candidate auth before quota escalation (#1349)

* fix(dispatch): scope candidate authentication to its own surface

A locally expired timestamp in one credential store was reported to the
captain as a sign-out, including for dispatch candidates that never read
that store. A `harness=pi, model=xai/grok-*` candidate authenticates
through Pi's own xAI credential, but the only Grok quota reading
available was gated on the standalone Grok CLI's separate token, whose
expiry clock drifts independently. The always-loaded intake rule then
turned that unreadable quota into a mandatory captain escalation.

Add `bin/fm-auth-preflight.sh` as the deterministic owner of the parts
that must not depend on agent memory: it resolves a tuple's
authentication surface from quota-axi's own emitted auth sources rather
than from a harness or model name, so another harness's CLI can never
gate a candidate that does not use it. A vendor CLI is launched only
when the tuple's own harness owns the credential store under test and a
non-destructive discovery command is registered for it, which today is
`grok models` alone. That probe runs at most once with stdin closed and
a hard timeout, reads its verdict from the first stdout line because the
command exits 0 either way, treats unrecognized output as indeterminate,
and never invokes login, logout, or the interactive TUI. Quota is read
at most twice, and unknown headroom never makes a candidate ineligible
on its own.

Update the dispatch procedure to match: usable authentication with
unmeasurable headroom stays eligible at lower preference with the
unknown disclosed, and stop-and-report is reserved for unresolved
authentication, an unresolved relationship, or malformed configuration.
Record that Grok's `credits.remaining` is a prepaid balance rather than
window headroom.

Gate quota-axi at 0.1.16 in bootstrap, the first build reporting
per-credential auth sources. A stale install previously passed the
presence check silently, which is why a fix published two days earlier
was still not in effect.

Replace the orphaned quota-array-dispatch fixtures, which encoded a
`provider: "xai"` shape the tool never emits and had no consumer, with
fixtures shaped like real 0.1.16 output that the new suite drives the
script against. The suite asserts the verdict and, separately, which
vendor CLIs were launched, so a Pi/xAI candidate reaching the Grok CLI
fails. Map `tests/fixtures/<dir>` to its consuming suite so a fixture
change selects the right tests instead of refusing.

* refactor(bootstrap): give the quota-axi floor one owner

The floor was stated twice - once in bootstrap's gate and once inline in
the auth preflight - so bumping it needed two edits that could drift.
Move it to bin/fm-quota-axi-lib.sh alongside its rationale, matching the
existing tasks-axi library, and derive the comparison from the constant
so the number appears exactly once. Bootstrap turns a failing check into
the operator diagnostic; the preflight refuses to emit an unscoped
verdict. Map the new library to both consuming suites so a bump re-runs
them, and record that any usable source means the surface authenticates.

* no-mistakes(review): Captain: bound quota checks and removed Python dependency

* no-mistakes(review): Captain: enforce conservative headroom and exact preflight retry

* no-mistakes(review): Captain: preserve OpenCode eligibility without auth-surface guessing

* no-mistakes(review): Captain: reject malformed OpenCode model relationships

* no-mistakes(review): Captain: exempt verified unmodeled tuples from intake escalation

* no-mistakes(document): Updated dispatch authentication documentation

* no-mistakes: apply CI fixes

* feat(x-mode): reconcile promised public replies deterministically (#1350)

* feat(x-mode): reconcile promised public replies deterministically

A promised final reply in an X or Discord thread was only kept while the
primary remembered it. Compaction or restart erased that memory, so a typed
public-followup obligation could sit at pending-work after its PR merged and
the original thread never got its reply.

Make the promise durable state instead:

- bin/fm-public-followup-emit.sh reports a typed terminal work result (source
  home, work id, generation, outcome, safe deliverables, bounded public-safe
  text) into the owning home's private inbox. The event id is derived from
  that identity tuple, so duplicate reports and restart replay converge with
  no coordination, and nothing ever parses a free-form done: sentence.
- bin/fm-public-followup.sh registers a commitment, reconciles events through
  tasks-axi public-followup, and runs the idempotent delivery sequence
  (begin-delivery with the payload hash, post, record the posted receipt or a
  typed error) against the stored platform and opaque thread binding. A
  delivery interrupted between post and receipt refuses rather than risk a
  second public reply.
- Session start surfaces unresolved commitments from disk, the existing relay
  poll surfaces a new terminal-result set once, and teardown refuses while
  this home still owes a public reply for that exact work.

tasks-axi public-followup remains the only owner of the obligation state
machine, state/x-context/ the only owner of the private request context, and
fm-x-reply.sh the only thing that posts. Its new optional --receipt-file is
the one addition there, so a caller can record how many messages were sent.

A home that never opted into the myfirstmate relay gates out on a single
[ -f "$FM_HOME/.env" ] test: no tasks-axi call, no backlog or context scan,
no output, and no artifact. Evidence in docs/verification/public-followup.md.

* no-mistakes(review): Hardened public-followup reconciliation and ownership guards

* no-mistakes(review): Hardened typed terminal cleanup and receipt reconciliation

* no-mistakes(review): Automated typed-delivery cleanup and strict backlog validation

* no-mistakes(review): Fail-closed parent resolution and registration-safe delivery

* no-mistakes(review): Harden relay gating and validate secondmate bindings

* no-mistakes(review): Use owner-aware single-gate teardown protection

* no-mistakes(document): Correct public-followup documentation drift

* no-mistakes(lint): Quote done literals to fix ShellCheck warnings

* no-mistakes: apply CI fixes

* feat(bin): replace busy heuristics with semantic lifecycle state (#1327)

* feat: add semantic busy-state contract owner and event writer

One owner (bin/fm-busy-lib.sh) for the captain-approved semantic
busy-state redesign: a per-task gen-bound record written only by
bin/fm-busy-event.sh, per-harness trusted-source classification with
explicit source attribution, busy/idle/unknown/dead semantics where
missing, malformed, stale, or untrusted semantic data is unknown -
never idle - and endpoint death is the only process-level override.
The Grok-only rendered-tail fallback and the standalone-Kimi
verification gate live behind the same classifier.

* feat: arm busy-state at spawn and convert Pi to the semantic extension path

fm-spawn arms the busy-state contract for converted adapters and seeds
busy/fm-spawn (the launch brief is a submitted turn). The Pi/pi-signed
per-task extension now reports agent_start -> busy and agent_settled ->
idle confirmed by ctx.isIdle(), covering auto-retries, compaction
retries, tool loops, and queued continuations, while turn_end stays a
wake notification touch. Teardown removes the new record, gen sidecar,
and lock. Live-verified on Pi 0.82.0: seed -> agent-start busy ->
agent-settled idle with the marker still touched.

* feat: convert OpenCode to the semantic session.status plugin path

The per-task plugin (renamed .opencode/plugins/fm-busy-state.js) now
classifies from OpenCode's semantic session.status events - busy and
retry are active, idle is inactive - latched to the worker's own
session so a subagent child session can never clear the worker's busy
state. The session.idle marker touch stays a wake notification.
Teardown removes both the new and the legacy plugin filenames.
Live-verified on OpenCode 1.17.18 in a real TUI pane: seed ->
session-busy -> session-status-idle.

* feat: convert Claude to the full lifecycle hooks path

The per-task settings.local.json now wires UserPromptSubmit -> busy
and Stop, StopFailure, and SessionEnd -> idle, so API-error and
shutdown turn ends can never strand a busy record; Stop keeps the
turn-ended notification touch. A refused (stale-gen) event exits 0 and
stays silent so Claude's own lifecycle is never broken. Live-verified
on Claude Code 2.1.220: UserPromptSubmit fires for the argv launch
prompt, Stop closes each turn, a mid-stream Escape interrupt fires no
closing hook, and the firstmate-controlled idle/fm-interrupt clear
resolves it.

* feat: gate Codex busy state behind verified semantic sources

The approved contract prefers Codex's app-server turn lifecycle with
capability negotiation and sanctions its lifecycle hooks as the
intermediate. Live probes on codex-cli 0.145.0 show neither is usable
for a pane worker: the app-server daemon is unreachable for a TUI
thread and refuses to start outside the managed standalone install,
and firstmate-written project hooks never fired (interactive with
directory trust granted, and exec, both with
--dangerously-bypass-hook-trust) while global hooks fired in the same
runs. Codex therefore classifies unknown codex-unverified behind an
explicit probe rather than falling back to idle or footer text, and
fm-spawn installs no unverified Codex wiring.

* feat: gate standalone Kimi busy state on live verification

Standalone Kimi has no installed binary here, so per the approved
contract its semantic path stays guarded and it classifies unknown
kimi-unverified rather than idle - and never from its locale-sensitive
moon-phase spinner, which the redesign forbids inventing as a state
source. The gate records the preferred source order (Wire prompt
request lifetime, which brackets a turn and reports cancellation, then
the documented hooks including Interrupt because Stop does not fire on
interrupts) and the exact evidence required to open it. Arming without
wiring would seed a busy record nothing could clear, so both land
together behind the same gate.

* feat: route busy consumers through the contract and drop the global OR

The watcher, crew-state reader, and away-mode daemon now decide busy
state through bin/fm-busy-lib.sh: only an exact busy verdict counts as
working, and unknown never becomes working or a silent idle, so a crew
whose semantic state is missing, malformed, stale, or unverified
surfaces instead of being absorbed. Crew-state reports the producing
source in its detail. The watcher's global OR regex default is gone;
Grok keeps its isolated fallback inside the contract. The daemon's
supervisor-pane reader stays rendered-text - that pane is not a
recorded task - but is now scoped to firstmate's own detected harness
instead of every vendor signature. Secondmate pending-reply
observation is deliberately unchanged and documented as a
delivery-confirmation signal, not task state.

* docs: point busy-state documentation at the single contract owner

Adds a maintainer-architecture section naming bin/fm-busy-lib.sh as
the owner of what busy means, with per-adapter sources, the
unknown-never-idle rule, the endpoint-death override, and the two
rendered-text readers that deliberately stay outside the contract.
Replaces the stale regex-first prose in architecture, tmux-backend,
herdr-backend, and configuration; converts the harness-adapters
per-harness rows from UI signatures to the semantic source each
harness uses; and records the live verification evidence, including
why Codex and standalone Kimi stay unknown.

* fix: arm away-launch signal handlers before acquiring the lifecycle lock

fm_afk_launch_main acquired its lock and only then installed the EXIT,
INT, and TERM traps. A signal arriving in that window terminated the
process by default action and left the lock directory behind, which
blocks the next away-mode launch until the stale-owner reclaim path
clears it. The release helper only removes a lock this process owns,
so the handlers are now armed first. The accompanying test also killed
the child whether or not the lock had appeared and sampled cleanup the
instant wait returned; it now requires the lock, then allows a bounded
settle, so it proves the guarantee instead of racing it.

* test: align fleet, Kimi, lifecycle, and detection suites with the contract

The fleet snapshot and wake-daemon lifecycle fixtures now prove a
working crew through its own semantic busy-state record instead of
rendered pane text, which is what those consumers read. The Kimi
watcher test asserts the approved contract directly: a standalone Kimi
task classifies unknown rather than matching its moon-phase spinner,
while Grok's isolated fallback still classifies only Grok. The
pi-signed detection cases clear ambient harness markers, fixing a
pre-existing failure where the running session's own CLAUDECODE
outranked the fixture's marker.

* fix: stop teardown from deleting a project's own Codex hooks file

An intermediate revision wired Codex through a firstmate-written
<worktree>/.codex/hooks.json, and teardown removed it alongside the
other generated wiring. The Codex wiring was dropped when its probes
came back unverified, so that removal now targets a file firstmate
never creates - and a project may legitimately track its own
.codex/hooks.json, which teardown would then delete from a pooled
worktree.

* fix: keep busy-record parsing from disturbing its sourcing caller

The record parser split fields with set -- under a temporary noglob,
which clobbers a sourcing caller's positional parameters and restores
glob expansion even when the caller had disabled it. The watcher, the
daemon, and the crew-state reader all source this library, so it now
reads fields with read -a, which never globs and never touches caller
state.

* docs: state exactly which Claude hook paths were reproduced live

The busy-state record listed all four wired Claude hooks in the source
column, which could read as a claim that every one fired during the
pass. UserPromptSubmit and Stop did; StopFailure and SessionEnd are
wired from hook names confirmed present in the installed binary, but
the abnormal turn ends they cover were not reproduced.

* test: let reset_fakes own the crew-state busy-text fixture lifecycle

The Grok fallback case set FM_FAKE_BUSY_TEXT and cleared it inline, so
the variable's lifetime was owned by one test rather than by the
shared reset that every other fake already uses.

* no-mistakes(review): Fix semantic busy-state lifecycle races

* no-mistakes(review): Make busy-state retirement idempotent

* no-mistakes(review): Enforce semantic state boundaries for status and injection

* no-mistakes(review): Restore harness-scoped away-mode busy guard

* no-mistakes(document): Refresh semantic busy-state documentation

* no-mistakes: apply CI fixes

* fix: preserve Calm boat continuity across working periods (#1356)

* fix(calm): resume working boat from frozen column across runs

Keep one extension-owned boat animation for the Pi session so settling
freezes column and direction, the next working period resumes there
without hidden-time jumps, and only a fresh session resets to the left edge.

* no-mistakes(review): Freeze Calm boat from last rendered state

* no-mistakes(document): Document Calm boat continuity contract

* fix: restore evidence-based dispatch eligibility (#1358)

* fix(dispatch): judge candidate provider relations instead of rejecting them

Firstmate deterministically dropped supported Pi candidates in the
openai-codex family. bin/fm-auth-preflight.sh resolved a harness=pi tuple's
credential surface by constructing the so…
DereKk8 added a commit to DereKk8/firstmate that referenced this pull request Aug 23, 2026
* fix(supervision): distinguish ordinary wakes from recovery (#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 (#745)

* 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

* feat(wake): enrich drained signals with bounded status context (#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 (#784)

* 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

* fix(send): treat opencode busy-queued composer state as submitted (#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

* fix(spawn): require two stable reads before accepting worktree path (#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>

* fix(bin): make watcher process identity immune to Linux wall-clock changes (#752)

* fix(watcher): stabilize Linux process identity

* no-mistakes(document): document FM_PROC_ROOT_OVERRIDE and Linux starttime identity rationale

* fix: prevent AFK idle stalls and stale run attribution (#758)

* 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(bin): allow safe teardown during watcher recovery (#750)

* 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

* feat(herdr): order presentation spaces while preserving focus (#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

* fix(bin): send literal config reread nudges after pushes (#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 (#797)

* 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

* fix(herdr): group projected children beneath owning parents (#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: keep local no-mistakes tests intent-targeted (#823)

* 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: add canonical timed test runner (#825)

* 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: surface main inventory gaps in Bearings (#830)

* 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 bounded concurrent test isolation proof (#832)

* 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: guard against missed secondmate reports (#834)

* 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: require pinned real-Herdr CI coverage (#838)

* 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

* feat: shard portable tests and add bounded local parallelism (#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

* fix: block primary-session delegation outside the fleet (#854)

* 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

* fix: install tasks-axi in portable CI shards (#866)

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(bin): make dispatch profiles quota aware (#867)

* 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

* Add built-in ahoy recap skill (#873)

* fix: preserve trustworthy Bearings data in partial snapshots (#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

* feat(pi): add session-local calm mode (#884)

* 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): prevent redundant watcher re-arms (#885)

* 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 (#895)

* 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 (#898)

* fix: execute every PR body compliance event

* no-mistakes(document): Document independent PR compliance events

* fix: exclude operational injections from ahoy boundaries (#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

* fix: canonically classify operational inputs across harnesses (#909)

* fix: type canonical operational inputs

* no-mistakes(document): Correct canonical operational-input documentation ownership

* fix: avoid generic secondmate start acknowledgements (#926)

* fix: avoid generic secondmate acknowledgements

* no-mistakes(document): Document sparse secondmate acknowledgement behavior

* no-mistakes: apply CI fixes

* fix(pi): make calm mode persistent and gapless (#927)

* fix(pi): preserve calm presentation across sessions

* no-mistakes(review): Fix Calm home fallback persistence

* no-mistakes(document): Clarify Calm gapless and export contracts

* no-mistakes: apply CI fixes

* fix(watch): retire merged PR polls after durable notification (#932)

* fix: retire merged PR polls after notification

* no-mistakes(review): Decouple PR retirement recovery from template updates

* no-mistakes(review): Recover pending PR retirements before poll migration

* no-mistakes(document): Document merged PR poll retirement contracts

* fix: refine scout intake and parallel dispatch (#934)

* Clarify intake evidence and overlap handling

* no-mistakes(review): Align scout guard with intake classification

* no-mistakes(document): Clarify scout documentation and intake ownership

* fix(pi): prevent duplicate assistant replies in Calm (#936)

* fix(pi): preserve operational follow-up semantics in Calm

* no-mistakes(document): Correct Calm operational-row visibility documentation

* perf(bin): shrink the ShellCheck source graph (#939)

* perf(lint): shrink shell source graph

* no-mistakes(review): Ensure lint workers terminate fully on cancellation

* no-mistakes(document): Repair stale lint documentation ownership

* fix(pi): remove Calm hidden-block gaps (#942)

* fix(pi): remove Calm hidden-block gaps

* no-mistakes(review): Validate Calm geometry against current viewport

* no-mistakes(review): Synchronize Calm geometry checks with reload completion

* fix: enforce contract boundaries for ask-user findings (#945)

* fix: escalate ask-user contract expansion

* no-mistakes(document): Point project management to authority owner

* docs: prefer direct operational paths (#946)

* fix(pi): hide operational user rows in Calm mode (#948)

* fix(pi): hide Calm operational user rows

* no-mistakes(review): Narrow Calm operational input suppression

* no-mistakes(review): Avoid Calm replay classifier subprocesses

* no-mistakes(document): docs: point Pi verification to Calm owner

* fix: relaunch missing second mates at session start (#950)

* fix(session-start): relaunch missing second mates

* fix(test): detect completed parallel workers

* no-mistakes(review): Isolate session-start recovery test cleanup

* no-mistakes(review): Complete backend-safe secondmate session recovery

* no-mistakes(review): Resolve Zellij task ownership before recovery

* no-mistakes(review): Recover relocated Zellij ghost tabs safely

* no-mistakes(review): Restore conservative Zellij recovery boundary

* no-mistakes(review): Reject malformed tmux recovery targets

* no-mistakes(document): Align secondmate recovery documentation

* no-mistakes: apply CI fixes

* fix(herdr): reclaim resumed task projections after restart (#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

* Teach Ahoy to surface open decisions (#968)

* Require shipshape routine acknowledgement (#969)

* docs: separate current guidance from verification evidence (#994)

* docs: separate current guides from verification

* no-mistakes(review): Restore Herdr 0.7.5 restart-reclaim verification evidence

* fix: preserve Claude watcher continuity across Stop hooks (#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

* fix(herdr): clean stale projections at session start (#996)

* 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

* fix: recover Claude supervision without watcher-status gate (#1001)

* fix: recover Claude supervision at session start

* fix: remove Claude watcher-status command gate

* no-mistakes(document): docs: remove stale continuity gate references

* fix: make quota-aware profile selection agent-owned (#1018)

* Replace quota dispatch selector instructions

* no-mistakes(review): Align bootstrap docs with agent-owned dispatch selection

* fix(bin): remove vestigial dispatch selector (#1026)

* 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

* docs(agents): drop superseded interim quota-window rule (#1039)

quota-axi 0.1.13 emits schemaVersion 2 with a quotaSemantics object per
provider, so the successor named in the interim rule has landed and the
rule's own removal condition is satisfied.

Keep the ownership clause so quota-axi remains the single owner of how
model or product windows relate to bounding account windows, and drop
the interim weakest-headroom instruction. The unknown-semantics case is
already covered by the existing requirement to stop and report a
candidate whose applicable quota data or interpretation cannot be
established.

Drop the matching assertion phrase from
tests/fm-instruction-owners.test.sh; the retained ownership phrase still
asserts.

* fix(tmux): scope busy detection and recognize current Claude turns (#1049)

* fix(tmux): scope Claude busy detection by harness

* no-mistakes(review): Separate verified and fallback busy signatures

* no-mistakes(test): Scope busy signatures to supplied harnesses

* no-mistakes(document): Document harness-scoped busy detection

* feat: add verified Kimi crewmate adapter (#1047)

* Add verified Kimi crewmate harness adapter

* no-mistakes(review): Scope Kimi moon detection to spinner lines

* no-mistakes(review): Match only complete Kimi spinner rows

* no-mistakes(review): Resolve Kimi binary portably before pane creation

* no-mistakes(document): Align Kimi adapter documentation

* no-mistakes(lint): Suppress false-positive ShellCheck warning for sourced watcher override

* Fix Kimi busy spinner detection

* no-mistakes(review): Recognize Kimi session-lock ancestry and holders

* no-mistakes(review): Scope pending-reply Kimi busy detection by harness

* no-mistakes(document): Correct Kimi spinner capture documentation

* no-mistakes(document): Clarify optional Kimi spinner whitespace

* no-mistakes(lint): Silence intentional pending-reply test stub warnings

* test: align rebased Kimi busy fixtures

* no-mistakes: apply CI fixes

* Reconcile Kimi busy detection after per-harness scoping

* no-mistakes(review): Clarify observed Kimi spinner whitespace contract

* no-mistakes(document): Clarify Kimi harness documentation

* fix: harden Kimi submission and spinner matching (#1058)

* fix kimi pointer submission and spinner conformance

* no-mistakes(review): Preserve Kimi submit target ownership guard

* feat(bin): add guarded Kimi turn-end wake (#1059)

* Add guarded Kimi turn-end hook

* no-mistakes(review): Require jq before installing Kimi turn-end hook

* no-mistakes(review): Expose jq inside isolated Kimi test fixtures

* no-mistakes(review): Preserve Kimi config boundaries during hook removal

* no-mistakes(review): Document Kimi removal newline safeguard

* no-mistakes(document): Document Kimi shared-home preservation

* fix(tmux): classify bordered composers across all rows (#1066)

* Fix structural tmux composer reading

* Verify Calm compatibility with Pi 0.82

* no-mistakes(review): Harden structural composer classification boundaries

* no-mistakes(review): Refresh composer and Kimi regression fixtures

* no-mistakes(review): Fail closed on unbounded composer edges

* no-mistakes(review): Enforce aligned composer geometry safely

* no-mistakes(review): Make composer ambiguity locale-safe

* no-mistakes(review): Preserve ambiguity through composer submission

* no-mistakes(review): Carry composer proof through retries

* no-mistakes(document): Document structural tmux composer delivery guarantees

* no-mistakes: apply CI fixes

* feat(bin): add verified pi-signed runtime adapter (#1145)

* feat: add verified pi-signed adapter

* no-mistakes(review): Correct pi-signed maintainer verification date

* no-mistakes(review): Correct remaining pi-signed verification dates

* no-mistakes(review): Preserve authoritative pi-signed runtime identity

* no-mistakes(document): Document pi-signed shared adapter semantics

* no-mistakes: apply CI fixes

* fix(pi): rearm watcher across session transitions (#1166)

* fix(pi): rearm watcher across same-process session transitions

Pi emits session_shutdown for ordinary /new, /resume, and /fork replacement
as well as terminal quit. The primary watcher extension latched a module-level
stopping flag on every shutdown, so a replacement session in the same process
could not arm monitoring until Pi restarted.

Own arm authority per session generation so only the active live generation
may start, stop, or rearm the child. Replacement sessions can arm again without
restarting Pi, stale prior-generation callbacks cannot mutate the active cycle,
and real quit still blocks late rearm.

* no-mistakes(review): Preserve Pi generation isolation and exit cleanup

* no-mistakes(document): Correct Pi watcher transition documentation

* feat: route crew dispatch using quota-window pace (#1172)

* Consume quota-axi pace signals in dispatch profile array selection.

Add quota-array-dispatch as the single owner of the pace-aware candidate
choice, keep AGENTS.md to the intake boundary and load trigger, and cover
the acceptance cases with sanitized schemaVersion 3 fixtures.

* no-mistakes(review): Stop and report genuine quota dispatch ties

* no-mistakes(document): Document quota pace freshness and uncertainty

* fix: adapt Grok Stop continuation and harden endpoint cleanup (#1171)

* fix(grok): adapt Stop continuation to runtime capability

* no-mistakes(review): Reject ambiguous Grok Stop payloads

* no-mistakes(review): Reject duplicate Grok fields and accept spaced tmux sessions

* no-mistakes(review): Enforce exact tmux cleanup selectors

* no-mistakes(test): Fix historical tmux fixture and validate Grok Stop

* no-mistakes: apply CI fixes

* fix: restore stock macOS Bash 3.2 brief scaffolding (#1093)

* fix(brief): make DOD scaffolding parse-safe on stock macOS Bash 3.2

fm-brief.sh built each Definition-of-done block and the not-enabled
Herdr declaration with `VAR=$(cat <<EOF ... EOF)`. On Bash 3.2 (macOS
/bin/bash) the lexer scans for the command substitution's closing `)`
textually and tracks quote state through the heredoc body, so a single
apostrophe, unbalanced quote, or unbalanced paren in that prose breaks
parsing of the whole script. Every ship-brief scaffold (no-mistakes,
direct-PR, local-only) failed with `unexpected EOF while looking for
matching )`. Bash 4+ parses it fine, so the breakage stayed invisible
everywhere except stock macOS.

Replace all four command-substitution heredocs with
`IFS= read -r -d '' VAR <<EOF || true`. That removes the `$(...)`
wrapper and the entire defect class regardless of future prose, and
preserves the variable expansion the direct-PR and local-only bodies
need. `read` keeps the heredoc's trailing newline that `$(...)` used to
strip, so trim one newline to keep every generated brief byte-identical
to prior output.

Guard the structure, not one historical phrase: a new test rejects any
heredoc nested in a command substitution anywhere in fm-brief.sh, where
the old assertion pinned a single apostrophe phrase and so missed the
reintroduction. Extend the stock-macOS Bash CI job from parsing one
script to the whole maintained shell surface (bin/*.sh,
bin/backends/*.sh, tests/*.sh), matching bin/fm-lint.sh's canonical file
set so parse scope and lint scope cannot drift apart.

* no-mistakes(review): Captain: harden Bash structure and inventory guards

* no-mistakes(document): Align stock macOS Bash contributor checks

* no-mistakes(lint): Suppress deliberate SC2016 literal fixture warnings

* test: stabilize tmux teardown conformance baseline (#1209)

* fix(test): pin teardown tmux baseline to historical kill selectors

merge-base HEAD main collapses to HEAD after the exact-selector change
lands on the default branch, so the old teardown fixture was accidentally
exercising current exact targets. Resolve a content-historical permissive
tmux adapter from first-parent history and force that post-squash topology
inside the conformance case so main and feature branches keep the same
old-vs-new contract.

* no-mistakes(lint): Suppress intentional literal-pattern ShellCheck warnings

* docs: slim quota-array-dispatch to the pace selection core (#1197)

Cut the runtime skill to the compact pace-aware selection procedure plus
minimum owner pointers. Keep every distinct decision rule and move expanded
acceptance scenarios to deterministic fixture ownership assertions.

Size: 170/1374/10187 -> 63/544/4068 (about 63%/60%/60% reduction).

* feat(bin): inherit backend config into secondmate homes (#1219)

* Inherit config/backend into secondmate homes with deliberate-override preservation

Add backend to the shared inheritable config allowlist so launch, locked
bootstrap, and config-push converge a primary pin into secondmate homes as each
home local future-spawn default. Track last-inherited bytes in a private state
provenance marker so deliberate per-home overrides survive present and absent
primary convergence, keep --backend and FM_BACKEND stronger, and extend the
existing inheritance tests plus docs and skill claims.

* no-mistakes(review): Preserve equal unprovenanced backend overrides

* no-mistakes(review): Preserve symlink overrides and verify spawn precedence

* no-mistakes(review): Snapshot backend inheritance for consistent provenance

* no-mistakes(review): Simplify backend inheritance to primary-authoritative convergence

* no-mistakes(document): Document inherited backend override preservation

* fix: restore primary-authoritative backend inheritance after document regression

The document step reintroduced provenance and deliberate per-home override
semantics after review had simplified config/backend to plain primary-authoritative
allowlist membership. Restore the primary-always-wins path: present overwrites,
absent removes, no provenance marker, and docs/tests match that contract.

* no-mistakes(review): Add divergent backend precedence regression fixtures

* no-mistakes(document): Document backend inheritance contract

* fix(pi): remove Calm's upper version ceiling (#1226)

* fix(pi): remove Calm's exclusive Pi upper-version ceiling

tests/fm-calm-pi-extension.test.sh gated on a closed PI_COMPAT_VERSIONS
allowlist ("0.81.1 0.82.0") that refused any other installed Pi, and docs
described that range as "supported" rather than verified evidence. The
Calm CHANGELOG shows no API introduced at either version, so there is no
evidence for a real minimum; the presentation adapters already probe the
exact method they patch rather than checking a version.

Replace the allowlist with dated version evidence that never rejects a
newer Pi, and make each presentation adapter degrade independently with
a diagnostic if a future Pi removes its API, instead of the whole Calm
extension failing to load. Rewrite the feasibility doc's "Pi 0.81.1
through 0.82.0" phrasing to state it as verified evidence, not a
ceiling.

* no-mistakes(review): Probe missing Calm adapter exports safely

* no-mistakes(document): Document Calm's unbounded Pi compatibility

* fix(bin): allow session-local todo tools in the subagent guard (#1204)

* fix(guard): allow session-local todo tools in the primary

The delegation-shape guard denied TaskCreate and TaskUpdate because their
normalized names contain the `task` stem. Those tools write only the harness's
session-local todo list, which has no executor: it spawns no agent, allocates
no worktree, registers no schedule, and starts nothing that outlives the
session. That is not the unaccounted work the guard exists to stop, so the stem
match was a false positive, and the deny text told the primary to run
bin/fm-brief.sh and bin/fm-spawn.sh to create a todo entry.

Add a separately-reasoned PLAN_ONLY_TOOLS exact-name exclusion rather than
widening OBSERVE_ONLY_TOOLS, whose documented contract is tools that only
observe or stop existing work. Both lists stay exact-name so neither can widen
by substring.

Tests cover the two allowed names and six near-miss names that a substring or
shortened-stem widening would release; both mutations were watched red.

* no-mistakes(review): drop session-local todo tools from recommended deny list

* no-mistakes: apply CI fixes

* fix(session-lock): resolve Claude bg-spare ancestry to the outermost claude pid (#1206)

* fix(session-lock): resolve Claude bg-spare ancestry to the outermost claude pid

fm_harness_ancestry_pid() previously returned the first ancestor process
whose command matched a verified harness name. Claude Code's Stop hook
fires as a bg-spare worker several levels below the session's actual
lock-owning claude process (hook shell -> claude bg-spare ->
claude bg-pty-host -> claude -> claude(lock)), so the first match was
the bg-spare worker, not the lock owner. fm_session_lock_owned_by_self()
then never matched state/.lock, and the Claude Stop auto-arm silently
treated its own primary session as an unrelated live owner and never
armed the watcher.

The walk now keeps going past a claude-named match, looking for a still
more ancestral claude-named match, and stops the instant a non-match
follows an already-found match (bounding it to a contiguous run rather
than the literal ancestry top, so an unrelated claude-named process
further up the real process tree is never mistaken for part of this
session's own nested chain). Every other harness keeps the original
first-match-wins behavior, since e.g. Pi's shared signed-wrapper
ancestry actually holds the session at the inner engine pid, not an
outer wrapper pid. Hop limit raised from 8 to 16 to cover the deeper
bg-spare chain.

* no-mistakes(review): Add nested-claude-ancestry regression test; fix nudge doc depth claim

* no-mistakes: apply CI fixes

* fix: conferma l'avvio del watcher su Windows/MSYS (#1212)

* fix: confirm watcher startup on MSYS

* no-mistakes(review): gate MSYS arm ready timeout, cache uname, harden locale test

* no-mistakes(review): validate OpenCode ready timeout, make uname cache internal

* fix(spawn): forward CLAUDE_CONFIG_DIR to claude crewmates (#1195)

* fix(spawn): forward firstmate's CLAUDE_CONFIG_DIR to claude crewmates

Crewmate panes are created by a long-lived tmux/herdr daemon that does not
inherit firstmate's current environment. When firstmate runs under a non-default
CLAUDE_CONFIG_DIR (for example a work-vs-personal subscription split), a bare
`claude` in the crewmate pane fell back to the default ~/.claude store and
launched unauthenticated, blocking the crewmate before it could do any work.

fm-spawn now prefixes the claude launch with firstmate's own resolved
CLAUDE_CONFIG_DIR when set, so the crewmate uses the same credential/config
store firstmate is authenticated with. An unset value is the single-store
default and adds no prefix; non-claude harnesses are unaffected.

Adds three tests in fm-spawn-dispatch-profile.test.sh (forwarded-when-set,
omitted-when-unset, non-claude-ignored) and pins CLAUDE_CONFIG_DIR in the test
helper so launch assertions no longer depend on the developer's environment.

* no-mistakes: apply CI fixes

* fix: preserve dispatch identity across authentication checks (#1233)

* fix: preserve dispatch harness identity

* no-mistakes(review): Fix Grok counterfactual tuple validation

* no-mistakes(document): Scope dispatch authentication to selected tuple

* fix: restore dispatch instruction budget

* no-mistakes(review): Scope dispatch authentication after candidate selection

* fix(bin): normalize relative durable paths (#1256)

* fix(bin): handle dash-leading harness process names (#2)

* fix: handle dash-leading harness process names

* no-mistakes(review): Make dash-leading harness regression hermetic

* fix: preserve secondmate reply routes across relative homes

Resolve relative home, data, and state inputs before durable charter generation, and fail when caller-relative directories cannot be resolved.

Use absolute paths at the related spawn, AFK daemon, and X-mode cross-process handoffs so later processes cannot reinterpret them from another working directory.

* no-mistakes(review): Preserve absolute overrides and normalize relative durable paths

* no-mistakes(review): Normalize relative home before deriving durable paths

* no-mistakes(document): Document relative durable-path normalization

* no-mistakes(review): Captain: Ignore inherited CDPATH during relative path normalization

* no-mistakes(lint): Fix empty CDPATH assignments for ShellCheck

* refactor(skills): make Bearings chat-only by default (#1136)

* Add internal status skill

* no-mistakes(document): register /status skill in documentation-audiences inventory

* no-mistakes(lint): replace grep|wc -l with grep -c in status skill test

* test: silence literal status skill patterns

* Refactor bearings default to chat-only

---------

Co-authored-by: Kun Chen <3233006+kunchenguid@users.noreply.github.com>

* Clarify follow-up routing during validation (#1277)

* fix: honor concrete approval for project operations (#1272)

* docs: add captain-approved project operation exception to hard rule 1

Firstmate stays read-only over projects by default, but when the captain
clearly approves a concrete project operation and scope in the moment,
firstmate may perform exactly that approved operation with its own tools.
The approval is never inferred, broadened, or standing, and it does not
relax the existing force, discard, unlanded-work, or merge-authority
boundaries.

* no-mistakes(review): Clarify captain-approved project operation boundaries

* no-mistakes(document): Clarify captain-approved project operation scope

* docs: cover directories and preserve the operation-or-scope alternative

Widen the captain-approved project operation exception in AGENTS.md to
files or directories, and restore the explicit operation-or-scope
alternative that a prior pipeline auto-fix had collapsed into "and".

Rework project-management SKILL.md's Remove section, which previously
told firstmate to refuse project removal until a guarded helper existed;
that helper was never built, so the text directly contradicted the new
instruction-only exception. It now points at the exception plus the
existing removal preflight it still requires unchanged.

Update the one instruction-owners test assertion that hard-coded the
sentence removed above, so the suite tracks current, not obsolete, text.

* docs: add captain-approved project operation exception to hard rule 1

Firstmate stays read-only over projects by default, but when the captain
clearly approves a concrete project operation and scope in the moment,
firstmate may perform exactly that approved operation with its own tools.
The approval is never inferred, broadened, or standing, and it does not
relax the existing force, discard, unlanded-work, or merge-authority
boundaries.

* no-mistakes(review): Clarify captain-approved project operation boundaries

* no-mistakes(document): Clarify captain-approved project operation scope

* docs: cover directories and preserve the operation-or-scope alternative

Widen the captain-approved project operation exception in AGENTS.md to
files or directories, and restore the explicit operation-or-scope
alternative that a prior pipeline auto-fix had collapsed into "and".

Rework project-management SKILL.md's Remove section, which previously
told firstmate to refuse project removal until a guarded helper existed;
that helper was never built, so the text directly contradicted the new
instruction-only exception. It now points at the exception plus the
existing removal preflight it still requires unchanged.

Update the one instruction-owners test assertion that hard-coded the
sentence removed above, so the suite tracks current, not obsolete, text.

* no-mistakes(review): Align project removal preflight with approved exception

* no-mistakes(document): Align project removal documentation with approved exception

* fix: restore removal test byte-for-byte and preserve the default sentence

tests/fm-instruction-owners.test.sh had been changed to assert different
text; restore it byte-for-byte to origin/main. project-management SKILL.md's
Remove section now keeps the exact default "Never issue a raw removal
command from Firstmate." sentence that test still asserts, immediately
followed by the already-approved captain-operation-or-scope exception, so
the default and the exception both stay explicit and consistent.

* no-mistakes(document): Align project-write boundary documentation

* fix(skills): route new project intake through secondmate scopes (#1275)

* Route project intake through secondmate scopes

* no-mistakes(test): Guard all main-home project registry mutations

* no-mistakes(document): Consolidate secondmate routing documentation

* no-mistakes: apply CI fixes

* Restore new-project routing scope

* no-mistakes(document): Clarify secondmate routing for new-project intake

* no-mistakes: apply CI fixes

* fix: scope validation corrections by accepted behavior (#1281)

* fix: scope validation corrections by accepted behavior

* no-mistakes(review): Classify stale delivery evidence as an autonomous correction

* test: replace source assertions with behavioral coverage (#1282)

* test: remove source-content assertions

* no-mistakes(review): Replace source assertions with runtime behavior coverage

* no-mistakes(review): Isolate Kimi task temp runtime coverage

* no-mistakes(document): Refresh test cleanup documentation

* no-mistakes: apply CI fixes

* fix(watch): escalate busy workers with no completed turn (#1286)

* fix(watch): bound how long a busy pane may run with no completed turn

A busy pane (backend busy state or the harness's rendered footer) was
unconditional, unbounded proof of liveness in every escalation path, so a
hung foreground tool call behind a busy signature could run for hours
undetected (2026-07 hibit-agent-focus-nonsteal-r1 incident: a catastrophic-
backtracking regex hung one bash call for 25h behind an unchanging
"Working..." footer).

FM_BUSY_TURN_MAX_SECS (default 3600s) now bounds how long a busy pane may
run with no completed turn (state/<id>.turn-ended, or its spawn record
before any turn has completed). Past the bound, busy_turn_over_age routes
the pane through the existing wedge_timer_check, reusing the identical
stale reason, escalation counter, and demand-deep-inspection marker for
human inspection only - never an automatic interrupt, signal, or restart
of the worker or its tool process. A completed turn resets the age.

Reproduced end-to-end against the real installed Pi TUI: a foreground
`sleep 999999` bash call with no timeout renders the actual busy footer,
and two captures ~15s apart show the elapsed counter changing the pane
hash while the same turn stays unfinished. Running the pre-fix watcher
against the real captures showed it never starts a wedge timer no matter
how long the pane stays busy; the fixed watcher starts and escalates the
timer through the same mechanism, while the real hung process remained
untouched and alive throughout.

* no-mistakes(review): fix: parse enriched AFK stale reasons

* no-mistakes(review): fix: preserve enriched wedges during AFK supervision

* no-mistakes(review): fix: route all enriched AFK wedges

* no-mistakes(document): Clarify busy-turn age supervision documentation

* fix(gitignore): ignore config/ as a directory, not by exact filename (#1261)

A name-by-name list of config/ entries silently stops ignoring any new or
home-local file placed there, which makes the working tree read as dirty and
blocks guarded sync paths that refuse to touch a dirty home. AGENTS.md
already documents config/ as captain-private and gitignored as a category;
this makes .gitignore match that contract.

* fix(tests): replace source-content .gitignore assertion with behavioral coverage (#1304)

The second assertion in fm-gitignore-config.test.sh (added by #1261) greps
.gitignore for a specific spelling of the config/ ignore pattern. It fails
on a semantically equivalent pattern like config/** and does not prove Git
actually ignores anything, per the completed source-content-test audit.

Replace it with a real git check-ignore control test on a generated
unrelated path, and strengthen the existing directory-coverage test with
generated unpredictable direct and nested config/ paths.

* feat: bound and consolidate startup memory during stow (#1303)

* Add bounded startup memory curation

* no-mistakes(review): Record reproducible stow verification evidence

* no-mistakes(review): Validate inherited secondmate stow evidence

* no-mistakes(document): Document editable startup-memory budget propagation

* fix(herdr): place workers in the launching workspace (#1328)

* fix(herdr): place workers in the launching agent's exact workspace

Herdr enforces no workspace-label uniqueness, and spawn resolved its
container by taking the FIRST workspace whose label matched the home
label. With two workspaces both labeled "firstmate", a worker launched
from the second one was created in the first, so it appeared in a
different space than the Firstmate the captain was watching.

Reproduced end to end on Herdr 0.7.5 protocol 17 by running the real
bin/fm-spawn.sh inside a launcher pane in the second "firstmate"
workspace: the worker landed in w1 while its launcher was in w2, with an
unrelated third workspace focused throughout, which also rules out any
dependence on the focused workspace.

Placement now binds to the launching process's own Herdr identity. Herdr
injects HERDR_PANE_ID, HERDR_SESSION, and HERDR_SOCKET_PATH into every
process it manages a pane for, and fm_backend_herdr_launcher_identity
resolves that pane's current owning tab and workspace live from Herdr,
cross-checking the pane against its tab and confirming the workspace
exists exactly once in the session. The injected HERDR_TAB_ID and
HERDR_WORKSPACE_ID are creation-time snapshots and are deliberately not
read as current identity. Labels are no longer placement authority.

A claimed parent identity that is unreadable, contradictory, stale, or
from another named session or Herdr server stops the spawn before any
worker endpoint exists, rather than degrading to a label search. A
launcher with no Herdr ancestry has no workspace to inherit and keeps
the per-home labeled container, which must now resolve to exactly one
workspace; two same-labeled candidates refuse instead of adopting
either. A --secondmate launch keeps standing up that home's own
workspace by design.

With presentation spaces enabled, the projected child is created and
bound under that same exact parent and anchors its ordering on it, so a
duplicated home label no longer makes the layout ambiguous. Projection,
focus restoration, restart binding, and quarantine rules are unchanged,
and children are never collapsed into the parent. tmux, Zellij, cmux,
Orca, and the away-mode daemon terminal were each inspected and are not
affected: none resolves a container by searching mutable labels.

tests/fm-backend-herdr-launcher-workspace-e2e.test.sh drives the real
spawn and teardown against an isolated Herdr lab, with its headline case
running fm-spawn.sh inside a real Herdr pane so the identity comes from
Herdr's own injection. The refusal matrix and the ordering anchor are
covered deterministically in tests/fm-backend-herdr.test.sh.

Eight existing real-Herdr suites inherited the developer terminal's own
Herdr pane into their isolated lab sessions, which the new cross-session
check correctly refuses. tests/herdr-test-safety.sh now owns
herdr_forget_inherited_pane and those suites call it, so what they assert
no longer depends on where they were launched from.

Two unrelated fixes found along the way. tests/fm-secondmate-harness.test.sh
had the same class of environment leak through CLAUDECODE, which outranks
PI_CODING_AGENT in bin/fm-harness.sh and made its pi-signed ancestry case
resolve "claude" whenever the suite ran inside Claude Code. And
fm-spawn.sh's usage() printed a fixed line range that had already been
truncating its own help mid-sentence.

* no-mistakes(review): Enforce exact Herdr launcher and projection identity

* no-mistakes(document): Document exact Herdr launcher workspace placement

* fix(calm): refine Calm working boat animation (#1339)

* feat(calm): replace Pi's working row with an animated ship while Calm is on

While Calm is active and one logical agent run is under way, Calm now hides
Pi's built-in working row and renders a small two-row SSHHIP-derived boat in
its place. When Calm is off, Pi's stock working row is left untouched.

The presentation uses only public Pi extension API: setWorkingVisible(false)
plus a temporary setWidget() component whose render(width) owns the responsive
geometry and whose timer requests a TUI render. Visibility follows agent_start
through agent_settled, so the boat does not flicker between tool calls,
automatic continuations, retries, or compaction inside the same run, and
settle, abort, and failure all reach the same cleanup.

fm-calm.ts stays the sole owner of the presentation choice and the only caller
of setWorkingVisible(); the new lib owns the sprite geometry and widget.

* no-mistakes(review): Guarded Calm-off lifecycle visibility writes; focused tests pass

* no-mistakes(test): Fixed Calm E2E wait to include tmux scrollback

* no-mistakes(document): Document Calm working boat behavior

* no-mistakes: apply CI fixes

* feat(calm): slow the Calm boat, animate blue water, and make the sail directional

The boat now moves one column every 880ms while a bounded fixed-cell water phase
advances every 220ms, so the water ripples several times between boat steps and
the presentation reads as calm. One scheduler drives both clocks and disposing
the widget stops them together; ticks rather than wall-clock timestamps drive
every state change, so tests seek animation time exactly.

Colors are standard ANSI foreground codes instead of theme lookups: blue for
every water cell and yellow for the complete boat, each run closed with a
default-foreground reset so nothing bleeds into padding or later frames. ANSI
bytes never enter geometry, so visible width stays exact.

The mainsail is directional and trails aft of the mast: <| travelling right and
|> travelling left. Direction reverses the moment the boat lands on an endpoint,
so the endpoint frame already shows the new heading and no frame at or after a
bounce shows the previous sail.

* test(calm): wait for the Ctrl+O expansion redraw this block asserts

* docs(calm): record the revised working-presentation verification evidence

* no-mistakes(document): Fix Calm feasibility document EOF whitespace

* fix(dispatch): preflight candidate auth before quota escalation (#1349)

* fix(dispatch): scope candidate authentication to its own surface

A locally expired timestamp in one credential store was reported to the
captain as a sign-out, including for dispatch candidates that never read
that store. A `harness=pi, model=xai/grok-*` candidate authenticates
through Pi's own xAI credential, but the only Grok quota reading
available was gated on the standalone Grok CLI's separate token, whose
expiry clock drifts independently. The always-loaded intake rule then
turned that unreadable quota into a mandatory captain escalation.

Add `bin/fm-auth-preflight.sh` as the deterministic owner of the parts
that must not depend on agent memory: it resolves a tuple's
authentication surface from quota-axi's own emitted auth sources rather
than from a harness or model name, so another harness's CLI can never
gate a candidate that does not use it. A vendor CLI is launched only
when the tuple's own harness owns the credential store under test and a
non-destructive discovery command is registered for it, which today is
`grok models` alone. That probe runs at most once with stdin closed and
a hard timeout, reads its verdict from the first stdout line because the
command exits 0 either way, treats unrecognized output as indeterminate,
and never invokes login, logout, or the interactive TUI. Quota is read
at most twice, and unknown headroom never makes a candidate ineligible
on its own.

Update the dispatch procedure to match: usable authentication with
unmeasurable headroom stays eligible at lower preference with the
unknown disclosed, and stop-and-report is reserved for unresolved
authentication, an unresolved relationship, or malformed configuration.
Record that Grok's `credits.remaining` is a prepaid balance rather than
window headroom.

Gate quota-axi at 0.1.16 in bootstrap, the first build reporting
per-credential auth sources. A stale install previously passed the
presence check silently, which is why a fix published two days earlier
was still not in effect.

Replace the orphaned quota-array-dispatch fixtures, which encoded a
`provider: "xai"` shape the tool never emits and had no consumer, with
fixtures shaped like real 0.1.16 output that the new suite drives the
script against. The suite asserts the verdict and, separately, which
vendor CLIs were launched, so a Pi/xAI candidate reaching the Grok CLI
fails. Map `tests/fixtures/<dir>` to its consuming suite so a fixture
change selects the right tests instead of refusing.

* refactor(bootstrap): give the quota-axi floor one owner

The floor was stated twice - once in bootstrap's gate and once inline in
the auth preflight - so bumping it needed two edits that could drift.
Move it to bin/fm-quota-axi-lib.sh alongside its rationale, matching the
existing tasks-axi library, and derive the comparison from the constant
so the number appears exactly once. Bootstrap turns a failing check into
the operator diagnostic; the preflight refuses to emit an unscoped
verdict. Map the new library to both consuming suites so a bump re-runs
them, and record that any usable source means the surface authenticates.

* no-mistakes(review): Captain: bound quota checks and removed Python dependency

* no-mistakes(review): Captain: enforce conservative headroom and exact preflight retry

* no-mistakes(review): Captain: preserve OpenCode eligibility without auth-surface guessing

* no-mistakes(review): Captain: reject malformed OpenCode model relationships

* no-mistakes(review): Captain: exempt verified unmodeled tuples from intake escalation

* no-mistakes(document): Updated dispatch authentication documentation

* no-mistakes: apply CI fixes

* feat(x-mode): reconcile promised public replies deterministically (#1350)

* feat(x-mode): reconcile promised public replies deterministically

A promised final reply in an X or Discord thread was only kept while the
primary remembered it. Compaction or restart erased that memory, so a typed
public-followup obligation could sit at pending-work after its PR merged and
the original thread never got its reply.

Make the promise durable state instead:

- bin/fm-public-followup-emit.sh reports a typed terminal work result (source
  home, work id, generation, outcome, safe deliverables, bounded public-safe
  text) into the owning home's private inbox. The event id is derived from
  that identity tuple, so duplicate reports and restart replay converge with
  no coordination, and nothing ever parses a free-form done: sentence.
- bin/fm-public-followup.sh registers a commitment, reconciles events through
  tasks-axi public-followup, and runs the idempotent delivery sequence
  (begin-delivery with the payload hash, post, record the posted receipt or a
  typed error) against the stored platform and opaque thread binding. A
  delivery interrupted between post and receipt refuses rather than risk a
  second public reply.
- Session start surfaces unresolved commitments from disk, the existing relay
  poll surfaces a new terminal-result set once, and teardown refuses while
  this home still owes a public reply for that exact work.

tasks-axi public-followup remains the only owner of the obligation state
machine, state/x-context/ the only owner of the private request context, and
fm-x-reply.sh the only thing that posts. Its new optional --receipt-file is
the one addition there, so a caller can record how many messages were sent.

A home that never opted into the myfirstmate relay gates out on a single
[ -f "$FM_HOME/.env" ] test: no tasks-axi call, no backlog or context scan,
no …
Alchemy86 added a commit to Alchemy86/firstmate that referenced this pull request Aug 23, 2026
* fix(bin): repair fm-brief.sh parse error and harden set -u array expansion (#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>
#166 apostrophe regression cannot return unnoticed.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(bin): keep watcher supervision continuous across child cycles (#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: fetch current PR head for review diffs (#722)

* 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

* docs: resolve five contract contradictions across AGENTS.md, README, and skills (#736)

* docs: resolve five contract contradictions

* no-mistakes(test): align owner-pointer assertions with reworded docs; skip absent shellcheck

* docs(harness): correct Grok exit guidance (#742)

* docs(harness): reverify grok exit command

* no-mistakes(test): Correct Grok exit resume attribution

* fix(watcher): bound stale wakes for parked crew (#743)

* 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

* fix(supervision): distinguish ordinary wakes from recovery (#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 (#745)

* 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

* feat(wake): enrich drained signals with bounded status context (#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 (#784)

* 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

* fix(send): treat opencode busy-queued composer state as submitted (#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

* fix(spawn): require two stable reads before accepting worktree path (#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>

* fix(bin): make watcher process identity immune to Linux wall-clock changes (#752)

* fix(watcher): stabilize Linux process identity

* no-mistakes(document): document FM_PROC_ROOT_OVERRIDE and Linux starttime identity rationale

* fix: prevent AFK idle stalls and stale run attribution (#758)

* 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(bin): allow safe teardown during watcher recovery (#750)

* 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

* feat(herdr): order presentation spaces while preserving focus (#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

* fix(bin): send literal config reread nudges after pushes (#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 (#797)

* 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

* fix(herdr): group projected children beneath owning parents (#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: keep local no-mistakes tests intent-targeted (#823)

* 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: add canonical timed test runner (#825)

* 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: surface main inventory gaps in Bearings (#830)

* 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 bounded concurrent test isolation proof (#832)

* 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: guard against missed secondmate reports (#834)

* 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: require pinned real-Herdr CI coverage (#838)

* 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

* feat: shard portable tests and add bounded local parallelism (#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

* fix: block primary-session delegation outside the fleet (#854)

* 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

* fix: install tasks-axi in portable CI shards (#866)

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(bin): make dispatch profiles quota aware (#867)

* 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

* Add built-in ahoy recap skill (#873)

* fix: preserve trustworthy Bearings data in partial snapshots (#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

* feat(pi): add session-local calm mode (#884)

* 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): prevent redundant watcher re-arms (#885)

* 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 (#895)

* 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 (#898)

* fix: execute every PR body compliance event

* no-mistakes(document): Document independent PR compliance events

* fix: exclude operational injections from ahoy boundaries (#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

* fix: canonically classify operational inputs across harnesses (#909)

* fix: type canonical operational inputs

* no-mistakes(document): Correct canonical operational-input documentation ownership

* fix: avoid generic secondmate start acknowledgements (#926)

* fix: avoid generic secondmate acknowledgements

* no-mistakes(document): Document sparse secondmate acknowledgement behavior

* no-mistakes: apply CI fixes

* fix(pi): make calm mode persistent and gapless (#927)

* fix(pi): preserve calm presentation across sessions

* no-mistakes(review): Fix Calm home fallback persistence

* no-mistakes(document): Clarify Calm gapless and export contracts

* no-mistakes: apply CI fixes

* fix(watch): retire merged PR polls after durable notification (#932)

* fix: retire merged PR polls after notification

* no-mistakes(review): Decouple PR retirement recovery from template updates

* no-mistakes(review): Recover pending PR retirements before poll migration

* no-mistakes(document): Document merged PR poll retirement contracts

* fix: refine scout intake and parallel dispatch (#934)

* Clarify intake evidence and overlap handling

* no-mistakes(review): Align scout guard with intake classification

* no-mistakes(document): Clarify scout documentation and intake ownership

* fix(pi): prevent duplicate assistant replies in Calm (#936)

* fix(pi): preserve operational follow-up semantics in Calm

* no-mistakes(document): Correct Calm operational-row visibility documentation

* perf(bin): shrink the ShellCheck source graph (#939)

* perf(lint): shrink shell source graph

* no-mistakes(review): Ensure lint workers terminate fully on cancellation

* no-mistakes(document): Repair stale lint documentation ownership

* fix(pi): remove Calm hidden-block gaps (#942)

* fix(pi): remove Calm hidden-block gaps

* no-mistakes(review): Validate Calm geometry against current viewport

* no-mistakes(review): Synchronize Calm geometry checks with reload completion

* fix: enforce contract boundaries for ask-user findings (#945)

* fix: escalate ask-user contract expansion

* no-mistakes(document): Point project management to authority owner

* docs: prefer direct operational paths (#946)

* fix(pi): hide operational user rows in Calm mode (#948)

* fix(pi): hide Calm operational user rows

* no-mistakes(review): Narrow Calm operational input suppression

* no-mistakes(review): Avoid Calm replay classifier subprocesses

* no-mistakes(document): docs: point Pi verification to Calm owner

* fix: relaunch missing second mates at session start (#950)

* fix(session-start): relaunch missing second mates

* fix(test): detect completed parallel workers

* no-mistakes(review): Isolate session-start recovery test cleanup

* no-mistakes(review): Complete backend-safe secondmate session recovery

* no-mistakes(review): Resolve Zellij task ownership before recovery

* no-mistakes(review): Recover relocated Zellij ghost tabs safely

* no-mistakes(review): Restore conservative Zellij recovery boundary

* no-mistakes(review): Reject malformed tmux recovery targets

* no-mistakes(document): Align secondmate recovery documentation

* no-mistakes: apply CI fixes

* fix(herdr): reclaim resumed task projections after restart (#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

* Teach Ahoy to surface open decisions (#968)

* Require shipshape routine acknowledgement (#969)

* docs: separate current guidance from verification evidence (#994)

* docs: separate current guides from verification

* no-mistakes(review): Restore Herdr 0.7.5 restart-reclaim verification evidence

* fix: preserve Claude watcher continuity across Stop hooks (#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

* fix(herdr): clean stale projections at session start (#996)

* 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

* fix: recover Claude supervision without watcher-status gate (#1001)

* fix: recover Claude supervision at session start

* fix: remove Claude watcher-status command gate

* no-mistakes(document): docs: remove stale continuity gate references

* fix: make quota-aware profile selection agent-owned (#1018)

* Replace quota dispatch selector instructions

* no-mistakes(review): Align bootstrap docs with agent-owned dispatch selection

* fix(bin): remove vestigial dispatch selector (#1026)

* 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

* docs(agents): drop superseded interim quota-window rule (#1039)

quota-axi 0.1.13 emits schemaVersion 2 with a quotaSemantics object per
provider, so the successor named in the interim rule has landed and the
rule's own removal condition is satisfied.

Keep the ownership clause so quota-axi remains the single owner of how
model or product windows relate to bounding account windows, and drop
the interim weakest-headroom instruction. The unknown-semantics case is
already covered by the existing requirement to stop and report a
candidate whose applicable quota data or interpretation cannot be
established.

Drop the matching assertion phrase from
tests/fm-instruction-owners.test.sh; the retained ownership phrase still
asserts.

* fix(tmux): scope busy detection and recognize current Claude turns (#1049)

* fix(tmux): scope Claude busy detection by harness

* no-mistakes(review): Separate verified and fallback busy signatures

* no-mistakes(test): Scope busy signatures to supplied harnesses

* no-mistakes(document): Document harness-scoped busy detection

* feat: add verified Kimi crewmate adapter (#1047)

* Add verified Kimi crewmate harness adapter

* no-mistakes(review): Scope Kimi moon detection to spinner lines

* no-mistakes(review): Match only complete Kimi spinner rows

* no-mistakes(review): Resolve Kimi binary portably before pane creation

* no-mistakes(document): Align Kimi adapter documentation

* no-mistakes(lint): Suppress false-positive ShellCheck warning for sourced watcher override

* Fix Kimi busy spinner detection

* no-mistakes(review): Recognize Kimi session-lock ancestry and holders

* no-mistakes(review): Scope pending-reply Kimi busy detection by harness

* no-mistakes(document): Correct Kimi spinner capture documentation

* no-mistakes(document): Clarify optional Kimi spinner whitespace

* no-mistakes(lint): Silence intentional pending-reply test stub warnings

* test: align rebased Kimi busy fixtures

* no-mistakes: apply CI fixes

* Reconcile Kimi busy detection after per-harness scoping

* no-mistakes(review): Clarify observed Kimi spinner whitespace contract

* no-mistakes(document): Clarify Kimi harness documentation

* fix: harden Kimi submission and spinner matching (#1058)

* fix kimi pointer submission and spinner conformance

* no-mistakes(review): Preserve Kimi submit target ownership guard

* feat(bin): add guarded Kimi turn-end wake (#1059)

* Add guarded Kimi turn-end hook

* no-mistakes(review): Require jq before installing Kimi turn-end hook

* no-mistakes(review): Expose jq inside isolated Kimi test fixtures

* no-mistakes(review): Preserve Kimi config boundaries during hook removal

* no-mistakes(review): Document Kimi removal newline safeguard

* no-mistakes(document): Document Kimi shared-home preservation

* fix(tmux): classify bordered composers across all rows (#1066)

* Fix structural tmux composer reading

* Verify Calm compatibility with Pi 0.82

* no-mistakes(review): Harden structural composer classification boundaries

* no-mistakes(review): Refresh composer and Kimi regression fixtures

* no-mistakes(review): Fail closed on unbounded composer edges

* no-mistakes(review): Enforce aligned composer geometry safely

* no-mistakes(review): Make composer ambiguity locale-safe

* no-mistakes(review): Preserve ambiguity through composer submission

* no-mistakes(review): Carry composer proof through retries

* no-mistakes(document): Document structural tmux composer delivery guarantees

* no-mistakes: apply CI fixes

* feat(bin): add verified pi-signed runtime adapter (#1145)

* feat: add verified pi-signed adapter

* no-mistakes(review): Correct pi-signed maintainer verification date

* no-mistakes(review): Correct remaining pi-signed verification dates

* no-mistakes(review): Preserve authoritative pi-signed runtime identity

* no-mistakes(document): Document pi-signed shared adapter semantics

* no-mistakes: apply CI fixes

* fix(pi): rearm watcher across session transitions (#1166)

* fix(pi): rearm watcher across same-process session transitions

Pi emits session_shutdown for ordinary /new, /resume, and /fork replacement
as well as terminal quit. The primary watcher extension latched a module-level
stopping flag on every shutdown, so a replacement session in the same process
could not arm monitoring until Pi restarted.

Own arm authority per session generation so only the active live generation
may start, stop, or rearm the child. Replacement sessions can arm again without
restarting Pi, stale prior-generation callbacks cannot mutate the active cycle,
and real quit still blocks late rearm.

* no-mistakes(review): Preserve Pi generation isolation and exit cleanup

* no-mistakes(document): Correct Pi watcher transition documentation

* feat: route crew dispatch using quota-window pace (#1172)

* Consume quota-axi pace signals in dispatch profile array selection.

Add quota-array-dispatch as the single owner of the pace-aware candidate
choice, keep AGENTS.md to the intake boundary and load trigger, and cover
the acceptance cases with sanitized schemaVersion 3 fixtures.

* no-mistakes(review): Stop and report genuine quota dispatch ties

* no-mistakes(document): Document quota pace freshness and uncertainty

* fix: adapt Grok Stop continuation and harden endpoint cleanup (#1171)

* fix(grok): adapt Stop continuation to runtime capability

* no-mistakes(review): Reject ambiguous Grok Stop payloads

* no-mistakes(review): Reject duplicate Grok fields and accept spaced tmux sessions

* no-mistakes(review): Enforce exact tmux cleanup selectors

* no-mistakes(test): Fix historical tmux fixture and validate Grok Stop

* no-mistakes: apply CI fixes

* fix: restore stock macOS Bash 3.2 brief scaffolding (#1093)

* fix(brief): make DOD scaffolding parse-safe on stock macOS Bash 3.2

fm-brief.sh built each Definition-of-done block and the not-enabled
Herdr declaration with `VAR=$(cat <<EOF ... EOF)`. On Bash 3.2 (macOS
/bin/bash) the lexer scans for the command substitution's closing `)`
textually and tracks quote state through the heredoc body, so a single
apostrophe, unbalanced quote, or unbalanced paren in that prose breaks
parsing of the whole script. Every ship-brief scaffold (no-mistakes,
direct-PR, local-only) failed with `unexpected EOF while looking for
matching )`. Bash 4+ parses it fine, so the breakage stayed invisible
everywhere except stock macOS.

Replace all four command-substitution heredocs with
`IFS= read -r -d '' VAR <<EOF || true`. That removes the `$(...)`
wrapper and the entire defect class regardless of future prose, and
preserves the variable expansion the direct-PR and local-only bodies
need. `read` keeps the heredoc's trailing newline that `$(...)` used to
strip, so trim one newline to keep every generated brief byte-identical
to prior output.

Guard the structure, not one historical phrase: a new test rejects any
heredoc nested in a command substitution anywhere in fm-brief.sh, where
the old assertion pinned a single apostrophe phrase and so missed the
reintroduction. Extend the stock-macOS Bash CI job from parsing one
script to the whole maintained shell surface (bin/*.sh,
bin/backends/*.sh, tests/*.sh), matching bin/fm-lint.sh's canonical file
set so parse scope and lint scope cannot drift apart.

* no-mistakes(review): Captain: harden Bash structure and inventory guards

* no-mistakes(document): Align stock macOS Bash contributor checks

* no-mistakes(lint): Suppress deliberate SC2016 literal fixture warnings

* test: stabilize tmux teardown conformance baseline (#1209)

* fix(test): pin teardown tmux baseline to historical kill selectors

merge-base HEAD main collapses to HEAD after the exact-selector change
lands on the default branch, so the old teardown fixture was accidentally
exercising current exact targets. Resolve a content-historical permissive
tmux adapter from first-parent history and force that post-squash topology
inside the conformance case so main and feature branches keep the same
old-vs-new contract.

* no-mistakes(lint): Suppress intentional literal-pattern ShellCheck warnings

* docs: slim quota-array-dispatch to the pace selection core (#1197)

Cut the runtime skill to the compact pace-aware selection procedure plus
minimum owner pointers. Keep every distinct decision rule and move expanded
acceptance scenarios to deterministic fixture ownership assertions.

Size: 170/1374/10187 -> 63/544/4068 (about 63%/60%/60% reduction).

* feat(bin): inherit backend config into secondmate homes (#1219)

* Inherit config/backend into secondmate homes with deliberate-override preservation

Add backend to the shared inheritable config allowlist so launch, locked
bootstrap, and config-push converge a primary pin into secondmate homes as each
home local future-spawn default. Track last-inherited bytes in a private state
provenance marker so deliberate per-home overrides survive present and absent
primary convergence, keep --backend and FM_BACKEND stronger, and extend the
existing inheritance tests plus docs and skill claims.

* no-mistakes(review): Preserve equal unprovenanced backend overrides

* no-mistakes(review): Preserve symlink overrides and verify spawn precedence

* no-mistakes(review): Snapshot backend inheritance for consistent provenance

* no-mistakes(review): Simplify backend inheritance to primary-authoritative convergence

* no-mistakes(document): Document inherited backend override preservation

* fix: restore primary-authoritative backend inheritance after document regression

The document step reintroduced provenance and deliberate per-home override
semantics after review had simplified config/backend to plain primary-authoritative
allowlist membership. Restore the primary-always-wins path: present overwrites,
absent removes, no provenance marker, and docs/tests match that contract.

* no-mistakes(review): Add divergent backend precedence regression fixtures

* no-mistakes(document): Document backend inheritance contract

* fix(pi): remove Calm's upper version ceiling (#1226)

* fix(pi): remove Calm's exclusive Pi upper-version ceiling

tests/fm-calm-pi-extension.test.sh gated on a closed PI_COMPAT_VERSIONS
allowlist ("0.81.1 0.82.0") that refused any other installed Pi, and docs
described that range as "supported" rather than verified evidence. The
Calm CHANGELOG shows no API introduced at either version, so there is no
evidence for a real minimum; the presentation adapters already probe the
exact method they patch rather than checking a version.

Replace the allowlist with dated version evidence that never rejects a
newer Pi, and make each presentation adapter degrade independently with
a diagnostic if a future Pi removes its API, instead of the whole Calm
extension failing to load. Rewrite the feasibility doc's "Pi 0.81.1
through 0.82.0" phrasing to state it as verified evidence, not a
ceiling.

* no-mistakes(review): Probe missing Calm adapter exports safely

* no-mistakes(document): Document Calm's unbounded Pi compatibility

* fix(bin): allow session-local todo tools in the subagent guard (#1204)

* fix(guard): allow session-local todo tools in the primary

The delegation-shape guard denied TaskCreate and TaskUpdate because their
normalized names contain the `task` stem. Those tools write only the harness's
session-local todo list, which has no executor: it spawns no agent, allocates
no worktree, registers no schedule, and starts nothing that outlives the
session. That is not the unaccounted work the guard exists to stop, so the stem
match was a false positive, and the deny text told the primary to run
bin/fm-brief.sh and bin/fm-spawn.sh to create a todo entry.

Add a separately-reasoned PLAN_ONLY_TOOLS exact-name exclusion rather than
widening OBSERVE_ONLY_TOOLS, whose documented contract is tools that only
observe or stop existing work. Both lists stay exact-name so neither can widen
by substring.

Tests cover the two allowed names and six near-miss names that a substring or
shortened-stem widening would release; both mutations were watched red.

* no-mistakes(review): drop session-local todo tools from recommended deny list

* no-mistakes: apply CI fixes

* fix(session-lock): resolve Claude bg-spare ancestry to the outermost claude pid (#1206)

* fix(session-lock): resolve Claude bg-spare ancestry to the outermost claude pid

fm_harness_ancestry_pid() previously returned the first ancestor process
whose command matched a verified harness name. Claude Code's Stop hook
fires as a bg-spare worker several levels below the session's actual
lock-owning claude process (hook shell -> claude bg-spare ->
claude bg-pty-host -> claude -> claude(lock)), so the first match was
the bg-spare worker, not the lock owner. fm_session_lock_owned_by_self()
then never matched state/.lock, and the Claude Stop auto-arm silently
treated its own primary session as an unrelated live owner and never
armed the watcher.

The walk now keeps going past a claude-named match, looking for a still
more ancestral claude-named match, and stops the instant a non-match
follows an already-found match (bounding it to a contiguous run rather
than the literal ancestry top, so an unrelated claude-named process
further up the real process tree is never mistaken for part of this
session's own nested chain). Every other harness keeps the original
first-match-wins behavior, since e.g. Pi's shared signed-wrapper
ancestry actually holds the session at the inner engine pid, not an
outer wrapper pid. Hop limit raised from 8 to 16 to cover the deeper
bg-spare chain.

* no-mistakes(review): Add nested-claude-ancestry regression test; fix nudge doc depth claim

* no-mistakes: apply CI fixes

* fix: conferma l'avvio del watcher su Windows/MSYS (#1212)

* fix: confirm watcher startup on MSYS

* no-mistakes(review): gate MSYS arm ready timeout, cache uname, harden locale test

* no-mistakes(review): validate OpenCode ready timeout, make uname cache internal

* fix(spawn): forward CLAUDE_CONFIG_DIR to claude crewmates (#1195)

* fix(spawn): forward firstmate's CLAUDE_CONFIG_DIR to claude crewmates

Crewmate panes are created by a long-lived tmux/herdr daemon that does not
inherit firstmate's current environment. When firstmate runs under a non-default
CLAUDE_CONFIG_DIR (for example a work-vs-personal subscription split), a bare
`claude` in the crewmate pane fell back to the default ~/.claude store and
launched unauthenticated, blocking the crewmate before it could do any work.

fm-spawn now prefixes the claude launch with firstmate's own resolved
CLAUDE_CONFIG_DIR when set, so the crewmate uses the same credential/config
store firstmate is authenticated with. An unset value is the single-store
default and adds no prefix; non-claude harnesses are unaffected.

Adds three tests in fm-spawn-dispatch-profile.test.sh (forwarded-when-set,
omitted-when-unset, non-claude-ignored) and pins CLAUDE_CONFIG_DIR in the test
helper so launch assertions no longer depend on the developer's environment.

* no-mistakes: apply CI fixes

* fix: preserve dispatch identity across authentication checks (#1233)

* fix: preserve dispatch harness identity

* no-mistakes(review): Fix Grok counterfactual tuple validation

* no-mistakes(document): Scope dispatch authentication to selected tuple

* fix: restore dispatch instruction budget

* no-mistakes(review): Scope dispatch authentication after candidate selection

* fix(bin): normalize relative durable paths (#1256)

* fix(bin): handle dash-leading harness process names (#2)

* fix: handle dash-leading harness process names

* no-mistakes(review): Make dash-leading harness regression hermetic

* fix: preserve secondmate reply routes across relative homes

Resolve relative home, data, and state inputs before durable charter generation, and fail when caller-relative directories cannot be resolved.

Use absolute paths at the related spawn, AFK daemon, and X-mode cross-process handoffs so later processes cannot reinterpret them from another working directory.

* no-mistakes(review): Preserve absolute overrides and normalize relative durable paths

* no-mistakes(review): Normalize relative home before deriving durable paths

* no-mistakes(document): Document relative durable-path normalization

* no-mistakes(review): Captain: Ignore inherited CDPATH during relative path normalization

* no-mistakes(lint): Fix empty CDPATH assignments for ShellCheck

* refactor(skills): make Bearings chat-only by default (#1136)

* Add internal status skill

* no-mistakes(document): register /status skill in documentation-audiences inventory

* no-mistakes(lint): replace grep|wc -l with grep -c in status skill test

* test: silence literal status skill patterns

* Refactor bearings default to chat-only

---------

Co-authored-by: Kun Chen <3233006+kunchenguid@users.noreply.github.com>

* Clarify follow-up routing during validation (#1277)

* fix: honor concrete approval for project operations (#1272)

* docs: add captain-approved project operation exception to hard rule 1

Firstmate stays read-only over projects by default, but when the captain
clearly approves a concrete project operation and scope in the moment,
firstmate may perform exactly that approved operation with its own tools.
The approval is never inferred, broadened, or standing, and it does not
relax the existing force, discard, unlanded-work, or merge-authority
boundaries.

* no-mistakes(review): Clarify captain-approved project operation boundaries

* no-mistakes(document): Clarify captain-approved project operation scope

* docs: cover directories and preserve the operation-or-scope alternative

Widen the captain-approved project operation exception in AGENTS.md to
files or directories, and restore the explicit operation-or-scope
alternative that a prior pipeline auto-fix had collapsed into "and".

Rework project-management SKILL.md's Remove section, which previously
told firstmate to refuse project removal until a guarded helper existed;
that helper was never built, so the text directly contradicted the new
instruction-only exception. It now points at the exception plus the
existing removal preflight it still requires unchanged.

Update the one instruction-owners test assertion that hard-coded the
sentence removed above, so the suite tracks current, not obsolete, text.

* docs: add captain-approved project operation exception to hard rule 1

Firstmate stays read-only over projects by default, but when the captain
clearly approves a concrete project operation and scope in the moment,
firstmate may perform exactly that approved operation with its own tools.
The approval is never inferred, broadened, or standing, and it does not
relax the existing force, discard, unlanded-work, or merge-authority
boundaries.

* no-mistakes(review): Clarify captain-approved project operation boundaries

* no-mistakes(document): Clarify captain-approved project operation scope

* docs: cover directories and preserve the operation-or-scope alternative

Widen the captain-approved project operation exception in AGENTS.md to
files or directories, and restore the explicit operation-or-scope
alternative that a prior pipeline auto-fix had collapsed into "and".

Rework project-management SKILL.md's Remove section, which previously
told firstmate to refuse project removal until a guarded helper existed;
that helper was never built, so the text directly contradicted the new
instruction-only exception. It now points at the exception plus the
existing removal preflight it still requires unchanged.

Update the one instruction-owners test assertion that hard-coded the
sentence removed above, so the suite tracks current, not obsolete, text.

* no-mistakes(review): Align project removal preflight with approved exception

* no-mistakes(document): Align project removal documentation with approved exception

* fix: restore removal test byte-for-byte and preserve the default sentence

tests/fm-instruction-owners.test.sh had been changed to assert different
text; restore it byte-for-byte to origin/main. project-management SKILL.md's
Remove section now keeps the exact default "Never issue a raw removal
command from Firstmate." sentence that test still asserts, immediately
followed by the already-approved captain-operation-or-scope exception, so
the default and the exception both stay explicit and consistent.

* no-mistakes(document): Align project-write boundary documentation

* fix(skills): route new project intake through secondmate scopes (#1275)

* Route project intake through secondmate scopes

* no-mistakes(test): Guard all main-home project registry mutations

* no-mistakes(document): Consolidate secondmate routing documentation

* no-mistakes: apply CI fixes

* Restore new-project routing scope

* no-mistakes(document): Clarify secondmate routing for new-project intake

* no-mistakes: apply CI fixes

* fix: scope validation corrections by accepted behavior (#1281)

* fix: scope validation corrections by accepted behavior

* no-mistakes(review): Classify stale delivery evidence as an autonomous correction

* test: replace source assertions with behavioral coverage (#1282)

* test: remove source-content assertions

* no-mistakes(review): Replace source assertions with runtime behavior coverage

* no-mistakes(review): Isolate Kimi task temp runtime coverage

* no-mistakes(document): Refresh test cleanup documentation

* no-mistakes: apply CI fixes

* fix(watch): escalate busy workers with no completed turn (#1286)

* fix(watch): bound how long a busy pane may run with no completed turn

A busy pane (backend busy state or the harness's rendered footer) was
unconditional, unbounded proof of liveness in every escalation path, so a
hung foreground tool call behind a busy signature could run for hours
undetected (2026-07 hibit-agent-focus-nonsteal-r1 incident: a catastrophic-
backtracking regex hung one bash call for 25h behind an unchanging
"Working..." footer).

FM_BUSY_TURN_MAX_SECS (default 3600s) now bounds how long a busy pane may
run with no completed turn (state/<id>.turn-ended, or its spawn record
before any turn has completed). Past the bound, busy_turn_over_age routes
the pane through the existing wedge_timer_check, reusing the identical
stale reason, escalation counter, and demand-deep-inspection marker for
human inspection only - never an automatic interrupt, signal, or restart
of the worker or its tool process. A completed turn resets the age.

Reproduced end-to-end against the real installed Pi TUI: a foreground
`sleep 999999` bash call with no timeout renders the actual busy footer,
and two captures ~15s apart show the elapsed counter changing the pane
hash while the same turn stays unfinished. Running the pre-fix watcher
against the real captures showed it never starts a wedge timer no matter
how long the pane stays busy; the fixed watcher starts and escalates the
timer through the same mechanism, while the real hung process remained
untouched and alive throughout.

* no-mistakes(review): fix: parse enriched AFK stale reasons

* no-mistakes(review): fix: preserve enriched wedges during AFK supervision

* no-mistakes(review): fix: route all enriched AFK wedges

* no-mistakes(document): Clarify busy-turn age supervision documentation

* fix(gitignore): ignore config/ as a directory, not by exact filename (#1261)

A name-by-name list of config/ entries silently stops ignoring any new or
home-local file placed there, which makes the working tree read as dirty and
blocks guarded sync paths that refuse to touch a dirty home. AGENTS.md
already documents config/ as captain-private and gitignored as a category;
this makes .gitignore match that contract.

* fix(tests): replace source-content .gitignore assertion with behavioral coverage (#1304)

The second assertion in fm-gitignore-config.test.sh (added by #1261) greps
.gitignore for a specific spelling of the config/ ignore pattern. It fails
on a semantically equivalent pattern like config/** and does not prove Git
actually ignores anything, per the completed source-content-test audit.

Replace it with a real git check-ignore control test on a generated
unrelated path, and strengthen the existing directory-coverage test with
generated unpredictable direct and nested config/ paths.

* feat: bound and consolidate startup memory during stow (#1303)

* Add bounded startup memory curation

* no-mistakes(review): Record reproducible stow verification evidence

* no-mistakes(review): Validate inherited secondmate stow evidence

* no-mistakes(document): Document editable startup-memory budget propagation

* fix(herdr): place workers in the launching workspace (#1328)

* fix(herdr): place workers in the launching agent's exact workspace

Herdr enforces no workspace-label uniqueness, and spawn resolved its
container by taking the FIRST workspace whose label matched the home
label. With two workspaces both labeled "firstmate", a worker launched
from the second one was created in the first, so it appeared in a
different space than the Firstmate the captain was watching.

Reproduced end to end on Herdr 0.7.5 protocol 17 by running the real
bin/fm-spawn.sh inside a launcher pane in the second "firstmate"
workspace: the worker landed in w1 while its launcher was in w2, with an
unrelated third workspace focused throughout, which also rules out any
dependence on the focused workspace.

Placement now binds to the launching process's own Herdr identity. Herdr
injects HERDR_PANE_ID, HERDR_SESSION, and HERDR_SOCKET_PATH into every
process it manages a pane for, and fm_backend_herdr_launcher_identity
resolves that pane's current owning tab and workspace live from Herdr,
cross-checking the pane against its tab and confirming the workspace
exists exactly once in the session. The injected HERDR_TAB_ID and
HERDR_WORKSPACE_ID are creation-time snapshots and are deliberately not
read as current identity. Labels are no longer placement authority.

A claimed parent identity that is unreadable, contradictory, stale, or
from another named session or Herdr server stops the spawn before any
worker endpoint exists, rather than degrading to a label search. A
launcher with no Herdr ancestry has no workspace to inherit and keeps
the per-home labeled container, which must now resolve to exactly one
workspace; two same-labeled candidates refuse instead of adopting
either. A --secondmate launch keeps standing up that home's own
workspace by design.

With presentation spaces enabled, the projected child is created and
bound under that same exact parent and anchors its ordering on it, so a
duplicated home label no longer makes the layout ambiguous. Projection,
focus restoration, restart binding, and quarantine rules are unchanged,
and children are never collapsed into the parent. tmux, Zellij, cmux,
Orca, and the away-mode daemon terminal were each inspected and are not
affected: none resolves a container by searching mutable labels.

tests/fm-backend-herdr-launcher-workspace-e2e.test.sh drives the real
spawn and teardown against an isolated Herdr lab, with its headline case
running fm-spawn.sh inside a real Herdr pane so the identity comes from
Herdr's own injection. The refusal matrix and the ordering anchor are
covered deterministically in tests/fm-backend-herdr.test.sh.

Eight existing real-Herdr suites inherited the developer terminal's own
Herdr pane into their isolated lab sessions, which the new cross-session
check correctly refuses. tests/herdr-test-safety.sh now owns
herdr_forget_inherited_pane and those suites call it, so what they assert
no longer depends on where they were launched from.

Two unrelated fixes found along the way. tests/fm-secondmate-harness.test.sh
had the same class of environment leak through CLAUDECODE, which outranks
PI_CODING_AGENT in bin/fm-harness.sh and made its pi-signed ancestry case
resolve "claude" whenever the suite ran inside Claude Code. And
fm-spawn.sh's usage() printed a fixed line range that had already been
truncating its own help mid-sentence.

* no-mistakes(review): Enforce exact Herdr launcher and projection identity

* no-mistakes(document): Document exact Herdr launcher workspace placement

* fix(calm): refine Calm working boat animation (#1339)

* feat(calm): replace Pi's working row with an animated ship while Calm is on

While Calm is active and one logical agent run is under way, Calm now hides
Pi's built-in working row and renders a small two-row SSHHIP-derived boat in
its place. When Calm is off, Pi's stock working row is left untouched.

The presentation uses only public Pi extension API: setWorkingVisible(false)
plus a temporary setWidget() component whose render(width) owns the responsive
geometry and whose timer requests a TUI render. Visibility follows agent_start
through agent_settled, so the boat does not flicker between tool calls,
automatic continuations, retries, or compaction inside the same run, and
settle, abort, and failure all reach the same cleanup.

fm-calm.ts stays the sole owner of the presentation choice and the only caller
of setWorkingVisible(); the new lib owns the sprite geometry and widget.

* no-mistakes(review): Guarded Calm-off lifecycle visibility writes; focused tests pass

* no-mistakes(test): Fixed Calm E2E wait to include tmux scrollback

* no-mistakes(document): Document Calm working boat behavior

* no-mistakes: apply CI fixes

* feat(calm): slow the Calm boat, animate blue water, and make the sail directional

The boat now moves one column every 880ms while a bounded fixed-cell water phase
advances every 220ms, so the water ripples several times between boat steps and
the presentation reads as calm. One scheduler drives both clocks and disposing
the widget stops them together; ticks rather than wall-clock timestamps drive
every state change, so tests seek animation time exactly.

Colors are standard ANSI foreground codes instead of theme lookups: blue for
every water cell and yellow for the complete boat, each run closed with a
default-foreground reset so nothing bleeds into padding or later frames. ANSI
bytes never enter geometry, so visible width stays exact.

The mainsail is directional and trails aft of the mast: <| travelling right and
|> travelling left. Direction reverses the moment the boat lands on an endpoint,
so the endpoint frame already shows the new heading and no frame at or after a
bounce shows the previous sail.

* test(calm): wait for the Ctrl+O expansion redraw this block asserts

* docs(calm): record the revised working-presentation verification evidence

* no-mistakes(document): Fix Calm feasibility document EOF whitespace

* fix(dispatch): preflight candidate auth before quota escalation (#1349)

* fix(dispatch): scope candidate authentication to its own surface

A locally expired timestamp in one credential store was reported to the
captain as a sign-out, including for dispatch candidates that never read
that store. A `harness=pi, model=xai/grok-*` candidate authenticates
through Pi's own xAI credential, but the only Grok quota reading
available was gated on the standalone Grok CLI's separate token, whose
expiry clock drifts independently. The always-loaded intake rule then
turned that unreadable quota into a mandatory captain escalation.

Add `bin/fm-auth-preflight.sh` as the deterministic owner of the parts
that must not depend on agent memory: it resolves a tuple's
authentication surface from quota-axi's own emitted auth sources rather
than from a harness or model name, so another harness's CLI can never
gate a candidate that does not use it. A vendor CLI is launched only
when the tuple's own harness owns the credential store under test and a
non-destructive discovery command is registered for it, which today is
`grok models` alone. That probe runs at most once with stdin closed and
a hard timeout, reads its verdict from the first stdout line because the
command exits 0 either way, treats unrecognized output as indeterminate,
and never invokes login, logout, or the interactive TUI. Quota is read
at most twice, and unknown headroom never makes a candidate ineligible
on its own.

Update the dispatch procedure to match: usable authentication with
unmeasurable headroom stays eligible at lower preference with the
unknown disclosed, and stop-and-report is reserved for unresolved
authentication, an unresolved relationship, or malformed configuration.
Record that Grok's `credits.remaining` is a prepaid balance rather than
window headroom.

Gate quota-axi at 0.1.16 in bootstrap, the first build reporting
per-credential auth sources. A stale install previously passed the
presence check silently, which is why a fix published two days earlier
was still not in effect.

Replace the orphaned quota-array-dispatch fixtures, which encoded a
`provider: "xai"` shape the tool never emits and had no consumer, with
fixtures shaped like real 0.1.16 output that the new suite drives the
script against. The suite asserts the verdict and, separately, which
vendor CLIs were launched, so a Pi/xAI candidate reaching the Grok CLI
fails. Map `tests/fixtures/<dir>` to its consuming suite so a fixture
change selects the right t…
7hoenix added a commit to 7hoenix/firstmate that referenced this pull request Aug 27, 2026
…signing (#19)

* fix: preserve secondmate routing markers in terminal sends (#533)

* fix: preserve secondmate routing markers

* no-mistakes(review): Captain, preserve trailing newlines in marked secondmate sends

* no-mistakes(test): Captain, tolerate bootstrap timeout elapsed drift

* no-mistakes(document): Refresh Herdr marker documentation

* fix: align Grok effort handling with 0.2.99 (#527)

* fix: align grok effort docs and spawn with 0.2.99 ceiling

grok 0.2.99 accepts only low|medium|high for --reasoning-effort and
rejects both xhigh and max. Omit unsupported values on spawn, flag them
in crew-dispatch validation, and update harness-adapters.

* no-mistakes(test): Captain: refresh gotmp teardown fixture dependencies

* no-mistakes(document): Clarify Grok effort documentation ownership

* fix: derive bearings from authoritative secondmate state (#555)

* fix: make bearings use secondmate home state

* test: anonymize bearings fixtures

* no-mistakes(review): Bound parent activity evidence scans, captain

* no-mistakes(review): Preserve structured secondmate authority and bounds, captain

* no-mistakes(review): Preserve registry completeness and child inventory, captain

* no-mistakes(review): Reconcile parent evidence by verb and key, captain

* no-mistakes(review): Treat unkeyed parent evidence as inconclusive, captain

* no-mistakes(document): Document bearings local snapshot and PR opt-in

* no-mistakes(lint): Fix fleet snapshot ShellCheck findings

* no-mistakes: apply CI fixes

* fix: restore fleet snapshots on stock macOS Bash (#578)

* fix: restore stock macOS snapshot parsing

* no-mistakes(document): Clarify Linux gate and macOS CI coverage

* fix(afk): make Pi escalation and return catch-up reliable (#587)

* fix: close away-mode blocker supervision gap

* no-mistakes(review): Gate teardown retries and verify U+2063 dedupe

* no-mistakes(test): Fail closed on incomplete Pi composer separators

* no-mistakes(document): Document Pi composer recognition and return gating

* feat: support Pi max reasoning profiles (#537)

* support Pi max thinking profiles

* no-mistakes(review): Captain, allow Pi max dispatch profiles

* chore: no-mistakes(document): Clarify yolo response ownership (#595)

* Clarify validation response ownership

* no-mistakes(document): Clarify yolo response ownership

* feat: establish instruction ownership foundation (#619)

* Add instruction owners foundation

* no-mistakes(document): Refresh project-management owner pointers

* fix: compress Firstmate contract and enforce delivery rigor ownership (#626)

* docs: compress firstmate operating contract

* docs: make delivery rigor single-owner

PR B already removed personal and stacked review requirements, but it did not explicitly assign rigor to the selected delivery path or forbid risk-based manual clean gates. That gap still permitted the Hi Bit inversion.

* no-mistakes(review): Honor configured merge authority across faster delivery paths

* no-mistakes(document): Align docs with compressed operating contract

* feat: add durable captain decision holds (#593)

* Add durable captain decision holds

* no-mistakes(review): Validate decision hold retries and origin paths

* no-mistakes(review): Enforce durable decision lifecycle boundaries

* no-mistakes(review): Harden decision display and partial retry recovery

* no-mistakes(test): Update scout teardown fixtures for decision inventory

* no-mistakes(document): Align decision lifecycle and scout teardown documentation

* no-mistakes: apply CI fixes

* no-mistakes(review): Reconcile terminal decision holds

* no-mistakes(document): Align captain decision-hold documentation

* fix(bin): harden PR check artifacts (#556)

* fix: harden PR check artifacts

* fix: close PR check migration gaps

* fix: close PR check retry gaps

* fix: clarify migration outcomes and ESM boundary

* fix: keep failed migrations authoritative

* test: use inert PR validation fixtures

* no-mistakes(review): Reserve noncanonical PR quarantine namespace

* no-mistakes(review): Prevalidate final PR-check teardown artifacts

* no-mistakes(review): Preserve X metadata and validate teardown IDs

* no-mistakes(review): Initialize migration state before watcher exclusion

* no-mistakes(review): Isolate failed poll migrations from bootstrap recovery

* no-mistakes(review): Allow safe polling during incomplete private repairs

* no-mistakes(review): Authenticate watcher checks at execution time

* no-mistakes(review): Preserve custom checks with hash-bound registration

* no-mistakes(review): Clean custom check snapshots on watcher signals

* no-mistakes(review): Stop watcher checks promptly on signals

* no-mistakes(review): Terminate watcher check groups before cleanup

* no-mistakes(document): Correct stale X-mode watcher documentation

* fix: drain returned watcher check groups

* no-mistakes(review): Harden quarantine links and recover validated replacement polls

* no-mistakes(review): Preserve X mode across shim version transitions

* no-mistakes(review): Refresh legacy X shims before marker short-circuits

* no-mistakes(document): Correct persisted PR-check artifact documentation

* no-mistakes(document): Correct stale PR-check documentation

* fix: bind PR poll repair provenance

* no-mistakes(review): Enforce single-link ownership for custom check artifacts

* no-mistakes(review): Preserve private checks, X polling, and lifecycle IDs

* no-mistakes(review): Separate task creation and legacy teardown validation

* no-mistakes(review): Restore safe legacy operations and teardown validation

* no-mistakes(review): Disambiguate migration obligations and preserve legacy retries

* no-mistakes(review): Preserve fail-closed diagnostics and legacy quarantine evidence

* no-mistakes(review): Reconcile legacy migration retries and teardown collisions

* no-mistakes(review): Force legacy namespace reconciliation before marker short-circuits

* no-mistakes(document): Document private poll artifact safety contracts

* no-mistakes(lint): Suppress intentional literal-dollar lint finding

* fix: migrate historical X poll identity

* fix: harden PR check artifacts

* no-mistakes(review): Preserve fail-closed diagnostics and legacy quarantine evidence

* no-mistakes(review): Reconcile legacy migration retries and teardown collisions

* fix: migrate historical X poll identity

* no-mistakes(review): Harden X-mode artifact publication against symlink corruption

* no-mistakes(review): Guard X artifact publication

* no-mistakes(review): Enforce private X artifact reads

* no-mistakes(test): Fix backend compatibility fixture dependencies

* no-mistakes(document): Refresh PR-check documentation

* no-mistakes(lint): Remove unused x-mode test locals

* no-mistakes: apply CI fixes

* fix(bin): compact session-start backlog digest (#636)

* fix: compact session-start backlog digest

* no-mistakes(test): Fix legacy backend fixture helper

* no-mistakes(test): Fix watcher exit wait helper

* no-mistakes(document): Document compact backlog digest

* fix: dedupe stale watcher guard banners (#637)

* fix: dedupe stale watcher guard banner

* no-mistakes(review): Keep read-only guard state nonmutating

* no-mistakes(document): Clarify stale watcher docs

* fix(bin): balance bearings landed baseline (#640)

* fix: balance bearings landed defaults

* no-mistakes(document): Document balanced landed baseline

* no-mistakes: apply CI fixes

* fix: clarify captain-facing translation contract (#644)

* docs: clarify captain-facing translation contract

* no-mistakes(review): Restore runtime fallback mandate

* no-mistakes(document): Align Bearings translation wording

* fix(bin): make bootstrap output and nudges deterministic (#646)

* fix: make bootstrap nudges deterministic

* no-mistakes(review): Honor state override for bootstrap nudges

* no-mistakes(review): Update benign bootstrap documentation labels

* no-mistakes(review): Validate bootstrap nudge retry markers

* no-mistakes(document): Align bootstrap nudge documentation

* no-mistakes: apply CI fixes

* docs(secondmate-provisioning): clarify concise registry ownership (#649)

* Clarify concise secondmate registry contract

* no-mistakes(review): Expand secondmate registry boilerplate coverage

* no-mistakes(document): Point route docs to owner

* fix(bin): strip quoted blocked_by values during decision hold resolve (#654)

* fix(bin): strip quotes on blocked_by in decision-hold resolve

tasks-axi quotes multi-entry blocked_by as "a,b,c", so the comma-boundary
membership test only matched middle elements. Strip surrounding quotes
before matching so first and last hold ids resolve correctly.

* no-mistakes(document): Refresh decision-hold regression evidence

* feat(secondmate): inherit shared captain preferences (#656)

* feat(secondmate): inherit shared captain preferences

* no-mistakes(review): Honor shared captain data overrides

* no-mistakes(review): Honor bootstrap data override registry

* no-mistakes(document): Refresh shared inheritance docs

* no-mistakes(document): Clarify inherited local-material docs

* feat: gate local agent secret injection (#658)

* feat(spawn): gate local agent secret injection

* fix(spawn): align final Keychain slot

* no-mistakes: apply CI fixes

* test: isolate Herdr autodetect smoke sessions (#662)

* test: isolate herdr autodetect smoke session

* no-mistakes(review): Restored autodetect smoke gate bypass

* no-mistakes(test): Harden Herdr lab provisioning

* no-mistakes(document): Refresh Herdr lab docs

* docs: adopt under way for active work (#666)

* Revert "feat: gate local agent secret injection (#658)" (#668)

This reverts commit c27135cd9d35bc4c237d49b3b374da31fbd52eef.

* fix(pi): distinguish stale locks when arming watcher (#681)

* 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 house vocabulary (#685)

* fix: accept secondmate as house vocabulary

* no-mistakes(test): Update captain vocabulary contract test

* no-mistakes(document): Align secondmate documentation vocabulary

* fix(bin): parse handoff homes after registry parentheticals (#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 (#687)

* feat: add native session-start nudges

* no-mistakes(document): Document nudge script inventory

* docs: call built-in defaults the firstmate repo, not template (#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.

* fix(bin): repair fm-brief.sh parse error and harden set -u array expansion (#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>
#166 apostrophe regression cannot return unnoticed.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(bin): keep watcher supervision continuous across child cycles (#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: fetch current PR head for review diffs (#722)

* 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

* docs: resolve five contract contradictions across AGENTS.md, README, and skills (#736)

* docs: resolve five contract contradictions

* no-mistakes(test): align owner-pointer assertions with reworded docs; skip absent shellcheck

* docs(harness): correct Grok exit guidance (#742)

* docs(harness): reverify grok exit command

* no-mistakes(test): Correct Grok exit resume attribution

* fix(watcher): bound stale wakes for parked crew (#743)

* 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

* fix(supervision): distinguish ordinary wakes from recovery (#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 (#745)

* 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

* feat(wake): enrich drained signals with bounded status context (#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 (#784)

* 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

* fix(send): treat opencode busy-queued composer state as submitted (#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

* fix(spawn): require two stable reads before accepting worktree path (#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>

* fix(bin): make watcher process identity immune to Linux wall-clock changes (#752)

* fix(watcher): stabilize Linux process identity

* no-mistakes(document): document FM_PROC_ROOT_OVERRIDE and Linux starttime identity rationale

* fix: prevent AFK idle stalls and stale run attribution (#758)

* 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(bin): allow safe teardown during watcher recovery (#750)

* 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

* feat(herdr): order presentation spaces while preserving focus (#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

* fix(bin): send literal config reread nudges after pushes (#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 (#797)

* 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

* fix(herdr): group projected children beneath owning parents (#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: keep local no-mistakes tests intent-targeted (#823)

* 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: add canonical timed test runner (#825)

* 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: surface main inventory gaps in Bearings (#830)

* 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 bounded concurrent test isolation proof (#832)

* 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: guard against missed secondmate reports (#834)

* 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: require pinned real-Herdr CI coverage (#838)

* 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

* feat: shard portable tests and add bounded local parallelism (#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

* fix: block primary-session delegation outside the fleet (#854)

* 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

* fix: install tasks-axi in portable CI shards (#866)

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(bin): make dispatch profiles quota aware (#867)

* 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

* Add built-in ahoy recap skill (#873)

* fix: preserve trustworthy Bearings data in partial snapshots (#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

* feat(pi): add session-local calm mode (#884)

* 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): prevent redundant watcher re-arms (#885)

* 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 (#895)

* 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 (#898)

* fix: execute every PR body compliance event

* no-mistakes(document): Document independent PR compliance events

* fix: exclude operational injections from ahoy boundaries (#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

* fix: canonically classify operational inputs across harnesses (#909)

* fix: type canonical operational inputs

* no-mistakes(document): Correct canonical operational-input documentation ownership

* fix: avoid generic secondmate start acknowledgements (#926)

* fix: avoid generic secondmate acknowledgements

* no-mistakes(document): Document sparse secondmate acknowledgement behavior

* no-mistakes: apply CI fixes

* fix(pi): make calm mode persistent and gapless (#927)

* fix(pi): preserve calm presentation across sessions

* no-mistakes(review): Fix Calm home fallback persistence

* no-mistakes(document): Clarify Calm gapless and export contracts

* no-mistakes: apply CI fixes

* fix(watch): retire merged PR polls after durable notification (#932)

* fix: retire merged PR polls after notification

* no-mistakes(review): Decouple PR retirement recovery from template updates

* no-mistakes(review): Recover pending PR retirements before poll migration

* no-mistakes(document): Document merged PR poll retirement contracts

* fix: refine scout intake and parallel dispatch (#934)

* Clarify intake evidence and overlap handling

* no-mistakes(review): Align scout guard with intake classification

* no-mistakes(document): Clarify scout documentation and intake ownership

* fix(pi): prevent duplicate assistant replies in Calm (#936)

* fix(pi): preserve operational follow-up semantics in Calm

* no-mistakes(document): Correct Calm operational-row visibility documentation

* perf(bin): shrink the ShellCheck source graph (#939)

* perf(lint): shrink shell source graph

* no-mistakes(review): Ensure lint workers terminate fully on cancellation

* no-mistakes(document): Repair stale lint documentation ownership

* fix(pi): remove Calm hidden-block gaps (#942)

* fix(pi): remove Calm hidden-block gaps

* no-mistakes(review): Validate Calm geometry against current viewport

* no-mistakes(review): Synchronize Calm geometry checks with reload completion

* fix: enforce contract boundaries for ask-user findings (#945)

* fix: escalate ask-user contract expansion

* no-mistakes(document): Point project management to authority owner

* docs: prefer direct operational paths (#946)

* fix(pi): hide operational user rows in Calm mode (#948)

* fix(pi): hide Calm operational user rows

* no-mistakes(review): Narrow Calm operational input suppression

* no-mistakes(review): Avoid Calm replay classifier subprocesses

* no-mistakes(document): docs: point Pi verification to Calm owner

* fix: relaunch missing second mates at session start (#950)

* fix(session-start): relaunch missing second mates

* fix(test): detect completed parallel workers

* no-mistakes(review): Isolate session-start recovery test cleanup

* no-mistakes(review): Complete backend-safe secondmate session recovery

* no-mistakes(review): Resolve Zellij task ownership before recovery

* no-mistakes(review): Recover relocated Zellij ghost tabs safely

* no-mistakes(review): Restore conservative Zellij recovery boundary

* no-mistakes(review): Reject malformed tmux recovery targets

* no-mistakes(document): Align secondmate recovery documentation

* no-mistakes: apply CI fixes

* fix(herdr): reclaim resumed task projections after restart (#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

* Teach Ahoy to surface open decisions (#968)

* Require shipshape routine acknowledgement (#969)

* docs: separate current guidance from verification evidence (#994)

* docs: separate current guides from verification

* no-mistakes(review): Restore Herdr 0.7.5 restart-reclaim verification evidence

* fix: preserve Claude watcher continuity across Stop hooks (#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

* fix(herdr): clean stale projections at session start (#996)

* 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

* fix: recover Claude supervision without watcher-status gate (#1001)

* fix: recover Claude supervision at session start

* fix: remove Claude watcher-status command gate

* no-mistakes(document): docs: remove stale continuity gate references

* fix: make quota-aware profile selection agent-owned (#1018)

* Replace quota dispatch selector instructions

* no-mistakes(review): Align bootstrap docs with agent-owned dispatch selection

* fix(bin): remove vestigial dispatch selector (#1026)

* 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

* docs(agents): drop superseded interim quota-window rule (#1039)

quota-axi 0.1.13 emits schemaVersion 2 with a quotaSemantics object per
provider, so the successor named in the interim rule has landed and the
rule's own removal condition is satisfied.

Keep the ownership clause so quota-axi remains the single owner of how
model or product windows relate to bounding account windows, and drop
the interim weakest-headroom instruction. The unknown-semantics case is
already covered by the existing requirement to stop and report a
candidate whose applicable quota data or interpretation cannot be
established.

Drop the matching assertion phrase from
tests/fm-instruction-owners.test.sh; the retained ownership phrase still
asserts.

* fix(tmux): scope busy detection and recognize current Claude turns (#1049)

* fix(tmux): scope Claude busy detection by harness

* no-mistakes(review): Separate verified and fallback busy signatures

* no-mistakes(test): Scope busy signatures to supplied harnesses

* no-mistakes(document): Document harness-scoped busy detection

* feat: add verified Kimi crewmate adapter (#1047)

* Add verified Kimi crewmate harness adapter

* no-mistakes(review): Scope Kimi moon detection to spinner lines

* no-mistakes(review): Match only complete Kimi spinner rows

* no-mistakes(review): Resolve Kimi binary portably before pane creation

* no-mistakes(document): Align Kimi adapter documentation

* no-mistakes(lint): Suppress false-positive ShellCheck warning for sourced watcher override

* Fix Kimi busy spinner detection

* no-mistakes(review): Recognize Kimi session-lock ancestry and holders

* no-mistakes(review): Scope pending-reply Kimi busy detection by harness

* no-mistakes(document): Correct Kimi spinner capture documentation

* no-mistakes(document): Clarify optional Kimi spinner whitespace

* no-mistakes(lint): Silence intentional pending-reply test stub warnings

* test: align rebased Kimi busy fixtures

* no-mistakes: apply CI fixes

* Reconcile Kimi busy detection after per-harness scoping

* no-mistakes(review): Clarify observed Kimi spinner whitespace contract

* no-mistakes(document): Clarify Kimi harness documentation

* fix: harden Kimi submission and spinner matching (#1058)

* fix kimi pointer submission and spinner conformance

* no-mistakes(review): Preserve Kimi submit target ownership guard

* feat(bin): add guarded Kimi turn-end wake (#1059)

* Add guarded Kimi turn-end hook

* no-mistakes(review): Require jq before installing Kimi turn-end hook

* no-mistakes(review): Expose jq inside isolated Kimi test fixtures

* no-mistakes(review): Preserve Kimi config boundaries during hook removal

* no-mistakes(review): Document Kimi removal newline safeguard

* no-mistakes(document): Document Kimi shared-home preservation

* fix(tmux): classify bordered composers across all rows (#1066)

* Fix structural tmux composer reading

* Verify Calm compatibility with Pi 0.82

* no-mistakes(review): Harden structural composer classification boundaries

* no-mistakes(review): Refresh composer and Kimi regression fixtures

* no-mistakes(review): Fail closed on unbounded composer edges

* no-mistakes(review): Enforce aligned composer geometry safely

* no-mistakes(review): Make composer ambiguity locale-safe

* no-mistakes(review): Preserve ambiguity through composer submission

* no-mistakes(review): Carry composer proof through retries

* no-mistakes(document): Document structural tmux composer delivery guarantees

* no-mistakes: apply CI fixes

* feat(bin): add verified pi-signed runtime adapter (#1145)

* feat: add verified pi-signed adapter

* no-mistakes(review): Correct pi-signed maintainer verification date

* no-mistakes(review): Correct remaining pi-signed verification dates

* no-mistakes(review): Preserve authoritative pi-signed runtime identity

* no-mistakes(document): Document pi-signed shared adapter semantics

* no-mistakes: apply CI fixes

* fix(pi): rearm watcher across session transitions (#1166)

* fix(pi): rearm watcher across same-process session transitions

Pi emits session_shutdown for ordinary /new, /resume, and /fork replacement
as well as terminal quit. The primary watcher extension latched a module-level
stopping flag on every shutdown, so a replacement session in the same process
could not arm monitoring until Pi restarted.

Own arm authority per session generation so only the active live generation
may start, stop, or rearm the child. Replacement sessions can arm again without
restarting Pi, stale prior-generation callbacks cannot mutate the active cycle,
and real quit still blocks late rearm.

* no-mistakes(review): Preserve Pi generation isolation and exit cleanup

* no-mistakes(document): Correct Pi watcher transition documentation

* feat: route crew dispatch using quota-window pace (#1172)

* Consume quota-axi pace signals in dispatch profile array selection.

Add quota-array-dispatch as the single owner of the pace-aware candidate
choice, keep AGENTS.md to the intake boundary and load trigger, and cover
the acceptance cases with sanitized schemaVersion 3 fixtures.

* no-mistakes(review): Stop and report genuine quota dispatch ties

* no-mistakes(document): Document quota pace freshness and uncertainty

* fix: adapt Grok Stop continuation and harden endpoint cleanup (#1171)

* fix(grok): adapt Stop continuation to runtime capability

* no-mistakes(review): Reject ambiguous Grok Stop payloads

* no-mistakes(review): Reject duplicate Grok fields and accept spaced tmux sessions

* no-mistakes(review): Enforce exact tmux cleanup selectors

* no-mistakes(test): Fix historical tmux fixture and validate Grok Stop

* no-mistakes: apply CI fixes

* fix: restore stock macOS Bash 3.2 brief scaffolding (#1093)

* fix(brief): make DOD scaffolding parse-safe on stock macOS Bash 3.2

fm-brief.sh built each Definition-of-done block and the not-enabled
Herdr declaration with `VAR=$(cat <<EOF ... EOF)`. On Bash 3.2 (macOS
/bin/bash) the lexer scans for the command substitution's closing `)`
textually and tracks quote state through the heredoc body, so a single
apostrophe, unbalanced quote, or unbalanced paren in that prose breaks
parsing of the whole script. Every ship-brief scaffold (no-mistakes,
direct-PR, local-only) failed with `unexpected EOF while looking for
matching )`. Bash 4+ parses it fine, so the breakage stayed invisible
everywhere except stock macOS.

Replace all four command-substitution heredocs with
`IFS= read -r -d '' VAR <<EOF || true`. That removes the `$(...)`
wrapper and the entire defect class regardless of future prose, and
preserves the variable expansion the direct-PR and local-only bodies
need. `read` keeps the heredoc's trailing newline that `$(...)` used to
strip, so trim one newline to keep every generated brief byte-identical
to prior output.

Guard the structure, not one historical phrase: a new test rejects any
heredoc nested in a command substitution anywhere in fm-brief.sh, where
the old assertion pinned a single apostrophe phrase and so missed the
reintroduction. Extend the stock-macOS Bash CI job from parsing one
script to the whole maintained shell surface (bin/*.sh,
bin/backends/*.sh, tests/*.sh), matching bin/fm-lint.sh's canonical file
set so parse scope and lint scope cannot drift apart.

* no-mistakes(review): Captain: harden Bash structure and inventory guards

* no-mistakes(document): Align stock macOS Bash contributor checks

* no-mistakes(lint): Suppress deliberate SC2016 literal fixture warnings

* test: stabilize tmux teardown conformance baseline (#1209)

* fix(test): pin teardown tmux baseline to historical kill selectors

merge-base HEAD main collapses to HEAD after the exact-selector change
lands on the default branch, so the old teardown fixture was accidentally
exercising current exact targets. Resolve a content-historical permissive
tmux adapter from first-parent history and force that post-squash topology
inside the conformance case so main and feature branches keep the same
old-vs-new contract.

* no-mistakes(lint): Suppress intentional literal-pattern ShellCheck warnings

* docs: slim quota-array-dispatch to the pace selection core (#1197)

Cut the runtime skill to the compact pace-aware selection procedure plus
minimum owner pointers. Keep every distinct decision rule and move expanded
acceptance scenarios to deterministic fixture ownership assertions.

Size: 170/1374/10187 -> 63/544/4068 (about 63%/60%/60% reduction).

* feat(bin): inherit backend config into secondmate homes (#1219)

* Inherit config/backend into secondmate homes with deliberate-override preservation

Add backend to the shared inheritable config allowlist so launch, locked
bootstrap, and config-push converge a primary pin into secondmate homes as each
home local future-spawn default. Track last-inherited bytes in a private state
provenance marker so deliberate per-home overrides survive present and absent
primary convergence, keep --backend and FM_BACKEND stronger, and extend the
existing inheritance tests plus docs and skill claims.

* no-mistakes(review): Preserve equal unprovenanced backend overrides

* no-mistakes(review): Preserve symlink overrides and verify spawn precedence

* no-mistakes(review): Snapshot backend inheritance for consistent provenance

* no-mistakes(review): Simplify backend inheritance to primary-authoritative convergence

* no-mistakes(document): Document inherited backend override preservation

* fix: restore primary-authoritative backend inheritance after document regression

The document step reintroduced provenance and deliberate per-home override
semantics after review had simplified config/backend to plain primary-authoritative
allowlist membership. Restore the primary-always-wins path: present overwrites,
absent removes, no provenance marker, and docs/tests match that contract.

* no-mistakes(review): Add divergent backend precedence regression fixtures

* no-mistakes(document): Document backend inheritance contract

* fix(pi): remove Calm's upper version ceiling (#1226)

* fix(pi): remove Calm's exclusive Pi upper-version ceiling

tests/fm-calm-pi-extension.test.sh gated on a closed PI_COMPAT_VERSIONS
allowlist ("0.81.1 0.82.0") that refused any other installed Pi, and docs
described that range as "supported" rather than verified evidence. The
Calm CHANGELOG shows no API introduced at either version, so there is no
evidence for a real minimum; the presentation adapters already probe the
exact method they patch rather than checking a version.

Replace the allowlist with dated version evidence that never rejects a
newer Pi, and make each presentation adapter degrade independently with
a diagnostic if a future Pi removes its API, instead of the whole Calm
extension failing to load. Rewrite the feasibility doc's "Pi 0.81.1
through 0.82.0" phrasing to state it as verified evidence, not a
ceiling.

* no-mistakes(review): Probe missing Calm adapter exports safely

* no-mistakes(document): Document Calm's unbounded Pi compatibility

* fix(bin): allow session-local todo tools in the subagent guard (#1204)

* fix(guard): allow session-local todo tools in the primary

The delegation-shape guard denied TaskCreate and TaskUpdate because their
normalized names contain the `task` stem. Those tools write only the harness's
session-local todo list, which has no executor: it spawns no agent, allocates
no worktree, registers no schedule, and starts nothing that outlives the
session. That is not the unaccounted work the guard exists to stop, so the stem
match was a false positive, and the deny text told the primary to run
bin/fm-brief.sh and bin/fm-spawn.sh to create a todo entry.

Add a separately-reasoned PLAN_ONLY_TOOLS exact-name exclusion rather than
widening OBSERVE_ONLY_TOOLS, whose documented contract is tools that only
observe or stop existing work. Both lists stay exact-name so neither can widen
by substring.

Tests cover the two allowed names and six near-miss names that a substring or
shortened-stem widening would release; both mutations were watched red.

* no-mistakes(review): drop session-local todo tools from recommended deny list

* no-mistakes: apply CI fixes

* fix(session-lock): resolve Claude bg-spare ancestry to the outermost claude pid (#1206)

* fix(session-lock): resolve Claude bg-spare ancestry to the outermost claude pid

fm_harness_ancestry_pid() previously returned the first ancestor process
whose command matched a verified harness name. Claude Code's Stop hook
fires as a bg-spare worker several levels below the session's actual
lock-owning claude process (hook shell -> claude bg-spare ->
claude bg-pty-host -> claude -> claude(lock)), so the first match was
the bg-spare worker, not the lock owner. fm_session_lock_owned_by_self()
then never matched state/.lock, and the Claude Stop auto-arm silently
treated its own primary session as an unrelated live owner and never
armed the watcher.

The walk now keeps going past a claude-named match, looking for a still
more ancestral claude-named match, and stops the instant a non-match
follows an already-found match (bounding it to a contiguous run rather
than the literal ancestry top, so an unrelated claude-named process
further up the real process tree is never mistaken for part of this
session's own nested chain). Every other harness keeps the original
first-match-wins behavior, since e.g. Pi's shared signed-wrapper
ancestry actually holds the session at the inner engine pid, not an
outer wrapper pid. Hop limit raised from 8 to 16 to cover the deeper
bg-spare chain.

* no-mistakes(review): Add nested-claude-ancestry regression test; fix nudge doc depth claim

* no-mistakes: apply CI fixes

* fix: conferma l'avvio del watcher su Windows/MSYS (#1212)

* fix: confirm watcher startup on MSYS

* no-mistakes(review): gate MSYS arm ready timeout, cache uname, harden locale test

* no-mistakes(review): validate OpenCode ready timeout, make uname cache internal

* fix(spawn): forward CLAUDE_CONFIG_DIR to claude crewmates (#1195)

* fix(spawn): forward firstmate's CLAUDE_CONFIG_DIR to claude crewmates

Crewmate panes are created by a long-lived tmux/herdr daemon that does not
inherit firstmate's current environment. When firstmate runs under a non-default
CLAUDE_CONFIG_DIR (for example a work-vs-personal subscription split), a bare
`claude` in the crewmate pane fell back to the default ~/.claude store and
launched unauthenticated, blocking the crewmate before it could do any work.

fm-spawn now prefixes the claude launch with firstmate's own resolved
CLAUDE_CONFIG_DIR when set, so the crewmate uses the same credential/config
store firstmate is authenticated with. An unset value is the single-store
default and adds no prefix; non-claude harnesses are unaffected.

Adds three tests in fm-spawn-dispatch-profile.test.sh (forwarded-when-set,
omitted-when-unset, non-claude-ignored) and pins CLAUDE_CONFIG_DIR in the test
helper so launch assertions no longer depend on the developer's environment.

* no-mistakes: apply CI fixes

* fix: preserve dispatch identity across authentication checks (#1233)

* fix: preserve dispatch harness identity

* no-mistakes(review): Fix Grok counterfactual tuple validation

* no-mistakes(document): Scope dispatch authentication to selected tuple

* fix: restore dispatch instruction budget

* no-mistakes(review): Scope dispatch authentication after candidate selection

* fix(bin): normalize relative durable paths (#1256)

* fix(bin): handle dash-leading harness process names (#2)

* fix: handle dash-leading harness process names

* no-mistakes(review): Make dash-leading harness regression hermetic

* fix: preserve secondmate reply routes across relative homes

Resolve relative home, data, and state inputs before durable charter generation, and fail when caller-relative directories cannot be resolved.

Use absolute paths at the related spawn, AFK daemon, and X-mode cross-process handoffs so later processes cannot reinterpret them from another working directory.

* no-mistakes(review): Preserve absolute overrides and normalize relative durable paths

* no-mistakes(review): Normalize relative home before deriving durable paths

* no-mistakes(document): Document relative durable-path normalization

* no-mistakes(review): Captain: Ignore inherited CDPATH during relative path normalization

* no-mistakes(lint): Fix empty CDPATH assignments for ShellCheck

* refactor(skills): make Bearings chat-only by default (#1136)

* Add internal status skill

* no-mistakes(document): register /status skill in documentation-audiences inventory

* no-mistakes(lint): replace grep|wc -l with grep -c in status skill test

* test: silence literal status skill patterns

* Refactor bearings default to chat-only

---------

Co-authored-by: Kun Chen <3233006+kunchenguid@users.noreply.github.com>

* Clarify follow-up routing during validation (#1277)

* fix: honor concrete approval for project operations (#1272)

* docs: add captain-approved project operation exception to hard rule 1

Firstmate stays read-only over projects by default, but when the captain
clearly approves a concrete project operation and scope in the moment,
firstmate may perform exactly that approved operation with its own tools.
The approval is never inferred, broadened, or standing, and it does not
relax the existing force, discard, unlanded-work, or merge-authority
boundaries.

* no-mistakes(review): Clarify captain-approved project operation boundaries

* no-mistakes(document): Clarify captain-approved project operation scope

* docs: cover directories and preserve the operation-or-scope alternative

Widen the captain-approved project operation exception in AGENTS.md to
files or directories, and restore the explicit operation-or-scope
alternative that a prior pipeline auto-fix had collapsed into "and".

Rework project-management SKILL.md's Remove section, which previously
told firstmate to refuse project removal until a guarded helper existed;
that helper was never built, so the text directly contradicted the new
instruction-only exception. It now points at the exception plus the
existing removal preflight it still requires unchanged.

Update the one instruction-owners test assertion that hard-coded the
sentence removed above, so the suite tracks current, not obsolete, text.

* docs: add captain-approved project operation exception to hard rule 1

Firstmate stays read-only over projects by default, but when the captain
clearly approves a concrete project operation and scope in the moment,
firstmate may perform exactly that approved operation with its own tools.
The approval is never inferred, broadened, or standing, and it does not
relax the existing force, discard, unlanded-work, or merge-authority
boundaries.

* no-mistakes(review): Clarify captain-approved project operation boundaries

* no-mistakes(document): Clarify captain-approved project operation scope

* docs: cover directories and preserve the operation-or-scope alternative

Widen the captain-approved project operation exception in AGENTS.md to
files or directories, and restore the explicit operation-or-scope
alternative that a prior pipeline auto-fix had collapsed into "and".

Rework project-management SKILL.md's Remove section, which previously
told firstmate to refuse project removal until a guarded helper existed;
that helper was never built, so the text directly contradicted the new
instruction-only exception. It now points at the exception plus the
existing removal preflight it still requires unchanged.

Update the one instruction-owners test assertion that hard-coded the
sentence removed above, so the suite tracks current, not obsolete, text.

* no-mistakes(review): Align project removal preflight with approved exception

* no-mistakes(document): Align project removal documentation with approved exception

* fix: restore removal test byte-for-byte and preserve the default sentence

tests/fm-instruction-owners.test.sh had been changed to assert different
text; restore it byte-for-byte to origin/main. project-management SKILL.md's
Remove section now keeps the exact default "Never issue a raw removal
command from Firstmate." sentence that test still asserts, immediately
followed by the already-approved captain-operation-or-scope exception, so
the default and the exception both stay explicit and consistent.

* no-mistakes(document): Align project-write boundary documentation

* fix(skills): route new project intake through secondmate scopes (#1275)

* Route project intake through secondmate scopes

* no-mistakes(test): Guard all main-home project registry mutations

* no-mistakes(document): Consolidate secondmate routing documentation

* no-mistakes: apply CI fixes

* Restore new-project routing scope

* no-mistakes(document): Clarify secondmate routing for new-project intake

* no-mistakes: apply CI fixes

* fix: scope validation corrections by accepted behavior (#1281)

* fix: scope validation corrections by accepted behavior

* no-mistakes(review): Classify stale delivery evidence as an autonomous correction

* test: replace source assertions with behavioral coverage (#1282)

* test: remove source-content assertions

* no-mistakes(review): Replace source assertions with runtime behavior coverage

* no-mistakes(review): Isolate Kimi task temp runtime coverage

* no-mistakes(document): Refresh test cleanup documentation

* no-mistakes: apply CI fixes

* fix(watch): escalate busy workers with no completed turn (#1286)

* fix(watch): bound how long a busy pane may run with no completed turn

A busy pane (backend busy state or the harness's rendered footer) was
unconditional, unbounded proof of liveness in every escalation path, so a
hung foreground tool call behind a busy signature could run for hours
undetected (2026-07 hibit-agent-focus-nonsteal-r1 incident: a catastrophic-
backtracking regex hung one bash call for 25h behind an unchanging
"Working..." footer).

FM_BUSY_TURN_MAX_SECS (default 3600s) now bounds how long a busy pane may
run with no completed turn (state/<id>…
BenWilcox8 added a commit to BenWilcox8/firstmate that referenced this pull request Aug 31, 2026
Join the two diverged firstmate lines again, carrying both feature sets
intact across the second divergence window (local 633326d + 34 commits,
origin 10ee779 + 56 commits, common base 6de9278).

Local line preserved:
- agent-axi delegation for herdr (89a4f46, 2b7da16): create/kill delegate to
  agent-axi's durable slot ledger with a minimal native fallback, plus the
  `layout --repair` sweep in session-start bootstrap, the read-only drift
  preview on the watcher heartbeat, and `snapshot` in the session-start digest
  (bin/fm-herdr-layout-lib.sh, tests/fm-herdr-layout-lib.test.sh).
- TZ=UTC pin in fm_pid_identity (2716497) and its timezone-invariance
  regression test.
- The dashboard-signals block in every generated brief (c3ea1df).
- The harness-neutrality migration (633326d and its three predecessors):
  docs/harness-neutrality-audit.md, the grok guard's CLAUDE_PROJECT_DIR
  fallback removal, positive pi liveness in the tmux backend, and the README
  all-five-harness invocation caveat.
- The token-lean session-start digest, --session-name spawning, the
  filesystem-map skill, the specs pointer, and the NixOS-portable
  fm_test_core_path test helper.

Origin line adopted whole: Herdr presentation spaces and their restart
reclaim (kunchenguid#784, kunchenguid#790, kunchenguid#821, kunchenguid#967), recovery-grade agent state across backends
(kunchenguid#950), canonical operational-input classification (kunchenguid#909, kunchenguid#899), the Pi Calm
mode extension (kunchenguid#884 and follow-ups), the canonical timed test runner and
sharded CI lanes (kunchenguid#825, kunchenguid#841, kunchenguid#832, kunchenguid#838), GitLab merge-request watching
(kunchenguid#797), pending-reply guards for secondmate requests (kunchenguid#834), the Ahoy recap
skill (kunchenguid#873, kunchenguid#968), Bearings hardening (kunchenguid#830, kunchenguid#875), ask-user authority
contracts (kunchenguid#945), quota-aware dispatch profiles (kunchenguid#867), and the rest.

Conflict resolution notes (16 files, 20 hunks):
- bin/backends/herdr.sh: kept BOTH behaviors. Origin's presentation-projection
  machinery is additive (its own workspace, its own placement); the local
  agent-axi delegation still owns ordinary pane create/kill. Took origin's
  recovery-grade fm_backend_herdr_agent_state over the local agent_alive verb
  (origin supplies a compatible agent_alive view on top of it).
- bin/backends/tmux.sh: took origin's fm_backend_tmux_agent_state and grafted
  the local exact-match `pi` arm into its classifier, so pi stays positively
  alive under the new missing/dead/alive/ambiguous/unreadable vocabulary.
- bin/fm-spawn.sh: the claude launch template now carries BOTH the local
  __NAMEFLAG__ and origin's operational-input launch encoder. The herdr branch
  takes origin's projection-or-flat structure, with the flat path still
  delegating placement to agent-axi.
- bin/fm-wake-lib.sh: origin's Linux /proc starttime identity is primary; the
  local TZ=UTC pin stays on the non-Linux `ps` fallback, where it is still the
  fix for the false supervision-off banner.
- bin/fm-bootstrap.sh, bin/fm-watch.sh: additive - origin's sweep ordering and
  pending-reply wiring alongside the local herdr layout repair sweep and drift
  probe.
- docs/tmux-backend.md: kept the local 2026-07-23 real-pi 0.81.1 verification
  (it supersedes origin's 2026-07-07 assumption-based record) and restated it
  in origin's new state vocabulary, preserving origin's `missing` relaunch fact.
- tests/fm-secondmate-liveness.test.sh: origin's rewritten agent_state suite
  replaces the local agent_alive suite, with the local pi/pip/node cases
  re-added to it.
- tests/fm-watcher-lock.test.sh: both identity regressions kept.
- tests/fm-pr-check-security.test.sh: took origin's helper, which builds the
  fake lock identity through production fm_pid_identity instead of a
  hand-rolled `ps` copy, so it can no longer drift from the code under test.

LOCAL OVERRIDE: fm_backend_herdr_tab_is_husk and the native close-and-replace
husk path stay deleted, as the captain's phase-1 agent-axi work intended;
agent-axi's `layout --repair` owns husk convergence and the native fallback
refuses a same-labelled leftover rather than replacing it. Nothing on the
origin side calls that helper. Relatedly, the config/herdr-layout split mode
that round 1 preserved was already retired on this local line by 2b7da16 -
that retirement is the captain's own later work, not an origin override.

Validated: bin/fm-lint.sh (ShellCheck 0.11.0) clean over bin/*.sh,
bin/backends/*.sh, and tests/*.sh; full tests/*.test.sh sweep matches the
pre-merge local-main and origin-main baselines on this host.
lytv pushed a commit to lytv/mymate that referenced this pull request Sep 8, 2026
* fix: escalate ask-user contract expansion

* no-mistakes(document): Point project management to authority owner
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant