Skip to content

docs: clarify live supervision constraints - #9

Merged
kunchenguid merged 2 commits into
mainfrom
fm/selfwork-deleg-m8
Jun 12, 2026
Merged

kunchenguid merged 2 commits into
mainfrom
fm/selfwork-deleg-m8

Conversation

@kunchenguid

Copy link
Copy Markdown
Owner

Intent

Encode two structural supervision rules into AGENTS.md for the firstmate orchestrator template. Section 1 should explicitly say firstmate delegates changes to shared firstmate-repo material such as AGENTS.md, bin, workflows, and skills to a crewmate while any fleet is live, while preserving direct self-edits when the fleet is empty and preserving firstmate ownership of operational fleet state. Section 8 should state that while any task is in flight, firstmate must not run long foreground-blocking operations such as its own no-mistakes pipeline or long builds, and must background such work so watcher wakes can interleave and supervision remains responsive. This is a documentation-only change that should touch AGENTS.md only, match the terse one-sentence-per-line voice, and avoid touching scripts.

What Changed

  • Documented that firstmate keeps ownership of operational fleet state but delegates shared firstmate-repo edits to crewmates while any fleet tasks are live.
  • Clarified that direct edits to firstmate materials remain allowed when the fleet is empty.
  • Added supervision guidance to keep long validation or build work in the background so watcher wakes can still be handled.

Risk Assessment

✅ Low: Documentation-only change scoped to AGENTS.md that directly encodes the requested supervision constraints without altering executable behavior.

Testing

Checked the baseline-to-target diff, confirmed only AGENTS.md changed, manually verified the added documentation covers live-fleet delegation, empty-fleet direct edits, operational fleet state ownership, and non-blocking supervision during in-flight work, captured the documentation diff as evidence, and confirmed testing left the worktree clean.

Evidence: AGENTS.md supervision rules diff

Shows the exact user-facing documentation additions in Section 1 and Section 8.

diff --git a/AGENTS.md b/AGENTS.md
index 6b370eb..089f137 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -21,28 +21,32 @@ Hard rules, in priority order:
    The single exception is tool-driven project initialization (section 6).
 2. **Never merge a PR without the captain's explicit word.**
 3. **Never tear down a worktree that holds work not on a remote.**
    `bin/fm-teardown.sh` enforces this; never bypass it with `--force` unless the captain explicitly said to discard the work.
    The one carve-out: a scout task's worktree is declared scratch from the start - its deliverable is the report, and teardown lets the worktree go once that report exists (section 7).
 4. **Crewmates never address the captain.**
    All crewmate communication flows through you.
    The captain may watch or type into any crewmate window directly; treat such intervention as authoritative and reconcile your records at the next heartbeat.
 5. Report outcomes faithfully.
    If a crewmate failed, say so plainly with the evidence.
 
 You may freely write to this repo itself (backlog, briefs, state, even this file when the captain approves a change).
+Operational fleet state stays yours to maintain even when crewmates are live.
+When one or more crewmates are in flight, delegate changes to shared repo material (AGENTS.md, README.md, CONTRIBUTING.md, .github/workflows/, bin/, agent skill files) to a crewmate through the normal scout or ship machinery instead of hand-editing them yourself.
+When the fleet is empty, you may make those firstmate-repo changes directly.
+Hands-on firstmate work competes with live supervision for the same single thread of attention.
 This repo is a shared template, not the captain's personal project.
 The tracking principle: anything shared (AGENTS.md, README.md, CONTRIBUTING.md, .github/workflows/, bin/, agent skill files) is tracked under git; anything personal to this captain's fleet (data/, state/, config/, projects/, .no-mistakes/) is not.
 Commit durable changes to the shared, tracked material with terse messages.
-This repo is itself behind the no-mistakes gate: ship tracked changes (AGENTS.md, README.md, CONTRIBUTING.md, .github/workflows/, bin/, agent skill files) through the pipeline yourself - branch, commit, run the pipeline, PR - and the captain's merge rule applies here exactly as it does to projects.
+This repo is itself behind the no-mistakes gate: ship tracked changes (AGENTS.md, README.md, CONTRIBUTING.md, .github/workflows/, bin/, agent skill files) through the pipeline - branch, commit, run the pipeline, PR - and the captain's merge rule applies here exactly as it does to projects.
 Never add an agent name as co-author.
 
 ## 2. Layout and state
 
 `` `
 AGENTS.md            this file (CLAUDE.md is a symlink to it)
 CONTRIBUTING.md      contributor workflow and repo conventions
 README.md            public overview and development notes
 .github/workflows/   shared CI and PR enforcement, committed
 .agents/skills/      shared skills, committed
 .claude/skills       symlink to .agents/skills for claude compatibility
 bin/                 helper scripts, committed; read each script's header before first use
@@ -318,24 +322,28 @@ On wake, in order of cheapness:
 Heartbeats back off exponentially while they are the only wakes firing (600s doubling to a 2h cap - an idle fleet stops burning turns); any signal, stale, or check wake resets the cadence to the base interval.
 
 Never rely on hooks or status files alone; the heartbeat review of every window is mandatory and unconditional.
 tmux is the ground truth.
 
 **Watcher liveness is guarded, not just disciplined.**
 Restarting the watcher is the last action of every wake-handling turn - but the protocol no longer relies on remembering that.
 While running, `fm-watch.sh` touches `state/.last-watcher-beat` every poll cycle.
 The supervision scripts (`fm-peek`, `fm-send`, `fm-spawn`, `fm-teardown`, `fm-pr-check`, `fm-promote`) call `bin/fm-guard.sh` first, which warns to stderr when any task is in flight (`state/*.meta` exists) but that beacon is missing or older than `FM_GUARD_GRACE` (default 300s).
 So the next time you touch the fleet with no watcher alive, the tool output itself tells you to restart it - a pull-based guard that works on any harness, since it rides the script output you already read rather than a harness-specific hook.
 The grace window keeps normal handling (watcher briefly down between a wake and its restart) silent.
 If a guard warning fires, restart the watcher before doing anything else.
+Watcher liveness is not enough if you are foreground-blocked.
+Whenever one or more tasks are in flight, do not run long foreground-blocking operations in your own session.
+This includes your own no-mistakes pipeline, long builds, and any other multi-minute command.
+Background that work so watcher wakes can interleave with it and the supervision loop stays responsive.
 
 Token discipline: status files before panes; default peeks to 40 lines; never stream a pane repeatedly through yourself; batch what you tell the captain.
 
 ### Stuck-crewmate playbook (escalate in order)
 
 1. Peek the pane.
 2. Crewmate is waiting on a question its brief already answers: answer in one line via fm-send.
 3. Crewmate is confused or looping: interrupt with the adapter's interrupt key (the window's harness is recorded as `harness=` in `state/<id>.meta`; e.g. `bin/fm-send.sh <window> --key Escape`), then redirect with one corrective line.
 4. Crewmate is context-exhausted or wedged: exit the agent with the adapter's exit command, relaunch with the same brief plus a `progress so far` note you append to it. The worktree and commits persist; this is cheap.
 5. Second relaunch fails too: write `failed` to backlog, tell the captain with evidence.
 
 ## 9. Escalation and captain etiquette

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.

  • git diff --stat 0cfbc844d587bcdc41cc17a26b934cff45540bb6..17f2dc37b62501f2ec0fefa4cbdbbeff84fc9a39
  • git diff --name-only 0cfbc844d587bcdc41cc17a26b934cff45540bb6..17f2dc37b62501f2ec0fefa4cbdbbeff84fc9a39
  • git diff --unified=80 0cfbc844d587bcdc41cc17a26b934cff45540bb6..17f2dc37b62501f2ec0fefa4cbdbbeff84fc9a39 -- AGENTS.md
  • grep search for Operational fleet state|When one or more crewmates are in flight|When the fleet is empty|foreground-blocking|Background that work in AGENTS.md
  • Captured reviewer-visible documentation diff to /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KTYVYRWAAXPEVQ135ESNETBV/agents-supervision-rules.diff
  • git status --short
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@kunchenguid
kunchenguid merged commit f395809 into main Jun 12, 2026
3 checks passed
@kunchenguid
kunchenguid deleted the fm/selfwork-deleg-m8 branch June 12, 2026 21:41
vipentti pushed a commit to vipentti/firstmate that referenced this pull request Aug 5, 2026
* Document live supervision constraints

* no-mistakes(document): Sync supervision documentation
@jk1rby

jk1rby commented Aug 23, 2026

Copy link
Copy Markdown

Rebase + verdict: unblocking this PR

Rebased fm/fm-briefs-ignore-devpod onto current main (twice — main advanced again
mid-task from PR #13, so this was re-rebased onto the latest tip before the final run).
Content is unchanged: git patch-id --stable on the original commit and the final rebased
commit both hash to a888d2c7b8f8b6a5121ab9717595cf0a895aeab3. No conflicts occurred on
either rebase, so nothing was hand-merged.

$ git log --oneline origin/main..HEAD
4d1170d feat(brief): hand the worker the project's environment and browser skill

bin/fm-ci.sh verdict

FM_CI_SUMMARY verdict=FAIL checks=7 passed=4 failed=1 unavailable=2 duration_ms=2096693

Full raw output is pasted across the 6 comments below (parts 1/6–6/6), unedited.

Classification

Check Result Classification Evidence
lint pass — —
invariants pass — —
coverage pass — —
push-guard unavailable could not check — host precondition, not code core.hooksPath is unset in this worktree; identical (git config --get core.hooksPath exits 1) in a fresh origin/main worktree. This is a local git-config wiring gap the PR does not touch.
behavior-parallel pass — —
behavior-serial fail see below 2 failing test files, both pre-existing
behavior-herdr unavailable could not check — host tool version, not code bin/fm-install-herdr.sh pins Herdr 0.7.4; this host has 0.8.0 installed. Declared pin is untouched by this branch (confirmed via git diff vs origin/main).

behavior-serial failing tests, both independently reproduced on a fresh origin/main worktree — pre-existing, not introduced by this branch:

  1. tests/fm-backend-herdr-focus-flash-e2e.test.sh — not ok - the Part C doomed pane never acquired a stable persistent sleep child process

    • Live e2e test against the real installed Herdr binary (0.8.0), not the 0.7.4 this suite is verified against.
    • Proof: bash tests/fm-backend-herdr-focus-flash-e2e.test.sh run from a clean origin/main worktree (ec9e954) produces the identical not ok line and exit=1.
  2. tests/fm-teardown.test.sh — not ok - leaked-process-reap: leaked worktree process survived teardown

    • This specific case (test_leaked_worktree_process_is_reaped) runs teardown's real leak-detection path (no lsof stub), which needs the actual lsof binary to find the worktree-rooted leaked process by cwd. lsof is not installed on this host (which lsof → exit 1).
    • Note: a sibling test in the same file, test_lsof_absent_reaps_tmux_process_group, explicitly covers the missing-lsof fallback path and passes — the fallback works, but this particular case doesn't route through it.
    • Proof: bash tests/fm-teardown.test.sh run from the same clean origin/main worktree produces the identical not ok line and exit=1.

Third previously-named failure (a lock test that only fails when a second suite runs concurrently on the same workstation): not observed in either single-suite run performed for this task (the rebased-branch run or the origin/main baseline run) — consistent with its documented trigger, which requires a second concurrent suite and did not occur here. Not deliberately reproduced: forcing a concurrent collision on this shared machine risks interfering with other lanes currently running here (visible via git worktree list), and doing so isn't required to classify this branch's own result. Flagging as could not independently confirm or refute this run rather than carrying it forward unverified.

No failure is introduced by this branch. Every failing or unavailable check is either a host-environment gap (lsof missing, Herdr version, hooks not wired) or reproduces byte-identically on a clean origin/main worktree.

Verdict

MERGE — the gate is red only for reasons entirely outside this branch's diff (host tooling gaps and a Herdr version mismatch, all reproduced on origin/main); this branch's own change introduces zero new failures. Firstmate/captain merges per standing authority — this comment does not merge anything.

@jk1rby

jk1rby commented Aug 23, 2026

Copy link
Copy Markdown

bin/fm-ci.sh full output — part 1/6

fm-ci.sh: running lint (shell and workflow lint)
fm-lint.sh: ShellCheck 0.11.0 (pinned 0.11.0)
fm-lint-workflows.sh: no GitHub workflow files under /home/jk/.treehouse/firstmate-b4b40d/2/firstmate/.github/workflows; nothing to lint
FM_CI_CHECK lint result=pass duration_ms=125571
fm-ci.sh: running invariants (repository invariants)
fm-ci.sh: repository invariants intact
FM_CI_CHECK invariants result=pass duration_ms=22
fm-ci.sh: running coverage (regression partition is complete)
FM_TEST_COVERAGE ok total=155 parallel=24 serial=119 serial_shards=4 herdr=12
FM_CI_CHECK coverage result=pass duration_ms=1922
fm-ci.sh: running push-guard (pipeline-provenance guard is enforced here)
fm-push-guard.sh: not enforced here: core.hooksPath is unset, expected .githooks.
fm-push-guard.sh: run bin/fm-install-hooks.sh to enforce it in this clone.
FM_CI_CHECK push-guard result=unavailable duration_ms=24
fm-ci.sh: running behavior-parallel (behavior suite, proven-isolated set)
FM_TEST_BEGIN 2026-08-23T22:15:22Z tests/fm-arm-pretool-check.test.sh family=pure-contract-unit expected_gate_skip=none
FM_TEST_BEGIN 2026-08-23T22:15:22Z tests/fm-backend-herdr.test.sh family=backend-dispatch expected_gate_skip=none
FM_TEST_BEGIN 2026-08-23T22:15:22Z tests/fm-brief.test.sh family=pure-contract-unit expected_gate_skip=none
FM_TEST_BEGIN 2026-08-23T22:15:22Z tests/fm-cd-pretool-check.test.sh family=pure-contract-unit expected_gate_skip=none
ok - fm-brief.sh: bash -n succeeds
/tmp/fm-test-run.WLUgAS/w3/tmp/fm-brief.Onz0UH/heredoc-in-substitution.sh:2
ok - fm-brief.sh: no heredoc is nested inside a command substitution (Bash 3.2 parse-safe)
ok - fm-brief.sh: --help renders the complete header
ok - fm-brief.sh: no-mistakes/direct-PR/local-only briefs generate cleanly
ok - fm-brief.sh: ship --mode is required and closed-set validated
ok - fm-brief.sh: the explicit ship mode wins over the registered posture
ok - fm-brief.sh: --yolo and scout/secondmate --mode are refused, never silently dropped
ok - fm-brief.sh: faster paths use configured authority without stacked review
ok - fm-brief.sh: no-mistakes DOD keeps its apostrophe prose, now parse-safe
ok - fm-brief.sh: ship project-memory wording carries the AGENTS.md authoring bar
ok - fm-brief.sh: --herdr-lab emits the complete hard safety contract
ok - fm-brief.sh: --herdr-lab uses its quoted Firstmate-owned helper path
ok - fm-brief.sh: ship and scout scaffolds make omitted Herdr intent fail-visible
ok - fm-brief.sh: Herdr lab contract covers scouts and rejects secondmate misuse
ok - fm-brief.sh: --no-projects scaffolds a project-less charter and guards misuse
ok - fm-brief.sh: marked requests avoid generic acknowledgements and preserve material reporting
ok - fm-brief.sh: relative directory inputs ignore CDPATH, render stable absolute charter paths, or fail loudly
ok - fm-brief.sh: custom pause verb renders in every scaffold
ok - fm-brief.sh: investigation and visual-review completions load the shared decision policy
ok - fm-brief: scout and secondmate code paths still scaffold well-formed briefs
ok - fm-brief.sh: the ship branch is resolved once and rendered consistently
ok - fm-brief.sh: --branch is validated and refused where no branch exists
ok - fm-brief.sh: --forge selects the direct-PR review path
ok - fm-brief.sh: --forge is closed-set and scoped to direct-PR
ok - fm-brief.sh: standing corrections ship with the scaffold instead of being hand-written
ok - fm-brief.sh: the scout report destination is absolute and its alternative is named
ok - fm-brief.sh: --dev-env names where this project's checks are decided
ok - fm-brief.sh: the environment inputs add nothing to a project that has neither
ok - fm-brief.sh: --browser-skill points interface work at the project's own skill
ok - fm-brief.sh: the environment inputs are validated and scoped to crewmate briefs
FM_TEST_END 2026-08-23T22:15:23Z tests/fm-brief.test.sh exit=0 duration_ms=1053 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:15:23Z tests/fm-composer-ghost.test.sh family=pure-contract-unit expected_gate_skip=none
ok - fm_tmux_strip_ghost drops dim/faint runs, keeps normal and bold text
ok - fm_tmux_strip_ghost handles combined SGR, ESC[22m, and reset-then-dim
ok - fm_tmux_strip_ghost keeps bright colored text with 2 payloads
ok - fm_tmux_strip_ghost drops a dark/muted truecolor foreground (grok placeholder)
ok - fm_tmux_strip_ghost keeps muse's near-threshold glyph and its typed text
ok - fm_pane_input_pending: a dim ghost-only composer is NOT pending
ok - fm_pane_input_pending: dim ghost inside a bordered composer is NOT pending
ok - fm_pane_input_pending: normal-intensity typed text is still pending
ok - fm_pane_input_pending: bright colored text with 2 payloads is still pending
ok - fm_pane_input_pending: a dark truecolor ghost-only composer (grok placeholder) is NOT pending
ok - fm_tmux_composer_state: dark truecolor shell prompts read unknown
ok - fm_pane_input_pending: real text plus a trailing ghost run is still pending
ok - fm_tmux_composer_state: text above an empty cursor row is pending
ok - fm_tmux_composer_state: a message wrapped across three rows is pending
ok - fm_tmux_composer_state: a proven titled box tolerates a bottom-border cursor
ok - fm_tmux_composer_identity: unknown busy state cannot become idle identity
ok - fm_tmux_composer_state: typed Pi and Grok busy signatures inside a box are pending
ok - fm_tmux_composer_state: a bare busy-footer row reads unknown (strict container-proof rule)
ok - fm_tmux_composer_state: an unbounded bordered box fails closed as unknown
ok - fm_tmux_composer_state: clipped and asymmetric composer edges fail closed
ok - fm_tmux_composer_state: inconsistent box geometry fails closed
ok - fm_tmux_composer_state: misaligned box bounds fail closed
ok - fm_tmux_composer_state: unproved ghost and malformed geometry stay unknown while styled placeholder-like text stays pending-unproven
ok - fm_tmux_composer_state: differing widths prefer pending or unknown, never empty
ok - fm_tmux_composer_state: emoji and CJK text remain pending under the C locale
ok - fm_tmux_composer_state: all tmux harnesses share empty and pending classification
ok - fm_pane_input_pending: unrecognized states defer by default
ok - fm_tmux_composer_state: one capture feeds the classifier; no band-capture race remains
ok - fm_tmux_composer_state: absent Pi identity preserves Claude's enclosed bare verdict
ok - fm_tmux_composer_state: only proven structural and non-bordered empty routes stay empty
ok - fm_tmux_composer_state: panes without bordered structure retain compatibility fallback
ok - fm_tmux_composer_state: interior edge glyphs retain non-bordered fallback
ok - fm-peek output is escape-free (no raw -e bytes reach firstmate context)
FM_TEST_END 2026-08-23T22:15:25Z tests/fm-composer-ghost.test.sh exit=0 duration_ms=1222 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:15:25Z tests/fm-composer-lib.test.sh family=pure-contract-unit expected_gate_skip=none
ok - fm_composer_classify_content: a bare shell prompt glyph (>/$/%/#) reads unknown, never empty
ok - fm_composer_classify_content: stripped unbordered content is unknown except verified agent glyphs
ok - fm_composer_classify_content: a bare shell prompt carrying a command is not empty
ok - fm_composer_classify_content: a bare prompt glyph inside a bordered composer box reads empty (claude's own idle composer)
ok - fm_composer_classify_content: agent prompt glyphs (❯ claude, › codex, ⟩ muse) read empty bordered or bare
ok - fm_composer_classify_content: an empty composer reads empty
ok - fm_composer_classify_content: idle matching is limited to proven placeholder positions
ok - fm_composer_classify_content: idle matching preserves the caller's case mode
ok - fm_composer_classify_content: real unsubmitted text reads pending (including a popup argument-hint fill)
ok - matrix: claude's ❯+NBSP row reads empty on every profile in both locales (#1988)
ok - matrix: codex's dim hint is empty when styling proves it, unknown (never pending) when it cannot
ok - matrix: muse's ⟩ reads empty everywhere and survives losing the styled-glyph signal
ok - matrix: cursor's reverse-video placeholder remnant reads empty; real typed text stays pending
ok - matrix: herdr half-block rules bound a bare composer's wrap region
ok - matrix: pi's separated composer needs identity + structure; the blank row alone never proves it
ok - matrix: opencode's left-bar composer reads empty everywhere and scans the full active run
ok - matrix: grok's titled bottom border is tolerated as a title, not read as ambiguity
ok - matrix: kimi's bordered shell-glyph box reads empty through the shared owner (spawn's fourth copy retired)
ok - matrix: the real claude-in-zellij --ansi dump reads empty in both locales
ok - strict posture: blank and unidentified rows are unknown, never injectable empty
ok - fm_composer_classify_screen: the bare composer's wrap region stays identified; structure breaks it
ok - fm_composer_classify_screen: a row-leading agent glyph reanchors the live composer
ok - fm_composer_classify_screen: a lower dead shell invalidates only cursorless stale composers
ok - fm_composer_classify_screen: cursorless bare wrap regions participate in verdicts
ok - fm_composer_classify_screen: cursorless containers reject only contiguous unclaimed activity
ok - fm_composer_classify_screen: the bottom-most candidate wins; stale banners cannot
ok - fm_composer_classify_screen: incomplete lower structure invalidates stale boxes
ok - fm_composer_classify_screen: titled bottoms retain full box geometry
ok - fm_composer_classify_screen: a proven box tolerates a bottom-border cursor
ok - fm_composer_extract_selected_content: scopes user content and excludes furniture
FM_TEST_END 2026-08-23T22:15:27Z tests/fm-composer-lib.test.sh exit=0 duration_ms=2693 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:15:27Z tests/fm-crew-state.test.sh family=pure-contract-unit expected_gate_skip=none
ok - active run-step is authoritative
ok - stale needs-decision over active run is superseded
ok - stale blocked over active run is superseded
ok - genuine parked run is not flagged superseded
ok - scalar gate parked run is not flagged superseded
ok - gate block parked run is not flagged superseded
ok - ci-ready status log beats monitoring run
ok - ci-monitoring run with checks already green surfaces done
ok - top-level ci status uses ci log green marker
ok - terminal no-checks ci-monitor marker surfaces done
ok - base-advance rearm after green stays working
ok - pending no-checks ci-monitor marker stays working
ok - ci-monitoring run with checks not yet green stays working
ok - a fresh issue after an earlier green reading is not masked
ok - stale checks-green status log does not mask CI relapse
ok - ci fixing is not overridden by an earlier green marker
ok - top-level fixing is not overridden by a stale ci running row
ok - top-level fixing is not overridden by a stale done log
ok - terminal passed run is authoritative
ok - terminal failed run is authoritative
ok - cross-branch run is attributed via the real runs list
ok - cross-branch attribution picks the branch's most recent row
ok - coarse run does not probe another branch's ci log
ok - another branch's run is ignored, falls back
ok - no run + a busy semantic record reads working, attributed to its source
ok - a converted adapter never reads working from rendered footer text
ok - grok still reads working through its isolated rendered-tail fallback
ok - herdr's native busy verdict reads working with no record present
ok - a mid-tool-call crew stays working because its record outranks herdr's generation state
ok - an idle record with idle agent_status stays not-busy (no regression for a human-blocked agent)
ok - no run + idle pane uses the status-log verb
ok - no run + idle pane parses keyed status syntax
ok - no run + idle pane on a paused: status reports state: paused with its reason
ok - no run + idle pane honors the configured paused verb
ok - a trailing resolved: event does not corrupt state render (idle stays idle)
ok - dead window ignores stale status log
ok - closed pane still reports a terminal run-step
ok - closed pane still reports an active run-step
ok - no timeout command uses perl bound
ok - scout skips the run lookup
ok - torn-down worktree is handled gracefully
ok - fm-crew-state remote: alive endpoint falls through to the routed status log
ok - fm-crew-state remote: an idle alive endpoint reads alive, never gone or dead
ok - fm-crew-state remote: an unreachable host reads unknown-remote, never gone or dead
ok - fm-crew-state remote: the remote host's own dead verdict is reported truthfully
ok - missing meta is handled gracefully
ok - crew_is_provably_working absorbs a validating crew found only via the runs-list fallback
ok - crew_is_provably_working still surfaces a genuinely stopped crew (safety property preserved)
ok - usage error exits 2
ok - historical same-branch rewritten head is not attributed as current
ok - active run with valid descendant fix head remains current
ok - local work advanced past run head invalidates attribution
ok - missing run head falls back instead of matching by branch
all fm-crew-state tests passed
FM_TEST_END 2026-08-23T22:15:32Z tests/fm-crew-state.test.sh exit=0 duration_ms=4547 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:15:32Z tests/fm-decision-hold-lifecycle.test.sh family=pure-contract-unit expected_gate_skip=none
ok - cd-guard acceptance matrix: 63 cases x 5 harness entry forms, block/allow all correct
ok - cd-guard: fires in a secondmate home (its own primary session is a primary)
ok - cd-guard: inert in a crewmate/scout task worktree (linked git worktree)
ok - cd-guard: inert in a non-firstmate repo (no AGENTS.md)
ok - cd-guard: inert when not inside a git repo
ok - cd-guard: reproduces the cwd leak and denies the exact command that causes it
ok - cd-guard: fails open on empty stdin
ok - cd-guard: fails open on unparseable stdin JSON
ok - cd-guard: fails open (never blocks) when node is missing
ok - cd-guard: fails open on the stdin path when jq is missing
ok - cd-guard: prefilter fast-allows (skips node) when no cd/pushd/popd substring is present
ok - cd-guard: fm-cd-command-policy.mjs CLI honors the deny/allow output contract
ok - bin/fm-cd-pretool-check.sh is clean under bin/fm-lint.sh
FM_TEST_END 2026-08-23T22:15:32Z tests/fm-cd-pretool-check.test.sh exit=0 duration_ms=9719 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:15:32Z tests/fm-ensure-agents-md.test.sh family=pure-contract-unit expected_gate_skip=none
ok - fm-ensure-agents-md.sh: created AGENTS.md includes self-governance section
ok - fm-ensure-agents-md.sh: fresh setup writes a real @AGENTS.md pointer
ok - fm-ensure-agents-md.sh: promoted CLAUDE.md includes self-governance section
ok - fm-ensure-agents-md.sh: newline-less promotion keeps a blank separator line
ok - fm-ensure-agents-md.sh: existing symlinked AGENTS.md gains the section idempotently
ok - fm-ensure-agents-md.sh: correct symlink migrates to pointer without clobbering AGENTS.md
ok - fm-ensure-agents-md.sh: existing AGENTS.md without CLAUDE.md gains section and pointer
ok - fm-ensure-agents-md.sh: AGENTS.md that already has the section stays unchanged
ok - fm-ensure-agents-md.sh: CRLF AGENTS.md with the section stays unchanged
ok - fm-ensure-agents-md.sh: CRLF injection preserves line endings idempotently
ok - fm-ensure-agents-md.sh: canonical real CLAUDE.md pointer is not a conflict
ok - fm-ensure-agents-md.sh: refuses distinct real AGENTS.md and CLAUDE.md
ok - fm-ensure-agents-md.sh: refuses AGENTS.md when it is a symlink
ok - fm-ensure-agents-md.sh: refuses a CLAUDE.md symlink that does not point to AGENTS.md
ok - fm-ensure-agents-md.sh: refuses a non-regular CLAUDE.md
ok - fm-ensure-agents-md.sh: refuses a case-variant lowercase agents.md (issue #389)
FM_TEST_END 2026-08-23T22:15:32Z tests/fm-ensure-agents-md.test.sh exit=0 duration_ms=194 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:15:32Z tests/fm-grok-harness.test.sh family=pure-contract-unit expected_gate_skip=none
ok - fm_backend_herdr_version_check: accepts the current protocol (14)
ok - fm_backend_herdr_version_check: refuses an old protocol loudly
ok - fm_backend_herdr_version_check: refuses loudly when herdr is not installed
ok - fm_backend_herdr_workspace_label: a primary home (no marker) resolves to 'firstmate'
ok - fm_backend_herdr_workspace_label: a secondmate home (.fm-secondmate-home) resolves to '2ndmate-<id>'
ok - fm_backend_herdr_workspace_label: trims whitespace around the marker's secondmate id
ok - fm_backend_herdr_workspace_label: an empty marker file falls back to the primary label 'firstmate'
ok - fm_backend_herdr_workspace_label: two different secondmate homes get two different, non-colliding labels
ok - fm_backend_herdr_cli: sets HERDR_SESSION AND appends a trailing --session flag on every call
ok - fm_backend_herdr_launcher_identity: a firstmate not running inside herdr has no launcher workspace to inherit
ok - fm_backend_herdr_launcher_identity: HERDR_ENV=1 without a pane id selects the backend but binds no parent
ok - fm_backend_herdr_launcher_identity: resolves the launcher's exact workspace even when a same-labeled workspace sorts first
ok - fm_backend_herdr_launcher_identity: refuses a launcher pane that names a different herdr session
ok - fm_backend_herdr_launcher_identity: refuses a claimed pane without exact server identity
ok - fm_backend_herdr_launcher_identity: refuses a launcher pane whose injected socket belongs to another herdr server
ok - fm_backend_herdr_launcher_identity: refuses when the launcher's own pane no longer resolves
ok - fm_backend_herdr_launcher_identity: refuses when the launcher's pane and tab disagree about their workspace
ok - fm_backend_herdr_launcher_identity: refuses when the launcher's workspace is gone from its own session
ok - fm_backend_herdr_workspace_ensure: places a worker in the launcher's exact workspace, not the first same-labeled one
ok - fm_backend_herdr_workspace_ensure: refuses to guess between two same-labeled home workspaces
ok - fm_backend_herdr_workspace_ensure: a --secondmate container resolves that home's own workspace, not the launcher's
ok - fm_backend_herdr_container_ensure: surfaces the exact ambiguous-placement refusal instead of a generic failure
ok - fm_backend_herdr_container_ensure: version-gates, starts the server, ensures the firstmate workspace, echoes session:workspace_id + the seeded default tab id
ok - fm_backend_herdr_container_ensure: reuses an existing firstmate workspace without recreating it, and reports no seeded default tab (adopted, not created)
ok - fm_backend_herdr_container_ensure: workspace create passes --no-focus
ok - fm_backend_herdr_container_ensure: creates the workspace under the SECONDMATE home's own label, not 'firstmate'
ok - fm_backend_herdr_create_task: prunes exactly the seeded default tab container_ensure identified, once the first real task tab exists
ok - herdr repeated spawn/teardown: one persistent firstmate workspace reused, zero orphans, default tab pruned, create ran once
ok - fm_backend_herdr_create_task: an ADOPTED workspace's pre-existing tab is never pruned (the created-vs-adopted gate)
ok - fm_backend_herdr_create_task: the label-collision startup-workspace scenario (2026-07-02 incident) leaves the captain's live tab untouched
ok - fm_backend_herdr_workspace_prune_seeded_default_tab: refuses to close the seeded default tab when its pane reports a working agent (defense in depth)
ok - fm_backend_herdr_create_task: refuses a duplicate tab label (herdr's own tab create has no uniqueness check)
ok - fm_backend_herdr_create_task: a same-labeled tab with a live (even idle) registered agent still refuses exactly as before
ok - fm_backend_herdr_create_task: scans every same-labeled tab and refuses if any duplicate is live
ok - fm_backend_herdr_create_task: closes and replaces a same-labeled tab whose pane is dead (pane_not_found)
ok - fm_backend_herdr_create_task: closes and replaces a same-labeled tab whose pane is alive but hosts no registered agent (a restored plain shell)
ok - fm_backend_herdr_create_task: closes every confirmed same-labeled husk only after creating the replacement
ok - fm_backend_herdr_create_task: refuses success when a preexisting husk tab remains after replacement
ok - fm_backend_herdr_create_task: refuses (fail-safe) rather than guessing when the duplicate's agent state cannot be classified confidently
ok - fm_backend_herdr_create_task: creates the replacement tab BEFORE closing the husk tab, never the reverse
ok - fm_backend_herdr_create_task: creates a tab and parses tab_id/pane_id from the JSON response, prunes nothing when no seeded tab id is given
ok - fm_backend_herdr_create_task: tab create passes --no-focus
ok - herdr presentation: a home that set nothing gets the projection by default at or above the floor
ok - herdr presentation: an unconfigured home below the floor falls back flat with one naming warning
ok - herdr presentation: an unreadable client release falls back flat instead of guessing
ok - herdr presentation: a deliberate opt-in is never silently downgraded below the floor
ok - herdr presentation: an explicit off opts the home out
ok - herdr presentation: an unrecognized value warns and follows the default instead of failing a spawn
ok - herdr presentation: the below-floor warning is one per home per release, not one per spawn
ok - herdr presentation: warning marker publication is atomic, symlink-safe, and fails visible
ok - herdr presentation: client and selected server floors compose conservatively without overriding explicit opt-in
ok - herdr presentation floor: every measured release, and each signal alone, classifies correctly
ok - herdr presentation floor: either signal alone can carry an above verdict, and each divergence is real
ok - herdr presentation: config parsing separates a deliberate choice from an unconfigured default
ok - herdr presentation journal: atomically publishes one non-authoritative 128-bit correlator and refuses overwrite
ok - herdr presentation journal: version 2 binds exact home/endpoint/parent identities and advances atomically
ok - herdr presentation create: exact response IDs yield one normal task pane with no workspace-close authority
ok - herdr presentation create: concurrent same-label tabs are never prune targets
ok - herdr presentation focus: snapshot requires one exact active workspace and tab
ok - herdr presentation focus: exact pane close restores the exact prior workspace and tab
ok - herdr presentation focus: cleanup refuses rather than close the captain's active tab
ok - herdr presentation focus: pane close fails when exact focus restoration fails
ok - herdr presentation reclaim: live agent state at the close boundary refuses mutation
ok - herdr presentation cleanup: emptying close behind focus ends the exact shell without a move or focus change
ok - herdr presentation cleanup: emptying close before focus moves the doomed workspace to the end and ends its exact shell
ok - herdr presentation cleanup: emptying close with the focused workspace last skips the move
ok - herdr presentation cleanup: emptying close of the last workspace skips the move
ok - herdr presentation cleanup: a non-emptying close stays plain with no proof, move, or signal
ok - herdr presentation cleanup: no-move plain close requires structured pane removal
ok - herdr presentation cleanup: an ambiguous workspace layout falls back to the plain close
ok - herdr presentation cleanup: a failed repositioning move falls back to the plain close with a warning
ok - herdr presentation cleanup: a pane with a live foreground process falls back to the plain close
ok - herdr presentation cleanup: a transient prompt helper settles into the pane-death path instead of the plain close
tests/fm-backend-herdr.test.sh: line 1846: 2317384 Killed                     bash -c 'trap "" HUP; sleep 300'
ok - herdr presentation cleanup: a SIGHUP-surviving shell is escalated to SIGKILL before giving up
tests/fm-backend-herdr.test.sh: line 1884: 2318477 Killed                     bash -c 'trap "" HUP; sleep 300'
ok - herdr presentation cleanup: a failed pane-death close falls back to the plain close
tests/fm-backend-herdr.test.sh: line 1917: 2319983 Hangup                     sleep 300
ok - herdr presentation cleanup: the exact-tab restore remains the backstop behind the pane-death close
ok - herdr presentation cleanup: SIGKILL never reaches a pid the exact pane no longer owns
ok - herdr presentation cleanup: every unconfirmed removal restores the exact original workspace order and reports failure
tests/fm-backend-herdr.test.sh: line 2076: 2325742 Hangup                     sleep 300
ok - fm_backend_herdr_kill: one session lock covers the focus-safe emptying removal
ok - fm_backend_herdr_kill: killing the focused workspace's tab keeps the legitimate plain close
ok - endpoint confirmed-gone: only structured not-found permits record removal and ambiguous identity refuses
ok - fm_backend_herdr_kill: unavailable session locks defer every pane close
ok - herdr presentation focus: projected seeded pruning refuses the active tab
ok - herdr presentation labels: └ concise-task · p:<full-token> for primary and secondmate children
ok - herdr presentation ordering: exact new workspace appends to the primary block while focus and relative orders stay stable
ok - herdr presentation ordering: secondmate children append under their owning parent block
ok - herdr presentation ordering: a foreign legacy child is warning-only and read-only
ok - herdr presentation ordering: intervening parent child blocks remain traversable
ok - herdr presentation ordering: only the exact new id moves; human spaces keep relative order
ok - herdr presentation ordering: move failure warns, returns success, and grants no cleanup authority
ok - herdr presentation ordering: an ambiguous existing worker block is warning-only and read-only
ok - herdr presentation ordering: the launcher's exact parent workspace id disambiguates a duplicated home label without moving anything
ok - herdr presentation ordering: a foreign new-format child is warning-only and read-only
ok - herdr presentation ordering: missing owning parent is warning-only and read-only
ok - herdr presentation lock: one path per session/socket across homes
ok - herdr presentation lock: null and missing socket paths fail closed
ok - herdr presentation ordering: malformed socket metadata is warning-only and read-only
ok - herdr presentation reclaim: legacy, cross-home, ambiguous, live/unknown, and focus-unknown cases refuse without mutation
ok - herdr presentation reclaim: exact agent-free husk survives duplicate parent labels while its sibling stays untouched
warning: 2 exact herdr presentation token matches for task-p3 are quarantined; inspecting only for duplicate-agent risk
warning: quarantined herdr presentation for task-p3 is dead or agent-free; exact bound reclaim may proceed, otherwise spawning flat
ok - herdr presentation recovery: duplicate-token inspection is read-only and live-agent risk refuses fallback
ok - fm_backend_herdr_workspace_find: matches only THIS home's own label among several coexisting workspaces
ok - fm_backend_herdr_list_live: scoped to this home's own workspace, never a sibling home's
ok - fm_backend_herdr_parse_target: splits '<session>:<pane_id>' on the FIRST colon (pane_id itself contains one)
ok - fm_backend_herdr_normalize_key: Enter/Escape/C-c map to herdr's verified enter/escape/ctrl+c
ok - fm_backend_herdr_capture: calls 'pane read <pane> --source recent --lines N' with the session set
ok - fm_backend_herdr_capture: works around the verified small-N '--lines' bug by over-fetching and trimming locally
ok - fm_backend_herdr_capture: ensures the session and preserves pane read failure
ok - fm_backend_herdr_send_key: normalizes the key and targets the right pane
ok - fm_backend_herdr_kill: calls pane close and stays best-effort on failure
ok - fm_backend_herdr_current_path: reads pane foreground_cwd (the live running process), not the frozen creation-time cwd
ok - fm_backend_herdr_current_path: falls back to cwd when the build emits no foreground_cwd
ok - fm_backend_herdr_current_path: an empty foreground_cwd falls back to cwd
ok - fm_backend_herdr_current_path: yields empty when neither field carries a path
ok - fm_backend_herdr_busy_state: working -> busy
ok - fm_backend_herdr_busy_state: done -> idle, blocked -> idle (surfaced like a stale pane, not suppressed as busy)
ok - fm_backend_herdr_busy_state: unparseable/absent agent state reports unknown, the regex-fallback cue
ok - fm_backend_herdr_composer_state: a bare '❯' composer row reads empty
ok - fm_backend_herdr_composer_state: bright placeholder-like text stays pending rather than being mistaken for an idle ghost
ok - fm_backend_herdr_composer_state: real composer text reads pending
ok - fm_backend_herdr_composer_state: a slash-command popup's argument-hint placeholder still reads pending (the incident fix)
ok - fm_backend_herdr_composer_state: reports unknown when the pane cannot be captured
ok - fm_backend_herdr_composer_state: reports unknown for bare shell prompts with no composer row
ok - fm_backend_herdr_composer_state: a native idle Pi separator composer reads empty
ok - fm_backend_herdr_composer_state: real Pi composer text remains pending
ok - fm_backend_herdr_composer_state: an incomplete lower Pi separator cannot inherit a stale empty row
ok - fm_backend_herdr_composer_state: Pi separators never authorize working, non-Pi, unreadable, or over-tall targets
ok - fm_backend_herdr_composer_state: a real-claude unbordered '❯' prompt row (no border box in view) reads empty
ok - fm_backend_herdr_composer_state: a real-claude unbordered '❯ <text>' prompt row reads pending
ok - fm_backend_herdr_composer_state: a live unbordered prompt row below a stale bordered decorative box still wins (not misread as the box's own row)
ok - fm_backend_herdr_composer_state: claude's dim prompt-suggestion ghost (the overnight wedge shape) reads empty
ok - fm_backend_herdr_composer_state: real typed text on the same claude prompt row still reads pending
ok - fm_backend_herdr_composer_state: grok's dark-truecolor placeholder (the TRUECOLOR gap) reads empty
ok - fm_backend_herdr_composer_state: grok's real bright typed input still reads pending
ok - fm_backend_herdr_composer_state: a real-codex unbordered '›' prompt row reads empty
ok - fm_backend_herdr_composer_state: a faint real-codex ghost suggestion reads empty
ok - fm_backend_herdr_composer_state: non-faint codex prompt text still reads pending
ok - fm_backend_herdr_wait_for_working: reports 'busy' immediately on the first poll, without spending the rest of the budget
ok - fm_backend_herdr_wait_for_working: a slow transition landing on a later sample within one window is still caught (robust against the 'slow transition' failure direction)
ok - fm_backend_herdr_wait_for_working: spreads six samples across the full budget endpoint without a final trailing sleep
ok - fm_backend_herdr_send_text_submit: applies the herdr minimum confirmation budget before polling agent-state
ok - fm_backend_herdr_wait_for_working: reports 'idle' (readable, genuinely not yet working) when 'busy' never appears
ok - fm_backend_herdr_wait_for_working: reports 'unknown' (a hard read failure, not a timing race) only when EVERY poll in the window fails
ok - fm_backend_herdr_wait_for_working: treats blocked as submit-active for confirmation without changing watcher busy-state semantics
ok - fm_backend_herdr_send_text_submit: reports 'empty' once agent_status reports working after one Enter, without ever reading the composer
ok - fm_backend_herdr_send_text_submit: reports 'pending' when agent_status never reports working after retried Enters (swallowed)
ok - fm_backend_herdr_send_text_submit: a slash-command popup's placeholder fill on Enter #1 never flips agent_status to working, so it does not short-circuit as submitted; Enter #2 is retried and lands it
ok - fm_backend_herdr_send_text_submit: a post-Enter blocked state confirms delivery without retrying into the prompt
ok - fm_backend_herdr_send_text_submit: preexisting working is not accepted as submit proof when the composer still holds the message
ok - fm_backend_herdr_composer_state: cursor's mid-turn placeholder-plus-busy-token row reads pending (why delivery needs a separate signal)
ok - fm_backend_herdr_rendered_busy_state: busy/idle/unknown from the rendered footer, with an unreadable pane never reading idle
ok - fm_backend_herdr_send_text_submit: a rendered-footer idle-to-busy transition confirms delivery when native agent-state never reports idle
ok - fm_backend_herdr_send_text_submit: an already-busy footer baseline is never accepted as proof that this Enter landed
ok - fm_backend_herdr_send_text_submit: confirms submission via native agent-state alone, immune to a codex-style dynamic idle-tip composer that would have misread as 'pending' under the old composer-based confirmation
ok - fm_backend_herdr_composer_state: a faint real-codex dynamic idle-tip composer row reads empty
ok - fm_backend_composer_state (herdr): the pre-injection empty-box guard still refuses a genuinely non-empty composer, unaffected by the submit-confirmation change
ok - fm_backend_herdr_send_text_submit: a slow transition landing on a later sample within one Enter's budget is confirmed WITHOUT sending a needless extra Enter
ok - fm_backend_herdr_send_text_submit: reports 'send-failed' when the literal send-text call itself errors
ok - fm_backend_herdr_send_text_submit: reports 'unknown' when the post-Enter agent-get read fails (never retries past an unreadable target)
ok - fm_backend_validate: herdr is a known backend (P2)
ok - fm_backend_busy_state: tmux (no native primitive) always reports unknown, preserving the P1 regex-only path
error: unknown backend 'bogus' (known: tmux herdr zellij orca cmux)
ok - fm_backend_composer_state dispatches every backend to its named thin classifier, unknown for unrecognized backends
ok - fm-peek/fm-send: explicit stale targets matching metadata use the recorded backend
ok - fm_backend_herdr_normalize_event routes through the shared record with an empty from_status
ok - fm_backend_herdr_escalation_marker keys the dedupe marker exactly like the watcher's .stale-<key>
ok - fm_backend_herdr_apply_transition: blocked dedupe starts only after explicit commit
ok - fm_backend_herdr_apply_transition: a working edge clears the marker so the next ->blocked re-escalates
ok - fm_backend_herdr_clear_transition removes task-owned dedupe state
ok - fm_backend_herdr_apply_transition: idle/done (defer) and unknown/empty (fallback) take no fast action
ok - fm_backend_herdr_wait_transition: a home with no herdr panes falls back to polling (rc 2)
ok - fm_backend_herdr_wait_transition: below-capability protocol/schema falls back to polling (rc 2)
ok - fm_backend_herdr_wait_transition: reconnect level-reconcile returns an uncommitted blocked pane
ok - fm_backend_herdr_wait_transition: subscribes before reconnect level-reconcile
ok - fm_backend_herdr_wait_transition: a still-blocked, already-escalated pane is not re-delivered on reconnect
ok - fm_backend_herdr_wait_transition: a streamed ->blocked edge returns the record sub-poll
ok - fm_backend_herdr_wait_transition: streamed working clears the marker, idle/done are deferred (clean timeout)
ok - fm_backend_herdr_wait_transition: a reader/subscribe failure falls back to polling (rc 2)
ok - fm_backend_herdr_wait_transition: Bash 3.2-safe bad-ack path closes fd 9 and removes its FIFO
ok - fm_backend_herdr_wait_transition: stock macOS Bash clean timeout closes fd 9 and returns 1
FM_TEST_END 2026-08-23T22:15:35Z tests/fm-backend-herdr.test.sh exit=0 duration_ms=12337 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:15:35Z tests/fm-herdr-lab.test.sh family=pure-contract-unit expected_gate_skip=none
ok - grok global hook requires a firstmate registry token
ok - grok teardown removes pointer and token state
ok - fm-lock recognizes grok harness processes
FM_TEST_END 2026-08-23T22:15:37Z tests/fm-grok-harness.test.sh exit=0 duration_ms=4334 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:15:37Z tests/fm-lint.test.sh family=pure-contract-unit expected_gate_skip=none
ok - fm-lint.sh --list-files reports the complete shell inventory
ok - fm-lint.sh pins an explicit ShellCheck version (0.11.0)
ok - ShellCheck installer retries a transient download failure
ok - ShellCheck installer selects the official archive, URL, and checksum per OS/arch
ok - ShellCheck installer rejects a wrong checksum
ok - ShellCheck installer falls back to shasum -a 256 when sha256sum is absent
ok - ShellCheck installer prefers sha256sum when both hashers are present
ok - ShellCheck installer rejects an unsupported OS or architecture
ok - fm-lint.sh refuses to lint under a non-pinned ShellCheck version
ok - fm-lint.sh catches a real lint defect the old no-op gate passed
ok - fm-lint.sh ignores ambient ShellCheck options
ok - fm-lint.sh passes a clean fixture
ok - jobs=1 and jobs=2 preserve deterministic diagnostics, failures, cleanup bounds, and quiet telemetry
ok - jobs=1 and jobs=2 stop complete worker trees with and without telemetry
ok - seeded dispatcher, adapter, production-owner, and test-local diagnostics preserve parity
ok - fm-lint.sh changed mode lints only the changed canonical file
ok - fm-lint.sh forces a full lint in CI even when the local diff would be empty
ok - fm-lint.sh forces a full lint when HEAD is on main
ok - fm-lint.sh explicit paths bypass changed-file mode selection
ok - fm-lint.sh exits 0 with a note when the local branch has no changed lint targets
ok - fm-lint.sh --list-files reports the would-be changed set in changed mode
FM_TEST_END 2026-08-23T22:15:39Z tests/fm-lint.test.sh exit=0 duration_ms=1964 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:15:39Z tests/fm-pi-primary-types.test.sh family=pure-contract-unit expected_gate_skip=none
skip: tsc not found for Pi extension typecheck
FM_TEST_END 2026-08-23T22:15:39Z tests/fm-pi-primary-types.test.sh exit=0 duration_ms=9 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:15:39Z tests/fm-pr-merge.test.sh family=pr-forge expected_gate_skip=none
ok - fm-herdr-lab: names fail closed and require the lab prefix
ok - fm-herdr-lab: provisioning, scoped calls, guarded teardown, and fleet tripwire are deterministic
ok - fm-herdr-lab: missing tripwire refuses teardown before any Herdr call
ok - fm-herdr-lab: changed default fleet state is a hard failure
ok - fm-herdr-lab: an owned stopped lab can re-provision safely
ok - fm-herdr-lab: failed deletion retains ownership until absence is confirmed
ok - fm-herdr-lab: timed-out provisioning cancels the launch before teardown
FM_TEST_END 2026-08-23T22:15:40Z tests/fm-herdr-lab.test.sh exit=0 duration_ms=5744 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:15:40Z tests/fm-review-diff.test.sh family=pr-forge expected_gate_skip=none
ok - fm-pr-merge records pr= and pr_head= before invoking gh-axi pr merge
ok - fm-pr-merge propagates a real merge failure without silently succeeding
ok - fm-pr-merge forwards extra flags to gh-axi pr merge after the -- separator
ok - fm-pr-merge refuses before merging when task meta is missing
ok - fm-pr-merge refuses malformed PR URLs before calling gh-axi
ok - fm-pr-merge refuses unsafe PR URL segments before recording state
ok - fm-pr-merge refuses repo override args before recording state
ok - fm-pr-merge does not add default --squash when the caller passes an explicit merge method
ok - fm-pr-merge respects --method=<value> as an explicit merge method
ok - fm-pr-merge parses a GitHub PR URL into gh-axi number and --repo arguments
FM_TEST_END 2026-08-23T22:15:41Z tests/fm-pr-merge.test.sh exit=0 duration_ms=2250 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:15:41Z tests/fm-send-popup-settle.test.sh family=pure-contract-unit expected_gate_skip=none
ok - matrix A01: allow through all five entry forms
ok - matrix A02: allow through all five entry forms
ok - matrix A03: allow through all five entry forms
ok - matrix A04: allow through all five entry forms
ok - matrix A05: allow through all five entry forms
ok - matrix A06: allow through all five entry forms
ok - matrix A07: allow through all five entry forms
ok - matrix A08: allow through all five entry forms
ok - matrix A09: allow through all five entry forms
ok - matrix A10: allow through all five entry forms
ok - matrix A11: allow through all five entry forms
ok - matrix A12: allow through all five entry forms
ok - matrix A13: allow through all five entry forms
ok - matrix A14: allow through all five entry forms
ok - matrix A15: allow through all five entry forms
ok - matrix A16: allow through all five entry forms
ok - matrix A17: allow through all five entry forms
ok - matrix R01: allow through all five entry forms
ok - matrix R02: allow through all five entry forms
ok - matrix R03: allow through all five entry forms
ok - matrix R04: allow through all five entry forms
ok - matrix R05: allow through all five entry forms
ok - matrix R06: allow through all five entry forms
ok - matrix R07: allow through all five entry forms
ok - matrix R08: allow through all five entry forms
ok - matrix R09: allow through all five entry forms
ok - matrix R10: allow through all five entry forms
ok - matrix R11: allow through all five entry forms
ok - matrix R12: allow through all five entry forms
ok - matrix R13: allow through all five entry forms
ok - matrix R14: allow through all five entry forms
ok - matrix R15: allow through all five entry forms
ok - matrix R16: allow through all five entry forms
ok - matrix R17: allow through all five entry forms
ok - matrix R18: allow through all five entry forms
ok - matrix R19: allow through all five entry forms
ok - matrix D01: deny through all five entry forms
ok - matrix D02: deny through all five entry forms
ok - matrix D03: deny through all five entry forms
ok - matrix D04: deny through all five entry forms
ok - matrix D05: deny through all five entry forms
ok - matrix D06: deny through all five entry forms
ok - matrix D07: deny through all five entry forms
ok - matrix D08: deny through all five entry forms
ok - matrix D09: deny through all five entry forms
ok - matrix D10: deny through all five entry forms
ok - matrix D11: deny through all five entry forms
ok - matrix D12: deny through all five entry forms
ok - matrix D13: deny through all five entry forms
ok - matrix D14: deny through all five entry forms
ok - matrix D15: deny through all five entry forms
ok - matrix D16: deny through all five entry forms
ok - matrix D17: deny through all five entry forms
ok - matrix D18: deny through all five entry forms
ok - matrix D19: deny through all five entry forms
ok - matrix D20: deny through all five entry forms
ok - matrix D21: deny through all five entry forms
ok - matrix D22: deny through all five entry forms
ok - matrix D23: deny through all five entry forms
ok - matrix D24: deny through all five entry forms
ok - matrix D25: deny through all five entry forms
ok - matrix D26: deny through all five entry forms
ok - matrix D27: deny through all five entry forms
ok - matrix D28: deny through all five entry forms
ok - matrix D29: deny through all five entry forms
ok - matrix D30: deny through all five entry forms
ok - matrix D31: deny through all five entry forms
ok - matrix D32: deny through all five entry forms
ok - matrix D33: deny through all five entry forms
ok - matrix D34: deny through all five entry forms
ok - matrix D35: deny through all five entry forms
ok - matrix D36: deny through all five entry forms
ok - matrix D37: deny through all five entry forms
ok - matrix D38: deny through all five entry forms
ok - matrix D39: deny through all five entry forms
ok - matrix D40: deny through all five entry forms
ok - matrix D41: deny through all five entry forms
ok - matrix D42: deny through all five entry forms
ok - matrix D43: deny through all five entry forms
ok - matrix D44: deny through all five entry forms
ok - matrix D45: deny through all five entry forms
ok - matrix D46: deny through all five entry forms
ok - matrix D47: deny through all five entry forms
ok - matrix D48: deny through all five entry forms
ok - matrix D49: deny through all five entry forms
ok - matrix D50: deny through all five entry forms
ok - matrix D51: deny through all five entry forms
ok - matrix D52: deny through all five entry forms
ok - matrix D53: deny through all five entry forms
ok - matrix D54: deny through all five entry forms
ok - matrix D55: deny through all five entry forms
ok - matrix D56: deny through all five entry forms
ok - matrix D57: deny through all five entry forms
ok - matrix D58: deny through all five entry forms
ok - matrix E01: allow through all five entry forms
ok - matrix E02: allow through all five entry forms
ok - matrix E03: allow through all five entry forms
ok - matrix E04: allow through all five entry forms
ok - matrix E05: deny through all five entry forms
ok - matrix E06: deny through all five entry forms
ok - matrix E07: deny through all five entry forms
ok - matrix E08: deny through all five entry forms
ok - matrix E09: deny through all five entry forms
ok - matrix E10: deny through all five entry forms
ok - matrix E11: deny through all five entry forms
ok - matrix E12: allow through all five entry forms
ok - matrix E13: allow through all five entry forms
ok - matrix E14: allow through all five entry forms
ok - matrix E15: allow through all five entry forms
ok - matrix E16: allow through all five entry forms
ok - matrix E17: allow through all five entry forms
ok - direct policy direct-data-pkill: allow
ok - direct policy direct-broad-pkill: deny	broad-watcher-kill
ok - direct policy direct-loop-broad-pkill: deny	broad-watcher-kill
ok - direct policy direct-loop-broad-kill-pgrep: deny	broad-watcher-kill
ok - direct policy direct-loop-no-kill-allowed: allow
ok - direct policy direct-pipeline: deny	watcher-pipeline
ok - direct policy direct-leading-redirection: deny	watcher-redirection
ok - direct policy direct-unclassifiable: deny	unclassifiable-protected-command
ok - direct policy direct-unsupported: deny	unclassifiable-protected-command
ok - direct policy direct-constructed-payload: deny	watcher-nested
ok - direct policy direct-parameter-export: allow
ok - direct policy direct-expanded-arm-blessed: allow
ok - direct policy direct-expanded-arm-background: deny	watcher-background
ok - direct policy direct-expanded-arm-pipeline: deny	watcher-pipeline
ok - direct policy direct-watch-not-blessed: deny	watcher-direct
ok - direct policy direct-watch-expanded: deny	watcher-direct
ok - direct policy direct-watch-safe-shape: deny	watcher-direct
ok - direct policy direct-heredoc-data: allow
ok - direct policy direct-heredoc-watcher: deny	watcher-redirection
ok - --command=<val> equals-form parses correctly
ok - --background is accepted for interface parity and is never itself a deny signal
ok - unknown CLI flag is rejected
ok - stdin grok schema (toolInput.command): denied with Grok-shaped stdout JSON
ok - stdin claude/codex schema (tool_input.command): blessed shape allowed
ok - stdin claude/codex schema (tool_input.command): backgrounded shape denied
ok - stdin: unrelated command is a fast allow
ok - transport prefilter is a strict superset: non-fm-watch fast-allows, every fm-watch and quoting-decoder-marker command reaches the classifier
ok - fail-open: empty stdin
ok - fail-open: unparseable JSON on stdin
ok - fail-open: missing jq on stdin path
ok - fail-open: missing classifier runtime
ok - --claude: stdout empty, stderr carries hookSpecificOutput deny JSON
ok - default mode: stdout carries Grok-shaped decision JSON on deny
ok - allow is silent on both stdout and stderr in default and --claude mode
ok - bin/fm-arm-pretool-check.sh is clean under bin/fm-lint.sh
FM_TEST_END 2026-08-23T22:15:41Z tests/fm-arm-pretool-check.test.sh exit=0 duration_ms=18629 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:15:41Z tests/fm-send-settle.test.sh family=pure-contract-unit expected_gate_skip=none
ok - fm-review-diff falls back to recorded pr_head when pull head cannot be fetched
ok - fm-review-diff fetches refs/pull/<n>/head when pr_head= is absent
ok - fm-review-diff prefers freshly fetched PR head over a stale recorded pr_head=
ok - fm-review-diff without pr= keeps the worktree-branch diff
ok - fm-review-diff falls back to local branch with a warning when PR head is unreachable
FM_TEST_END 2026-08-23T22:15:41Z tests/fm-review-diff.test.sh exit=0 duration_ms=611 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:15:41Z tests/fm-send-strict.test.sh family=backend-dispatch expected_gate_skip=none
ok - fm-send: a successful text send pauses the default 1s after submit
ok - fm-send: FM_SEND_SETTLE=0 produces no settle pause
ok - fm-send: the settle pause is tunable via FM_SEND_SETTLE
ok - fm-send: the --key path never pauses (settle scoped to text submit)
ok - fm-send: a successful Claude Escape records the interrupt lifecycle edge
FM_TEST_END 2026-08-23T22:15:41Z tests/fm-send-settle.test.sh exit=0 duration_ms=559 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:15:41Z tests/fm-spawn-batch.test.sh family=backend-dispatch expected_gate_skip=none
ok - fm-send strict: exact task/lane ids resolve through home metadata
ok - fm-send --key: exit status follows delivery, and an undelivered key never reports success
ok - fm-send strict: unset FM_HOME fails before target resolution
ok - fm-send strict: unresolvable selectors do not fall back to tmux
ok - fm-send strict: prefixless herdr pane ids are rejected before tmux fallback
ok - fm-send strict: unmatched single-colon explicit targets must verify live before sending
ok - fm-send strict: fm-prefixed Herdr sessions remain explicit backend targets
ok - fm-send strict: healthy fm-<id> sends still type once and submit
FM_TEST_END 2026-08-23T22:15:42Z tests/fm-send-strict.test.sh exit=0 duration_ms=808 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:15:42Z tests/fm-supervision-instructions.test.sh family=pure-contract-unit expected_gate_skip=none
ok - fm-send popup-settle: codex $skill -> long settle -> 1.2s
ok - fm-send popup-settle: codex $skill exact task id -> long settle -> 1.2s
ok - fm-send popup-settle: claude $-message -> fast path -> 0.3s
ok - fm-send popup-settle: claude "$5/month" -> fast path -> 0.3s
ok - fm-send popup-settle: explicit target $message -> fast path (unknown harness) -> 0.3s
ok - fm-send popup-settle: claude /command -> long settle (slash unchanged) -> 1.2s
ok - fm-send popup-settle: codex /command -> long settle (slash unchanged) -> 1.2s
ok - fm-send popup-settle: codex plain text -> fast path -> 0.3s
FM_TEST_END 2026-08-23T22:15:42Z tests/fm-send-popup-settle.test.sh exit=0 duration_ms=1090 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:15:42Z tests/fm-test-run.test.sh family=pure-contract-unit expected_gate_skip=none
ok - renderer prints exactly the selected harness block
ok - renderer falls back to unknown.md for unverified harness names
ok - renderer includes read-only, afk, and effective x-mode current-state stanzas
ok - renderer repair-line mode is harness-aware and honors conditional state
ok - renderer preserves every harness ordinary-continuation and missing-cycle repair path
ok - pi-signed keeps its identity while sharing Pi's supervision protocol
ok - grok supervision is Claude-shaped background notify with passive Stop-hook backstop
ok - grok rendered command sources the effective x-mode config
ok - pi supervision snippet renders the effective extension path
FM_TEST_END 2026-08-23T22:15:42Z tests/fm-supervision-instructions.test.sh exit=0 duration_ms=157 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:15:42Z tests/fm-tmux-submit-busy.test.sh family=pure-contract-unit expected_gate_skip=none
ok - batch dispatch re-execs and reports every id=repo pair
ok - batch detection: single pair batches, non-pair rejected, single-task and slash-id stay single
ok - batch dispatch requires the shared ship delivery contract before any pair runs
ok - scout batch refuses ship delivery flags instead of ignoring them
ok - projects/ paths are scoped through the firstmate home for single-task spawn
FM_TEST_END 2026-08-23T22:15:42Z tests/fm-spawn-batch.test.sh exit=0 duration_ms=645 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:15:42Z tests/fm-transition-lib.test.sh family=pure-contract-unit expected_gate_skip=none
ok - fm_transition_record builds a 5-field record and every accessor reads its field
ok - fm_transition_record uses a single TAB between each of the five fields
ok - fm_transition_record scrubs TAB/newline out of fields so the record stays exactly five columns
ok - fm_transition_record tolerates empty workspace/from/agent fields
ok - fm_transition_policy is the single-owner status->action table (blocked=actionable, working=absorb, idle/done=defer, else=fallback)
# fm-transition-lib.test.sh: all assertions passed
FM_TEST_END 2026-08-23T22:15:42Z tests/fm-transition-lib.test.sh exit=0 duration_ms=53 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:15:42Z tests/fm-x-mode.test.sh family=pr-forge expected_gate_skip=none
ok - fm_tmux_submit_enter_core: busy pane + pending composer returns empty (message queued)
ok - fm_tmux_submit_enter_core: idle pane + pending composer stays pending (genuine swallow preserved)
ok - fm_tmux_submit_enter_core: wrapped input retains swallowed-Enter retries
ok - fm_tmux_submit_enter_core: placeholder-like bare input retains swallowed-Enter retries
ok - fm_tmux_submit_enter_core: busy pane clears composer on first Enter - returns empty
ok - fm_tmux_submit_enter_core: idle pane clears composer on first Enter - returns empty as before
ok - fm_tmux_submit_enter_core: busy conversion is limited to proven pending input
ok - fm_tmux_submit_core: failed baseline capture disables busy unknown conversion
ok - fm_tmux_submit_enter_core: pending-unproven retries without busy conversion
ok - fm_tmux_submit_enter_core: unrecognized states skip busy conversion
ok - fm_pane_is_busy: Claude spinner is scoped, multi-frame, and backward-compatible
FM_TEST_END 2026-08-23T22:15:44Z tests/fm-tmux-submit-busy.test.sh exit=0 duration_ms=1842 gate_skip=false
ok - exact suite coverage: --all lists every tests/*.test.sh once
ok - family selection returns a proper subset of the suite
ok - single-script selection lists exactly that path
ok - changed-file selection stays conservative (never silent full suite)
ok - changed selection covers dependents and fails closed for unmapped source
ok - empty changed selection emits deterministic text and JSON summaries
ok - timing markers and JSON artifact are valid
ok - aggregate exit reflects any script failure
ok - gate-skip accounting is honest and non-failing
ok - fail-on-gate-skip converts herdr-not-found into a hard failure
ok - exclude-family drops the named primary family after selection

@jk1rby

jk1rby commented Aug 23, 2026

Copy link
Copy Markdown

bin/fm-ci.sh full output — part 2/6

ok - portable shard union, disjointness, and coverage guard hold
ok - portable serial shards are a deterministic disjoint cover of the serial lane
ok - portable serial shard lanes refuse mismatched, out-of-range, and countless names
ok - --jobs refuses non-proven / stateful selections
ok - jobs scheduler runs proven scripts; failure propagates; non-proven refused
ok - the coverage guard is locale-independent
ok - aggregate-json merges lane timing artifacts
FM_TEST_END 2026-08-23T22:15:54Z tests/fm-test-run.test.sh exit=0 duration_ms=12330 gate_skip=false
ok - fm-x-poll is a hard no-op without a token (inert default)
ok - fm-x-poll treats an explicitly empty env token as configured
ok - fm-x-poll stays silent on HTTP 204 (the common case)
ok - fm-x-poll lets an explicitly empty relay env override .env
ok - fm-x-poll surfaces auth/config errors once and clears on recovery
ok - fm-x-poll diagnostic markers use private guarded publication
ok - fm-x-poll stashes the question and prints the compact marker
ok - fm-x-poll wakes once per durable request offer across inbox cleanup
ok - fm-x-poll retains offer claim diagnostics until recovery
ok - fm-x-poll preserves in_reply_to conversation context in the inbox
ok - fm-x-poll reports inbox commit failures without emitting a mention wake
ok - fm-x-poll publishes inbox records only through private guarded artifacts
ok - fm-x-poll requires a non-empty question before waking
ok - fm-x-poll rejects an unsafe request_id (path-traversal guard)
ok - fm-x-reply posts a request-bound answer and echoes only the request_id
ok - fm-x-reply accepts the reply via --text-file and stdin (safe, unexpanded)
ok - fm-x-reply exits non-zero on a non-2xx relay response
ok - fm-x-reply cleans up auth header temp files on interrupted posts
ok - fm-x-reply rejects missing arguments with a usage error
ok - fm-x-reply --help makes image support discoverable
ok - fm-x-reply rejects whitespace-only reply text
ok - fm-x-reply dry-run records the would-be reply and never posts
ok - fm-x-reply dry-run works without a token
ok - fm-x-reply honors FMX_DRY_RUN from .env
ok - fm-x-reply lets an explicitly empty dry-run env override .env
ok - fm-x-reply dry-run fails when it cannot record the preview
ok - fm-x-reply dry-run publishes outbox records only through private guarded artifacts
ok - fmx_split_thread: word-boundary, fence-aware, within-limit, numbered, lossless, capped
ok - fm-x-reply keeps a concise reply as a single unnumbered tweet
ok - fm-x-reply auto-splits a long reply into a numbered thread (texts[])
ok - fm-x-reply uses the Discord inbox platform budget instead of the X tweet budget
ok - fm-x-reply keeps numeric X requests on the X tweet budget
ok - fm-x-reply prefers an explicit relay-provided reply limit
ok - fm-x-reply rejects unsafe inbox context artifacts
ok - fm-x-reply clamps a below-floor max to 50 characters
ok - fm-x-reply posts a thread payload (texts[]) to the relay
ok - fm-x-reply --image posts an image object on answer
ok - fm-x-reply streams large image payloads outside curl argv
ok - fm-x-reply dry-run records compact image metadata for threaded replies
ok - fm-x-reply cleans image and payload temp files
ok - fm-x-reply --image rejects missing and unsupported image paths clearly
ok - fm-x-reply --followup posts to /connector/followup with the same request-bound body
ok - fm-x-reply maps a followup_unavailable follow-up 409 to exit 9
ok - fm-x-reply maps every follow-up 409 to exit 9 even without the marker
ok - fm-x-reply treats answer-endpoint 409 as a generic failure
ok - fm-x-reply --followup --image posts an image object
ok - fm-x-reply --followup is accepted in any position and leaves the answer path default
ok - fm-x-reply --followup dry-run marks the endpoint without changing the answer path
ok - fm-x-reply --followup auto-splits a long follow-up into a marked thread
ok - fm-x-reply followup dry-run keeps endpoint marker and compact image metadata
ok - fm-x-poll records the durable per-request reply context from the relay payload
ok - context registry publishes records only through private guarded artifacts
ok - context registry reads only private single-link artifacts
ok - private artifact publisher is compatible with the system bash path
ok - context registry retention is bounded to the seven-day follow-up window
ok - context registry rewrites preserve the first-seen timestamp
ok - context retention starts only when a live initial answer succeeds
ok - a delayed Discord follow-up stays one message after inbox cleanup via the durable registry
ok - an X follow-up over 280 still splits correctly after inbox cleanup
ok - every unresolved follow-up is refused before posting
ok - a partial registry platform combines with the relay's authoritative budget
ok - concurrent requests each recover their own platform/budget with no cross-overwrite
ok - fm-x-dismiss clears the durable per-request context (a dismissed mention gets no follow-up)
ok - fm-x-dismiss posts a request-bound dismiss and echoes only the request_id
ok - fm-x-dismiss dry-run records the would-be body and never posts
ok - fm-x-dismiss dry-run works without a token
ok - fm-x-dismiss dry-run publishes outbox records only through private guarded artifacts
ok - fm-x-dismiss exits non-zero on a non-2xx relay response
ok - fm-x-dismiss exits non-zero on a transport failure
ok - fm-x-dismiss rejects an unsafe request_id (path-traversal guard)
ok - fm-x-dismiss rejects missing or extra arguments with a usage error
ok - fm-x-link records and refreshes the X-request link without disturbing meta
ok - fm-x-link records Discord platform context so follow-ups keep the Discord budget
ok - fm-x-link resolves the platform by request_id so a post-cleanup link keeps the Discord budget
ok - fm-x-link warns loudly and the follow-up is held (not wrongly split) when the platform is unknown
ok - fm-x-link paired carry flags preserve a prior task's follow-up binding onto a successor
ok - fm-x-link recovery relink preserves Discord platform context after inbox drain
ok - fm-x-link rejects malformed or unpaired carry flags
ok - meta rewrites are independent of TMPDIR
ok - fm-x-link rejects unsafe ids, missing meta, and missing arguments
ok - fm-x-link keeps the plain missing-task error when no second mate is registered
ok - fm-x-link refuses a second-mate-routed task and points at the promised-final path
ok - fm-x-link points an unlocatable task at the promised-final path when second mates exist
ok - fm-x-followup --check reports postable / not-linked correctly
ok - fm-x-followup --check prunes a link past the 7-day window
ok - fm-x-followup --check prunes a link that already reached the follow-up cap
ok - fm-x-followup posts a follow-up, increments the counter, and keeps the link under the cap
ok - fm-x-followup --final clears the link after one post regardless of the remaining count
ok - fm-x-followup clears the link once the third follow-up reaches the cap
ok - fm-x-followup --image forwards the attachment through fm-x-reply --followup
ok - fm-x-followup keeps the link and counter when the post fails
ok - fm-x-followup tombstones the link when a post-success counter write fails
ok - fm-x-followup treats a relay cap/window rejection as an already-exhausted link, not a retry
ok - fm-x-followup skips silently and clears the link past the 7-day window
ok - fm-x-followup is a no-op for a task with no X link
ok - fm-x-followup dry-run records the follow-up and increments the counter, keeping the link
ok - fm-x-followup dry-run --final clears the link just as a live post would
ok - fm-x-followup rejects malformed invocations
ok - bootstrap activates X mode from an .env token, idempotently
ok - bootstrap ignores CDPATH when writing absolute FM_HOME into the durable X-mode poll shim
ok - bootstrap reports missing X-mode dependencies before arming
ok - bootstrap does not report X mode on when activation artifacts cannot be written
ok - bootstrap rejects linked X artifacts without touching their targets
ok - bootstrap is inert without a non-empty .env token (non-X users unaffected)
ok - bootstrap cleans up X artifacts on opt-out and is silent once off
ok - bootstrap reports failed X artifact cleanup on opt-out
FM_TEST_END 2026-08-23T22:16:02Z tests/fm-x-mode.test.sh exit=0 duration_ms=19898 gate_skip=false
ok - report-only unresolved decision is reproduced and completion refuses before loss
ok - non-forced scout teardown always requires durable inventory verification
ok - a declined decision closes with a recorded answer and no routed work
ok - a decision closed outside the script is repairable and then clears teardown
ok - an unanswered decision still blocks completion and resists both unrouted close paths
ok - captain holds are idempotent, distinct, teardown-safe, Bearings-visible, and durably routed before close
ok - completion and verification validate origins before constructing paths
ok - ended visual review follows the same decision-hold completion owner
ok - resolved findings and decision-like prose do not create false holds
ok - terminal single-owner stale status decisions do not block empty inventory
ok - main-home and secondmate-home captain holds remain correctly routed
ok - resolve matches first/middle/last in quoted blocked_by and rejects a genuinely absent id
ok - a bound channel's captured answers close their captain holds at answer time
ok - a channel source with no decision binding closes nothing
ok - the answer path keeps every guard the unrouted close path already had
ok - the chat channel feeds the same keyed-answer intake a captured review does
FM_TEST_END 2026-08-23T22:16:10Z tests/fm-decision-hold-lifecycle.test.sh exit=0 duration_ms=38191 gate_skip=false
FM_TEST_SUMMARY total=24 failed=0 skipped_gate=1 duration_ms=47850
FM_TEST_SUMMARY_FAMILY family=backend-dispatch count=3 duration_ms=13790 failed=0
FM_TEST_SUMMARY_FAMILY family=pr-forge count=3 duration_ms=22759 failed=0
FM_TEST_SUMMARY_FAMILY family=pure-contract-unit count=18 duration_ms=104330 failed=0
FM_TEST_SLOWEST rank=1 script=tests/fm-decision-hold-lifecycle.test.sh duration_ms=38191
FM_TEST_SLOWEST rank=2 script=tests/fm-x-mode.test.sh duration_ms=19898
FM_TEST_SLOWEST rank=3 script=tests/fm-arm-pretool-check.test.sh duration_ms=18629
FM_TEST_SLOWEST rank=4 script=tests/fm-backend-herdr.test.sh duration_ms=12337
FM_TEST_SLOWEST rank=5 script=tests/fm-test-run.test.sh duration_ms=12330
FM_TEST_SLOWEST rank=6 script=tests/fm-cd-pretool-check.test.sh duration_ms=9719
FM_TEST_SLOWEST rank=7 script=tests/fm-herdr-lab.test.sh duration_ms=5744
FM_TEST_SLOWEST rank=8 script=tests/fm-crew-state.test.sh duration_ms=4547
FM_TEST_SLOWEST rank=9 script=tests/fm-grok-harness.test.sh duration_ms=4334
FM_TEST_SLOWEST rank=10 script=tests/fm-composer-lib.test.sh duration_ms=2693
FM_TEST_SLOWEST rank=11 script=tests/fm-pr-merge.test.sh duration_ms=2250
FM_TEST_SLOWEST rank=12 script=tests/fm-lint.test.sh duration_ms=1964
FM_TEST_SLOWEST rank=13 script=tests/fm-tmux-submit-busy.test.sh duration_ms=1842
FM_TEST_SLOWEST rank=14 script=tests/fm-composer-ghost.test.sh duration_ms=1222
FM_TEST_SLOWEST rank=15 script=tests/fm-send-popup-settle.test.sh duration_ms=1090
fm-test-run: wrote timing artifact: /tmp/fm-ci.41J2ND/fm-test-timing-proven-isolated.json
FM_CI_CHECK behavior-parallel result=pass duration_ms=47934
fm-ci.sh: running behavior-serial (behavior suite, portable serial remainder)
FM_TEST_BEGIN 2026-08-23T22:16:10Z tests/fm-afk-inject-e2e.test.sh family=afk expected_gate_skip=none
ok - Scenario A: partial input defers injection; digest arrives clean after idle
ok - Scenario B: swallowed Enter produces exactly one clean digest
ok - Scenario C: a normal captain status injects exactly one clean single-line sentinel digest
all e2e injection tests passed
FM_TEST_END 2026-08-23T22:16:45Z tests/fm-afk-inject-e2e.test.sh exit=0 duration_ms=34402 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:16:45Z tests/fm-afk-pi-herdr-return-e2e.test.sh family=live-harness-optin expected_gate_skip=optin-env
skip: set FM_AFK_PI_HERDR_E2E=1 to run the real Pi/Herdr away-return regression
FM_TEST_END 2026-08-23T22:16:45Z tests/fm-afk-pi-herdr-return-e2e.test.sh exit=0 duration_ms=104 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:16:45Z tests/fm-afk-return.test.sh family=afk expected_gate_skip=none
ok - return catch-up precedes Bearings, owns live blocker remediation, preserves evidence once, and clears idempotently
ok - tmux and Herdr blockers require the same explicit durable reclassification before ordinary work
ok - needs-decision remains reportable without masquerading as a firstmate-actionable blocker
ok - AFK return re-drains published wakes until handling acknowledges
ok - away-mode re-entry fails closed while the prior return catch-up is pending
ok - check retries recorded terminal teardown and keeps catch-up gated until success
FM_TEST_END 2026-08-23T22:16:46Z tests/fm-afk-return.test.sh exit=0 duration_ms=1033 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:16:46Z tests/fm-ask-user-authority.test.sh family=pure-contract-unit expected_gate_skip=none
ok - primary workers and secondmates receive the authority rule through generated instructions
FM_TEST_END 2026-08-23T22:16:46Z tests/fm-ask-user-authority.test.sh exit=0 duration_ms=113 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:16:46Z tests/fm-backend-cmux-smoke.test.sh family=cmux expected_gate_skip=optional-binary
skip: cmux CLI not found on PATH or at the bundle path
FM_TEST_END 2026-08-23T22:16:46Z tests/fm-backend-cmux-smoke.test.sh exit=0 duration_ms=19 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:16:46Z tests/fm-backend-cmux.test.sh family=cmux expected_gate_skip=optional-binary
ok - fm_backend_cmux_version_check: accepts the verified minimum (0.64.17)
ok - fm_backend_cmux_version_check: accepts a newer version (0.70.0)
ok - fm_backend_cmux_version_check: refuses an old version loudly
ok - fm_backend_cmux_version_check: refuses loudly when cmux is not found on PATH or at the bundle path
ok - fm_backend_cmux_password: reads the first non-empty line of config/cmux-socket-password
ok - fm_backend_cmux_password: preserves spaces and tabs in config/cmux-socket-password
ok - fm_backend_cmux_password: respects FM_CONFIG_OVERRIDE
ok - fm_backend_cmux_password: empty when config/cmux-socket-password is absent
ok - fm_backend_cmux_cli: exports CMUX_SOCKET_PASSWORD when config/cmux-socket-password is set
ok - fm_backend_cmux_parse_target: splits '<workspace_uuid>:<surface_uuid>' on the first colon
ok - fm_backend_cmux_normalize_key: Enter/Escape/C-c map to cmux's verified enter/escape/ctrl-c
ok - fm_backend_cmux_scoped_title: scopes a primary task title with firstmate plus root hash
ok - fm_backend_cmux_scoped_title: scopes a secondmate task title with the home marker plus root hash
ok - fm_backend_cmux_scoped_title: includes the resolved FM_ROOT hash in the home label
ok - fm_backend_validate: cmux is a known backend
ok - fm_backend_busy_state: cmux (no native primitive) always reports unknown, same as tmux/zellij/orca
ok - fm_backend_composer_state: routes cmux to the cmux composer classifier
ok - fm_backend_cmux_ping_state: reports 'ok' on PONG
ok - fm_backend_cmux_ping_state: reports 'denied' when socketControlMode=cmuxOnly rejects the connection
ok - fm_backend_cmux_ping_state: reports 'unauth' when password mode rejects a missing/wrong password
ok - fm_backend_cmux_ping_state: reports 'unauth' when password mode rejects a wrong password (Invalid password)
ok - fm_backend_cmux_ping_state: reports 'down' when the app is not running yet
ok - fm_backend_cmux_ensure_running: returns immediately when cmux is already reachable
ok - fm_backend_cmux_ensure_running: fails fast on a denied socket without attempting to launch, naming every viable mode
ok - fm_backend_cmux_ensure_running: fails fast on an unauthenticated socket, naming the password config and the Automation mode alternative
ok - fm_backend_cmux_create_task: refuses a duplicate workspace title (cmux's own new-workspace has no uniqueness check)
ok - fm_backend_cmux_create_task: creates a workspace and parses workspace_id/surface_id from list responses
ok - fm_backend_cmux_target_ready: fails when the workspace/surface is not found (list-panes structural check)
ok - fm_backend_cmux_target_ready: verifies the workspace title against the expected label first
ok - fm_backend_cmux_target_ready: rejects a workspace id reused under a different title
ok - fm_backend_cmux_capture: fetches generously and trims to N lines locally
ok - fm_backend_cmux_capture: propagates a read-screen failure even when stdout is empty
ok - fm_backend_cmux_capture: fails when the target surface is absent
ok - fm_backend_cmux_send_key: normalizes the key (Escape -> escape) and targets the explicit workspace/surface
ok - fm_backend_cmux_send_key: recovers stale workspace/surface ids by expected label
ok - fm_backend_cmux_send_literal: calls send with an explicit workspace/surface and a -- separator
ok - fm_backend_cmux_send_text_line: clears partial input when Enter fails
ok - fm_backend_cmux_send_text_line: reports unsafe input when cleanup also fails
ok - fm_backend_cmux_current_path: actively probes with marked begin/end lines (zellij-shape frozen cwd)
ok - fm_backend_cmux_composer_state: a bare '❯' composer row reads empty
ok - fm_backend_cmux_composer_state: a borderless Claude '❯' composer row reads empty
ok - fm_backend_cmux_composer_state: a borderless Claude row outranks stale bordered scrollback
ok - fm_backend_cmux_composer_state: a borderless Claude '❯'+NBSP composer row reads empty under LC_ALL=C
ok - fm_backend_cmux_composer_state: plain-capture text after a bare glyph degrades to unknown (never false pending)
ok - fm_backend_cmux_composer_state: the ghost placeholder text reads empty, not pending
ok - fm_backend_cmux_composer_state: real composer text reads pending
ok - fm_backend_cmux_composer_state: a slash-command popup's argument-hint placeholder still reads pending (the incident fix)
ok - fm_backend_cmux_composer_state: reports unknown when the surface cannot be captured
ok - fm_backend_cmux_composer_state: reports unknown when no border-delimited composer row is found
ok - fm_backend_cmux_send_text_submit: reports 'empty' once the composer row reads empty after one Enter
ok - fm_backend_cmux_send_text_submit: reports 'pending' when the composer never clears after retried Enters (swallowed)
ok - fm_backend_cmux_send_text_submit: retries past a popup-placeholder-fill Enter and lands the real second Enter (the incident fix)
ok - fm_backend_cmux_send_text_submit: reports 'send-failed' when the target workspace/surface is absent
ok - fm_backend_cmux_window_of_workspace: walks windows and counts the membership-confirming workspace list
ok - fm_backend_cmux_window_of_workspace: echoes nothing when no window holds the workspace
ok - fm_backend_cmux_kill: closes the task workspace directly when it is not the last in its window
ok - fm_backend_cmux_kill: adds a throwaway sibling then closes the target when it is the last workspace in its window
ok - fm_backend_cmux_kill: never fails even when close-workspace fails
ok - fm_backend_cmux_kill: recovers stale workspace/surface ids by expected label
ok - fm_backend_cmux_list_live: lists only this home's scoped task workspaces using plain fm-<id> labels
ok - fm-spawn.sh: refuses backend=cmux for --secondmate spawns (mirrors Orca's refusal; no secondmate launch design exists yet)
FM_TEST_END 2026-08-23T22:16:48Z tests/fm-backend-cmux.test.sh exit=0 duration_ms=2226 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:16:48Z tests/fm-backend-herdr-focus-flash-e2e.test.sh family=unclassified expected_gate_skip=none
ok - old path note: this Herdr release preserves focus across the explicit close; continuing with outcome-only assertions
ok - mitigation: every in-operation sample preserved exact focus while the doomed workspace was removed
not ok - the Part C doomed pane never acquired a stable persistent sleep child process
FM_TEST_END 2026-08-23T22:17:02Z tests/fm-backend-herdr-focus-flash-e2e.test.sh exit=1 duration_ms=13369 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:17:02Z tests/fm-backend-orca.test.sh family=orca expected_gate_skip=optional-binary
ok - fm_backend_orca_capture: parses result.terminal.tail and calls terminal read
ok - fm_backend_orca_capture: falls back to result text fields
ok - fm_backend_orca_capture: fails closed on Orca read error JSON
ok - fm_backend_orca_runtime_check: accepts reachable ready runtime
ok - fm_backend_orca_runtime_check: fails closed when runtime is not ready
ok - fm_backend_orca_send_text_submit: verifies empty composer after Enter with one bounded read
ok - fm_backend_orca_send_text_submit: a borderless claude composer confirms delivery (the missing #2029 shape)
ok - fm_backend_orca_composer_state: a stale startup banner cannot outrank the live composer row
ok - fm_backend_orca_send_text_submit: retries Enter while composer remains pending
ok - fm_backend_orca_composer_state: a slash-command popup's argument-hint placeholder still reads pending
ok - fm_backend_orca_composer_state: a bare dead-shell prompt reads unknown (unsafe-for-injection), never empty
ok - fm_backend_orca_send_text_submit: a slash-command popup's placeholder fill on Enter #1 does not short-circuit as submitted; Enter #2 is retried and lands it
ok - fm_backend_orca_send_literal: sends text without submitting
ok - fm_backend_orca_send_text_submit: reports send-failed when Orca send fails
ok - Orca send helpers: fail closed on ok:false JSON
ok - fm_backend_orca_send_key: Enter maps to empty enter, C-c maps to interrupt
ok - fm_backend_orca_send_key: refuses unsupported keys loudly
ok - fm_backend_orca_send_key: refuses Escape instead of mapping it to interrupt
ok - fm_backend_orca_kill: calls terminal close and stays best-effort
ok - fm_backend_orca_remove_worktree: refuses empty worktree ids
ok - fm_backend_orca_remove_worktree: fails closed on ok:false JSON
ok - fm_backend_orca_worktree_path: resolves an Orca worktree id to its path
ok - fm-backend dispatcher: accepts orca and routes capture through bin/backends/orca.sh
ok - fm_backend_orca_json_get: ignores undocumented terminal id shapes
ok - Orca lifecycle helpers: register repo, create worktree, create terminal, parse stable ids
ok - fm_backend_orca_worktree_create: removes created worktree when path is missing
ok - fm-spawn.sh --backend orca: preserves metadata when pathless cleanup fails
ok - fm-spawn.sh --backend orca: reuses implicit terminal, records metadata, launches harness
ok - fm-spawn.sh --backend orca --secondmate: refuses before secondmate-home mutation
ok - fm-spawn.sh --backend orca: refuses before mutation when Orca runtime is not ready
ok - fm-spawn.sh --backend orca: refuses non-isolated worktrees and closes implicit terminals
ok - fm-spawn.sh --backend orca: removes worktree when terminal creation fails
ok - fm-spawn.sh --backend orca: preserves metadata when abort cleanup fails
ok - fm-spawn.sh --backend orca: releases terminal and worktree on later aborts
error: text not submitted to term-io (delivery unconfirmed; verdict=unknown; tried meta=/tmp/fm-backend-orca-tests.M44xQE/io-state/orcaiopathz2.meta; backend=from-meta)
ok - fm-peek/fm-send/fm-crew-state route through backend=orca metadata
ok - fm-peek/fm-crew-state: Orca read error JSON fails closed
ok - fm_backend_target_exists: Orca ok:false read JSON is not live
ok - fm-teardown.sh backend=orca: scout report gate then helper-backed worktree removal
ok - fm-teardown.sh backend=orca: scout teardown refuses id/path mismatches
ok - fm-teardown.sh backend=orca: releases terminal/worktree when path is absent
ok - fm-teardown.sh backend=orca: preserves metadata on remove ok:false JSON
ok - fm-teardown.sh backend=orca: scout report gate precedes pathless helper cleanup
ok - fm-teardown.sh backend=orca: ship teardown fails closed when worktree path is missing
ok - fm-teardown.sh backend=orca: ship teardown requires a matching Orca id path
ok - fm-teardown.sh backend=orca: ship teardown fails closed when id resolution fails
ok - fm-teardown.sh backend=orca: ship teardown refuses id/path mismatches
ok - fm-teardown.sh backend=orca: refuses missing worktree ids before cleanup
ok - fm-teardown.sh backend=orca: refuses incomplete worktree-only endpoint metadata before runtime dispatch
ok - fm-teardown.sh --force: removes Orca secondmate children through Orca
ok - fm-teardown.sh --force: refuses Orca child id/path mismatches
ok - fm-teardown.sh --force: refuses partial Orca secondmate children before runtime dispatch
FM_TEST_END 2026-08-23T22:17:12Z tests/fm-backend-orca.test.sh exit=0 duration_ms=9786 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:17:12Z tests/fm-backend-tmux-smoke.test.sh family=backend-dispatch expected_gate_skip=none
@1
ok - real tmux: fm_backend_tmux_create_task creates a window and refuses a duplicate
ok - real tmux: fm_backend_tmux_send_text_line sends literal text and submits with Enter
ok - real tmux: fm_backend_tmux_send_literal + fm_backend_tmux_send_key Enter submit as two separate steps
ok - real tmux: fm_backend_tmux_capture's -S -N bound trims old history for a small window and reaches it for a large one
ok - real tmux: fm_backend_tmux_resolve_bare_selector (list-live) finds the created window by name
ok - real tmux: fm_backend_tmux_resolve_bare_selector fails for a window that does not exist
ok - real tmux: kill removes the window and the readable session inventory authoritatively classifies it missing
FM_TEST_END 2026-08-23T22:17:12Z tests/fm-backend-tmux-smoke.test.sh exit=0 duration_ms=645 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:17:12Z tests/fm-backend-zellij-smoke.test.sh family=zellij expected_gate_skip=optional-binary
skip: zellij not found
FM_TEST_END 2026-08-23T22:17:12Z tests/fm-backend-zellij-smoke.test.sh exit=0 duration_ms=9 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:17:12Z tests/fm-backend-zellij.test.sh family=zellij expected_gate_skip=optional-binary
ok - fm_backend_zellij_version_check: accepts the verified minimum (0.44.0)
ok - fm_backend_zellij_version_check: accepts a newer version (0.45.2)
ok - fm_backend_zellij_version_check: refuses an old version loudly
ok - fm_backend_zellij_version_check: refuses loudly when zellij is not installed
ok - fm_backend_zellij_session: defaults to 'firstmate' when FM_ZELLIJ_SESSION is unset
ok - fm_backend_zellij_session: honors the FM_ZELLIJ_SESSION test-isolation override
ok - fm_backend_zellij_parse_target: splits '<session>:<pane_id>' on the first colon
ok - fm_backend_zellij_normalize_key: Enter/Escape/C-c map to zellij's verified Enter/Esc/'Ctrl c'
ok - fm_backend_zellij_scoped_title: scopes a primary task title with firstmate plus root hash
ok - fm_backend_zellij_scoped_title: scopes a secondmate task title with the home marker plus root hash
ok - fm_backend_zellij_scoped_title: includes the resolved FM_ROOT hash in the home label
ok - fm_backend_zellij_tab_matches_label: accepts an untagged legacy fm-<id> title when it is the only live tab carrying it
ok - fm_backend_zellij_tab_matches_label: refuses an untagged legacy label match when 2+ live tabs share it (migration ambiguity guard)
ok - fm_backend_zellij_list_live: scopes to this home's own tag - excludes a different installation's tagged tab and unrelated tabs
ok - fm_backend_zellij_resolve_bare_selector: matches the home-scoped tagged title first
ok - fm_backend_zellij_resolve_bare_selector: refuses an ambiguous untagged legacy label shared by 2+ live tabs
ok - fm_backend_zellij_resolve_bare_selector: checks every session for the scoped title before legacy fallback
ok - fm_backend_zellij_resolve_bare_selector: requires untagged legacy labels to be globally unique across sessions
ok - fm_backend_zellij_session_exists: true when the session name is listed
ok - fm_backend_zellij_session_exists: false when the session name is not listed
ok - fm_backend_zellij_server_ensure: reuses an existing session without calling attach
ok - fm_backend_validate: zellij is a known backend (P3)
ok - fm_backend_busy_state: zellij (no native primitive) always reports unknown, same as tmux
ok - fm_backend_zellij_create_task: refuses a duplicate home-scoped tab title (zellij's own new-tab has no uniqueness check)
ok - fm_backend_zellij_create_task: creates a home-scoped tab and parses tab_id/pane_id from the response
ok - fm_backend_zellij_create_task: restores focus to the previously-active tab after the steal-focus new-tab call
ok - fm_backend_zellij_create_task: skips the restore call when there was no previously-active tab
ok - fm_backend_zellij_capture: small reads use viewport-only dump-screen and trim to N lines locally
ok - fm_backend_zellij_capture: reads above the watcher-size threshold request --full scrollback
ok - fm_backend_zellij_capture: fails when the specific pane is absent
ok - fm_backend_zellij_capture: fails when the target session is not listed as active (session_exists pre-check)
ok - fm_backend_zellij_send_key: normalizes the key (Escape -> Esc) and targets the explicit pane id
ok - fm_backend_zellij_send_literal: calls paste with an explicit pane id and a -- separator
ok - fm_backend_zellij_send_text_line: clears partial input when Enter fails
ok - fm_backend_zellij_send_text_line: reports unsafe input when cleanup also fails
ok - fm_backend_zellij_target_ready: expected labels allow matching fm-<id> tabs
ok - fm_backend_zellij_target_ready: expected labels reject stale pane ids reused by another tab
ok - fm_backend_zellij_current_path: actively probes with marked begin/end lines and reconstructs wrapped cwd output
ok - fm_backend_zellij_current_path: never picks up a ~-prefixed banner line as the answer
ok - fm_backend_zellij_kill: resolves the owning tab id fresh and calls close-tab-by-id (never a bare close-pane)
ok - fm_backend_zellij_kill: falls back to close-pane when the owning tab cannot be resolved
ok - fm_backend_zellij_kill: closes the recorded tab id only after label verification
ok - fm_backend_zellij_kill: skips a stale recorded tab id whose label does not match
ok - fm_backend_zellij_kill: never fails when the target session no longer exists
ok - fm-teardown.sh: passes recorded zellij_tab_id with the expected task label
ok - fm-teardown.sh: force cleanup kills zellij children using the child home tag
ok - fm_backend_zellij_send_text_submit: reports 'empty' once the composer classifies empty (submitted)
ok - fm_backend_zellij_send_text_submit: reports 'pending' when the composer still holds the text after retried Enters (swallowed)
ok - fm_backend_zellij_send_text_submit: an unrelated pane change is not a delivery confirmation (false-positive regression)
ok - fm_backend_zellij_send_text_submit: refuses confirmation when paste exits successfully without typing
ok - fm_backend_zellij_send_text_submit: transcript echoes outside the selected composer cannot prove typing
ok - fm_backend_zellij_send_text_submit: pre-existing text cannot prove a no-op paste landed
ok - fm_backend_zellij_send_text_submit: unrelated drafts and furniture cannot prove typing
ok - fm_backend_zellij_send_text_submit: observes wrapped text replacing a shell-prompt placeholder
ok - fm_backend_zellij_send_text_submit: observes wrapped text in a bare composer
ok - fm_backend_zellij_send_text_submit: preserves agent glyphs within wrapped content
ok - fm_backend_zellij_send_text_submit: refuses a live shell below a stale composer
ok - fm_backend_zellij_composer_state: classifies the real claude-in-zellij --ansi dump as empty
ok - fm_backend_zellij_composer_state: a dead pane (empty dumps) reads unknown, never a confirmation
ok - fm_backend_zellij_send_text_submit: reports 'send-failed' when the target session is not active
ok - fm_backend_zellij_send_text_submit: reports 'send-failed' when the target pane is absent
ok - fm-peek/fm-send: explicit metadata-matched targets use the recorded zellij backend
ok - fm-peek: fm-id zellij targets verify the owning tab label before capture
ok - fm-send: fm-id zellij targets reject pane ids whose tab label no longer matches
FM_TEST_END 2026-08-23T22:17:17Z tests/fm-backend-zellij.test.sh exit=0 duration_ms=4361 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:17:17Z tests/fm-backend.test.sh family=backend-dispatch expected_gate_skip=none
ok - fm_backend_name: FM_BACKEND env > config/backend > default tmux
ok - fm_backend_detect: no markers -> undetected, HERDR_ENV=1 -> herdr, $TMUX -> tmux, CMUX_WORKSPACE_ID -> cmux, nested combinations resolve innermost-first
ok - fm_backend_detect: falls back to __CFBundleIdentifier=com.cmuxterm.app when CMUX_WORKSPACE_ID is absent (signal bundle-id; foreign bundle ids rejected)
ok - fm_backend_detect: the cmux fallback signals are macOS-only (inert on a non-Darwin uname)
ok - fm_backend_detect: an inherited cmux bundle id never outranks $TMUX or HERDR_ENV (tmux/herdr-inside-cmux false positive absorbed)
ok - fm_backend_detect: ancestry fallback matches the lsappinfo-resolved (bundle-id) cmux app pid in the parent chain
ok - fm_backend_detect: ancestry fallback matches a bundle-shaped cmux comm path at any install location when lsappinfo cannot resolve a pid
ok - fm_backend_detect: ancestry fallback stops undetected at launchd (a reparented tmux server never reaches cmux)
ok - fm_backend_name: a fallback-detected cmux prints a NOTICE naming the fallback signal; the primary-marker notice is unchanged
ok - fm_backend_name: auto-detect selects herdr or cmux (loud notice) or tmux (silent, including nested tmux-in-herdr/tmux-in-cmux)
ok - fm_backend_name: an explicit FM_BACKEND or config/backend setting always wins over runtime auto-detection, including an ambient cmux marker
ok - fm_backend_validate: implemented adapters accepted, unknown and blocked codex-app backends refused loudly
ok - zsh: shell-portable backend matching skipped (zsh not found)
ok - bash: fm_backend_source recognizes known backends and rejects unknown ones
ok - fm_backend_validate_spawn: all implemented lifecycle backends are spawn-supported
ok - fm_meta_get / fm_backend_of_meta: read key=value, default backend to tmux
ok - fm_backend_resolve_selector: session:window literal, exact task id first, legacy fm-<id> label fallback, ad hoc bare name via tmux list-windows
ok - fm_backend_of_selector: exact task ids, legacy fm-<id> labels, and matching explicit targets inherit metadata backend
ok - fm-send.sh: explicit tmux targets are verified; text types once and submits with Enter
ok - fm-peek.sh: capture-pane invocation and output are byte-identical old vs new
ok - fm-spawn.sh: a project reached through a symlinked prefix (e.g. macOS /tmp -> /private/tmp) does not trip the isolation guard's false refusal
ok - fm-teardown.sh: treehouse return remains compatible while tmux cleanup uses exact selectors
ok - fm-spawn.sh --backend bogus is refused loudly
ok - fm-spawn.sh --backend codex-app is refused
ok - fm-spawn.sh honors FM_BACKEND and refuses an unimplemented value loudly
ok - fm-spawn.sh: an explicit --backend tmux resolves silently and writes no backend= (missing means tmux)
ok - fm-spawn.sh: explicit --backend tmux wins over an ambient HERDR_ENV=1 auto-detect marker
ok - fm-spawn.sh: auto-detect resolves nested tmux-in-herdr to tmux and stays silent end to end
FM_TEST_END 2026-08-23T22:17:31Z tests/fm-backend.test.sh exit=0 duration_ms=14235 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:17:31Z tests/fm-backlog-handoff.test.sh family=secondmate expected_gate_skip=none
ok - body followed by another item moves intact with no source orphans
ok - body followed by section heading moves intact; section stays
ok - multi-paragraph body with internal blank lines moves whole and is idempotent
ok - body as last lines of the file moves intact
ok - EOF body before a seeded destination section keeps its boundary
ok - untouched EOF line preserves its original terminator
ok - body-carrying handoff is idempotent: re-run changes nothing
ok - noncanonical one-space and tab continuations refuse without changes
ok - indented ## Intent / ## Acceptance are body, not section boundaries
ok - registry home parses when summary has parentheses before (home: ...)
ok - registry entry without (home: ...) fails cleanly with has no home
ok - handoff reports a moved item whose public commitment still binds this home
ok - handoff says nothing about public commitments in a relay-free home
ALL TESTS PASSED
FM_TEST_END 2026-08-23T22:17:33Z tests/fm-backlog-handoff.test.sh exit=0 duration_ms=2496 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:17:33Z tests/fm-bearings-snapshot.test.sh family=snapshot-bearings expected_gate_skip=optional-binary
ok - Domain Alpha structured state overrides a stale parent Phase 7 event
ok - GNU stat file reads select -c without BSD filesystem-report pollution
ok - parent activity evidence is bounded and disclosed
ok - Bearings excludes a status-only child decision
ok - a structured child captain hold reaches Captain's Call
ok - missing, invalid, unreadable, malformed, and timed-out homes stay explicit unknowns
ok - an oversized secondmate summary retains the strict empty unknown fallback
ok - secondmate and per-home child counts are bounded, disclosed, and explicitly expandable
ok - parent decisions remain untrusted contradiction evidence
ok - parent evidence reconciliation distinguishes matching holds, blocks, and decisions
ok - nonprogressing child states are explicit and inconsistent terminal rows invalidate
ok - registry unavailability and bounded truncation remain explicit
ok - repeated snapshots keep the same current landed baseline and ignore prior reports
ok - default output is bounded, local-only, and marks omitted surfaces
ok - TOON and JSON are parity representations of the same model
ok - landed includes secondmate-managed merges alongside main-home merges
ok - default landed selection balances one dominant home with sparse homes
ok - landed selection refills capacity after sparse homes exhaust
ok - landed selection uses deterministic home order when homes exceed the cap
ok - landed selection preserves deterministic home and internal tie ordering
ok - landed selection handles no landed items
ok - --all-landed keeps the complete global landed output
ok - landed stays bounded with per-home + overall caps and omitted[] disclosure
ok - Bearings keeps a live blocker in structured live state and never converts it to Charted Next queue work
ok - action-free items (working/done/queued/landed) do not leak into Captain's Call
ok - main orphan in-flight stays out of Underway and is disclosed in omitted/gates
ok - main unstructured current is disclosed while structured siblings still project
ok - counterfactual meta clears main inventory warning and projects the live task
ok - mixed secondmate roles, partial state, and captain readiness project independently
ok - main and secondmate captain actionability use the same blocker readiness
ok - a completed scout with decision-like report prose is a pointer, not pending
ok - an authoritative captain hold surfaces end-to-end
ok - current report pointers surface
ok - superseded queued items are dropped by default and restored with --all-queued
ok - --include-prs is the only path that fetches, and it enriches correctly
ok - a partial GitHub failure degrades gracefully
ok - Perl fallback bounds stalled GitHub calls without coreutils timeout
ok - all fleet-sized sections are capped with counted opt-in expansion
ok - live PR enrichment caps repositories with counted expansion
ok - per-repository open-PR caps are disclosed with an expansion knob
ok - projection and TOON rendering failures exit nonzero with diagnostics
FM_TEST_END 2026-08-23T22:18:15Z tests/fm-bearings-snapshot.test.sh exit=0 duration_ms=41268 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:18:15Z tests/fm-bootstrap.test.sh family=session-bootstrap expected_gate_skip=none
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
ok - bootstrap reports treehouse lease + tasks-axi/quota-axi bootstrap contracts
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
ok - bootstrap enforces no-mistakes minimum version
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
ok - bootstrap enforces gh-axi minimum version
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
ok - bootstrap enforces lavish-axi minimum version
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
ok - bootstrap enforces tasks-axi minimum version
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
ok - bootstrap enforces quota-axi minimum version
ok - bootstrap requires git with an install instruction
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
ok - bootstrap reports a required tool that is on PATH but cannot run
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
ok - bootstrap reports a broken runtime-gated tool exactly once
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
ok - bootstrap: backend=orca gates the Orca CLI without requiring it on the default backend
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)

@jk1rby

jk1rby commented Aug 23, 2026

Copy link
Copy Markdown

bin/fm-ci.sh full output — part 3/6

Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
ok - bootstrap: session-provider backends require their own CLI + jq + treehouse, never tmux
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
ok - bootstrap: a session-provider backend gates its own CLI, never a false tmux requirement
ok - bootstrap: Herdr manual-install guidance is never executed as a shell command
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
ok - bootstrap: the bundled cmux CLI satisfies the active backend dependency
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
ok - bootstrap: unknown resolved backends fail closed with an actionable diagnostic
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
ok - bootstrap: JSON-emitting backends require jq (their genuine dep), never tmux
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
ok - bootstrap: the treehouse lease check follows the resolved backend's worktree provider
ok - bootstrap computes a fleet-size-aware default timeout and preserves partial fleet-sync output
ok - bootstrap keeps the quick 20s default for small fleets
ok - bootstrap preserves FM_FLEET_SYNC_BOOTSTRAP_TIMEOUT as an explicit override
ok - bootstrap treats a blank timeout override as unset
ok - bootstrap computes the timeout before launching fleet sync
ok - bootstrap keeps routine tasks-axi, harness, dispatch, and already-live liveness confirmations silent
ok - bootstrap routine contract runs under system /bin/bash
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
ok - bootstrap: FM_BOOTSTRAP_NETWORK partitions one run into local and network halves
ok - bootstrap: every deferred mutating sweep rechecks fleet-lock ownership
ok - bootstrap: each deferred network phase, secondmate, and clone records its own elapsed time
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
ok - bootstrap: the tasks-axi compatibility verdict travels exactly one process hop
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
ok - bootstrap surfaces active crew-dispatch rules only as verbose BOOTSTRAP_INFO
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
ok - bootstrap validates crew-dispatch.json and reports malformed or unverified configs
FM_TEST_END 2026-08-23T22:18:37Z tests/fm-bootstrap.test.sh exit=0 duration_ms=22618 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:18:37Z tests/fm-busy-adapter-wiring.test.sh family=unclassified expected_gate_skip=none
ok - pi extension reports agent_start busy, settles idle only via ctx.isIdle(), and keeps turn_end a notification
ok - pi extension awaits agent_settled before the next agent_start without a test delay
ok - pi extension events from a superseded incarnation are rejected as stale
ok - kimi and grok install no unverified semantic wiring and classify through their own gates
ok - opencode plugin classifies from session.status, scoped to the latched worker session
ok - claude hooks open on UserPromptSubmit and close on Stop, StopFailure, and SessionEnd
ok - claude hook events from a superseded incarnation are rejected without breaking the hook
ok - codex classifies unknown until a semantic source is verified, never idle or footer-matched
all fm-busy-adapter-wiring tests passed
FM_TEST_END 2026-08-23T22:18:51Z tests/fm-busy-adapter-wiring.test.sh exit=0 duration_ms=13472 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:18:51Z tests/fm-busy-state.test.sh family=unclassified expected_gate_skip=none
ok - arm mints a gen sidecar and seeds busy fm-spawn at seq=1
ok - apply advances seq under the armed gen and attributes the writing source
ok - firstmate-owned interrupt and recovery events bind to the current gen
ok - apply is refused for a task whose busy contract was never armed
ok - retire waits for the writer lock and cannot remove a new incarnation
ok - retire treats only an absent sidecar as already retired
ok - a late event from a previous incarnation is rejected, record unchanged
ok - a record from a stale incarnation classifies unknown, never idle
ok - a converted adapter with no record classifies unknown, never idle
ok - malformed records classify unknown malformed, never busy or idle
ok - a record with no armed gen sidecar classifies unknown
ok - a record is trusted only by the adapter whose source wrote it
ok - converted adapters never classify busy from rendered footer text
ok - the grok fallback is regex-scoped to grok and classifies only grok tasks
ok - codex classifies unknown until a semantic source passes its verification gate
ok - standalone kimi classifies unknown until the live verification gate opens
ok - cursor classifies only from its transcript fold, never rendered text or native state
ok - endpoint death is the only process-level override and yields dead, never busy
ok - herdr's native verdict is trusted for busy only, and records outrank it
ok - record parsing never clobbers the caller's positional parameters, glob setting, or fields
ok - the boolean view reports busy only on an exact busy verdict
all fm-busy-state tests passed
FM_TEST_END 2026-08-23T22:18:51Z tests/fm-busy-state.test.sh exit=0 duration_ms=488 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:18:51Z tests/fm-calm-pi-extension.test.sh family=pure-contract-unit expected_gate_skip=none
skip: installed @earendil-works/pi-coding-agent package not found
ok - Pi calm compatibility evidence never rejects a Pi version for being newer than 0.82.0, and still fails closed on a missing or malformed version
skip: installed @earendil-works/pi-coding-agent package not found
ok - missing Pi presentation class exports reach the independent adapter degradation path
skip: installed @earendil-works/pi-coding-agent package not found
skip: installed @earendil-works/pi-coding-agent package not found
skip: installed @earendil-works/pi-coding-agent package not found
skip: installed @earendil-works/pi-coding-agent package not found
ok - Pi operational follow-up E2E processes exact user-role notifications once while Calm hides current and adjacent rows, Calm off and absent render them, and restart preserves semantics
ok - Pi Calm native /skill:ahoy geometry keeps every collapsed thinking and tool block at zero height while preserving expansion, history, restart, and Calm-off rendering
skip: installed @earendil-works/pi-coding-agent package not found
ok - Pi calm native E2E replaces the stock working row with a moving, resize-clamped working ship that freezes and resumes across two working periods in one Pi session, clears on abort, keeps captain turns visible, hides exact operational user rows without changing persistence, restores stock rendering Calm-off, survives restart, and preserves export plus Ctrl+O behavior
FM_TEST_END 2026-08-23T22:19:14Z tests/fm-calm-pi-extension.test.sh exit=0 duration_ms=22387 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:19:14Z tests/fm-ci.test.sh family=pure-contract-unit expected_gate_skip=none
ok - --list names every check and the removed job whose guarantee it carries
ok - an unknown check id is a usage error
ok - every check green is PASS and exit 0
ok - a failing check is FAIL and exit 1
ok - a missing tool is COULD NOT CHECK and exit 75, never a pass
ok - a present but unrunnable tool is COULD NOT CHECK, not a lane failure
ok - a pinned tool that cannot run names the installer, not a phantom absence
ok - a pinned tool at the wrong version is COULD NOT CHECK
ok - a check that reports 75 itself is COULD NOT CHECK
ok - a real failure outranks an unavailable check
ok - a missing default Herdr session is COULD NOT CHECK
ok - a broken compatibility pointer fails the invariants check
ok - a tracked personal fleet path fails the invariants check
ok - a hung check hits its tripwire and fails
ok - pinned tools in FM_CI_BIN_DIR satisfy a check's preconditions
ok - --only runs just the named checks
FM_TEST_END 2026-08-23T22:19:18Z tests/fm-ci.test.sh exit=0 duration_ms=4540 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:19:18Z tests/fm-classify-decision-key.test.sh family=pure-contract-unit expected_gate_skip=none
ok - a stated [key=X] opens X whether it precedes or follows the verb colon
ok - a keyless needs-decision still opens and closes the default key
ok - a resolution closes its decision regardless of either line's key position
ok - blocked [key=X] opens X in both key positions
ok - two colon-form keyed decisions never collapse into one shared bucket
ok - a [key=x] mentioned mid-note is prose, never an opened or closed key
ok - a malformed stated key is rejected in both positions, never folded as default
ok - status_line_verb strips every bracket tag before the colon, in any order, and recovers the bare verb
ok - a [corr=...] tag ahead of [key=...] no longer swallows the verb: opens and closes under the stated key
ok - a [corr=...] tag with no stated key opens under 'default', exactly like a bare needs-decision line
ok - a [key=x] tag alone (no corr tag) still opens x - no regression from the tag-stripping fix
ok - blocked/resolved parse their bare verb with any bracket-tag order preceding the colon
ok - the incremental fold matches the full fold across appends in both key positions
FM_TEST_END 2026-08-23T22:19:19Z tests/fm-classify-decision-key.test.sh exit=0 duration_ms=635 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:19:19Z tests/fm-claude-stop-autoarm-live-e2e.test.sh family=unclassified expected_gate_skip=none
skip: set FM_CLAUDE_LIVE_E2E=1 to run the Claude Stop auto-arm regression
FM_TEST_END 2026-08-23T22:19:19Z tests/fm-claude-stop-autoarm-live-e2e.test.sh exit=0 duration_ms=11 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:19:19Z tests/fm-claude-stop-autoarm.test.sh family=unclassified expected_gate_skip=none
ok - auto-arm: inert in a linked child worktree even when in-flight
ok - auto-arm: inert with no session lock
ok - auto-arm: a demonstrably dead recorded session owner is reclaimed through fm-lock.sh before arming
ok - auto-arm: inert without arm, rewake, or lock replacement when another live harness owns the home
ok - auto-arm: an alive-but-not-self decline in an idle home leaves no decline trace
ok - auto-arm: a prior identity-decline trace is cleared once a later cycle confirms self
ok - auto-arm: inert while AFK owns supervision
ok - auto-arm: stale-owner recovery leaves the AFK and supervision-need gates unchanged
ok - auto-arm: resolves the outermost pid of a nested contiguous claude ancestry (bg-spare chain)
ok - auto-arm: inert with nothing in flight and no X-mode need
ok - auto-arm: actionable close translates to exactly one exit-2 rewake with reason
ok - auto-arm: actionable close survives a healthy successor without duplicate delivery
ok - auto-arm: bounded failure verification emits one automatic-mechanism alarm
ok - auto-arm: consecutive failures keep Stop-owned retry without repeating notice
ok - auto-arm: unverified clean close exhausts retries and fails closed
ok - auto-arm: post-alarm actionable outcomes cannot continue or reset failure state
ok - auto-arm: benign cycle end with a live watcher and fresh beacon stays silent across the next cycle
ok - auto-arm: budget contention preserves the episode and forces a reset retry
ok - auto-arm: X-mode poll need arms the cycle even with no tasks in flight
ok - auto-arm: concurrent firings admit one owner and one rewake translation
ok - auto-arm: need vanishing mid-cycle closes without a rewake
ok - auto-arm: mid-cycle AFK hands triage to the daemon with no rewake
ok - auto-arm: active in a marked secondmate home
ok - fm-lock: shared session-lock lib preserves the status path
FM_TEST_END 2026-08-23T22:20:20Z tests/fm-claude-stop-autoarm.test.sh exit=0 duration_ms=60710 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:20:20Z tests/fm-cmux-claude-composer-live-e2e.test.sh family=live-harness-optin expected_gate_skip=optin-env
skip: set FM_CMUX_CLAUDE_COMPOSER_LIVE=1 to run the real cmux Claude composer drift guard
FM_TEST_END 2026-08-23T22:20:20Z tests/fm-cmux-claude-composer-live-e2e.test.sh exit=0 duration_ms=13 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:20:20Z tests/fm-codex-continuity-live-e2e.test.sh family=live-harness-optin expected_gate_skip=optin-env
skip: set FM_CODEX_LIVE_E2E=1 to run the Codex continuity regression
FM_TEST_END 2026-08-23T22:20:20Z tests/fm-codex-continuity-live-e2e.test.sh exit=0 duration_ms=10 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:20:20Z tests/fm-composer-matrix-live-e2e.test.sh family=live-harness-optin expected_gate_skip=optin-env
skip: set FM_COMPOSER_MATRIX_LIVE=1 to run the live composer-matrix guard
FM_TEST_END 2026-08-23T22:20:20Z tests/fm-composer-matrix-live-e2e.test.sh exit=0 duration_ms=12 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:20:20Z tests/fm-control-relaunch.test.sh family=backend-dispatch expected_gate_skip=none
ok - fm-control relaunch: a same-harness relaunch replaces the agent in the same endpoint and worktree
ok - fm-control relaunch: durable task metadata survives replacement launch publication
ok - fm-control relaunch: trace and concurrent task metadata publications serialize
ok - fm-control relaunch: disabling tracing clears metadata and pane context
ok - fm-control relaunch: the progress note lands in the instructions the replacement reads
ok - fm-control relaunch: a ship task refuses without the progress note its replacement needs
ok - fm-control relaunch: switching harness is one ordinary relaunch, and the old wiring goes with the old agent
ok - fm-control relaunch: a harness switch resets model and effort unless they are named too
ok - fm-control relaunch: a prefixed recorded harness can switch adapters transactionally
ok - fm-control relaunch: a prefixed command requires an explicit replacement harness
ok - fm-control relaunch: a same-harness relaunch keeps the profile axes it was running with
ok - fm-control relaunch: explicit model and effort win over the recorded ones
ok - fm-control relaunch: refuses to relaunch onto an adapter with no verified mechanics
ok - fm-control relaunch: the retired incarnation's global turn-end token is revoked
ok - fm-control relaunch: wiring cleanup failure refuses replacement arming
ok - fm-control-lib: one owner resolves each harness's turn-end registry entry, and refuses a malformed token
ok - fm-control relaunch: a secondmate relaunch re-resolves its durable configured harness pin
ok - fm-control relaunch: invalid configured effort is ignored before stop
ok - fm-control relaunch: an adapter unverified for this task kind refuses before the agent is stopped
ok - fm-control relaunch: explicit secondmate harness resets unnamed profile axes
ok - fm-control relaunch: a ship task keeps its recorded harness instead of re-reading crew config
ok - fm-spawn --relaunch: with no explicit harness it reuses the task's recorded one, never the crew default
ok - fm-spawn --relaunch: wiring armed under a prefixed harness name is still retired
ok - fm-spawn --relaunch: switching away from muse retires its session binding
ok - fm-spawn --relaunch: switching away from cursor retires its session binding
ok - fm-control relaunch: an unaccountable local copy refuses before the agent is touched
ok - fm-control relaunch: a worker with nothing to work from is never launched
ok - fm-control relaunch: a refusal before the agent is stopped leaves the durable record untouched
ok - fm-control relaunch: checkpoint inspection failures refuse before stopping
ok - fm-control relaunch: a launch failure after the stop keeps the prior record and reports the real state
ok - fm-control relaunch: unpublished rollback keeps concurrent durable metadata
ok - fm-control relaunch: post-publication failure keeps the new durable record
ok - fm-control relaunch: partial stop reconciles actual agent state
ok - fm-control relaunch: failed journal replacement preserves durable phase
ok - fm-spawn relaunch: prepublication abort removes replacement state
ok - fm-control relaunch: the checkpoint records the exact unlanded work it preserved
ok - fm-control relaunch: a secondmate's child work is accounted for and its charter is left alone
ok - fm-control relaunch: a secondmate home that is not this secondmate's is refused
ok - fm-control relaunch: unreadable and untraversable child state fails checkpoint
ok - fm-control relaunch: two control actions on one task serialize instead of interleaving
ok - fm-spawn relaunch: direct entry participates in lifecycle serialization
ok - fm-promote: promotion participates in lifecycle serialization
ok - fm-spawn --relaunch: refuses to launch a second agent into a live endpoint
ok - fm-spawn --relaunch: every identity axis comes from the record, and a contradicting flag refuses
ok - fm-spawn --relaunch: an unrecorded task is refused
ok - fm-spawn --relaunch: refuses to start a replacement outside the copy holding the work
FM_TEST_END 2026-08-23T22:20:41Z tests/fm-control-relaunch.test.sh exit=0 duration_ms=21396 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:20:41Z tests/fm-control.test.sh family=backend-dispatch expected_gate_skip=none
ok - fm-control exit: every verified harness gets its own verified exit command
ok - fm-control interrupt: every verified harness gets its own verified key and repeat count
ok - fm-control interrupt: opencode needs a double Escape, claude a single one
ok - fm-control: a harness with no verified control mechanics is refused, not guessed at
ok - fm-control-lib: a recorded harness resolves to its verified adapter without guessing
ok - fm-control: prefixed recorded harnesses reach interrupt and exit mechanics
ok - fm-control-lib: the backend key matrix matches each adapter's real send-key surface
ok - fm-control-lib: adapter capability is per task kind, not per adapter alone
ok - fm-control interrupt: a backend that cannot deliver the harness's key refuses instead of sending another
ok - fm-control: a backend that cannot prove an agent stopped refuses exit and relaunch
ok - fm-control-lib: stop-proving verbs are gated on the backends that really classify agent state
ok - fm-control: a legacy window label is refused and the exact task id is named
ok - fm-control: an explicit backend endpoint is never a control target
ok - fm-control: an unrecorded task id is refused
ok - fm-control: a record whose endpoint identity names another task is refused
ok - fm-control: a remotely placed secondmate is refused by placement, not by a metadata complaint
ok - fm-control: interrupt and exit lock before task-state resolution
ok - fm-control: the verb list is closed - no raw keys, arbitrary text, or clear verb
ok - fm-control: resume is refused with the determinism reason and the alternative
ok - fm-control: profile and note flags belong to relaunch only
ok - fm-control exit: an already-stopped agent is idempotent success with no bytes sent
ok - fm-control exit: a vanished endpoint refuses instead of silently succeeding
ok - fm-control interrupt: refuses when no agent is running rather than keying a shell
ok - fm-control exit: an endpoint whose process cannot be attributed refuses
ok - fm-control exit: a busy agent receives interrupt delivery before the exit command
ok - fm-control exit: an idle agent goes straight to its exit command
ok - fm-control interrupt: unconfirmed delivery preserves observed busy state
ok - fm-control interrupt: muse confirms cancellation from its session log
ok - fm-control interrupt: postconditions are revalidated after acknowledgement polling
ok - fm-control exit: an interrupt-stopped agent satisfies the gone-state postcondition
ok - fm-control exit: a stubborn agent reports delivered input and an unconfirmed exit
ok - fm-control interrupt: grok reports delivery without claiming cancellation
ok - fm-control interrupt: grok's idle footer does not confirm cancellation
ok - fm-control: a lifecycle command to a secondmate is unmarked and opens no reply expectation
ok - fm-control's arrival leaves fm-send's from-firstmate marking untouched
FM_TEST_END 2026-08-23T22:20:48Z tests/fm-control.test.sh exit=0 duration_ms=6699 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:20:48Z tests/fm-cursor-harness.test.sh family=unclassified expected_gate_skip=none
ok - fm_cursor_process_matches: cursor's real shapes identify; real node/agent lookalikes do not
ok - fm_cursor_process_matches: name and install-tree signals each carry a verdict alone
ok - fm_cursor_verify_executable: the legacy alias is accepted only with cursor evidence
ok - fm_cursor_resolve_binary: prints the stable launcher, not the versioned target
ok - tmux liveness: a cursor pane is agent; an unrelated node/agent is other, never dead
ok - fm-harness.sh: cursor's marker outranks an inherited CLAUDECODE
ok - fm-harness.sh: cursor-like node script names do not establish ancestry identity
ok - cursor transcript fold: role:user opens a turn, turn_ended closes it, aborts included
ok - cursor transcript fold: malformed closes cannot settle through either parser
ok - cursor transcript fold: partial appends never make an active turn idle
ok - cursor transcript fold: an unresolvable binding is unknown, never idle
ok - cursor transcript binding: exact recorded workspacePath only, never a prefix or rebuilt slug
ok - cursor transcript fold: a prior conversation is excluded so a relaunch folds its own turn
FM_TEST_END 2026-08-23T22:21:18Z tests/fm-cursor-harness.test.sh exit=0 duration_ms=30115 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:21:18Z tests/fm-cursor-primary-live-e2e.test.sh family=live-harness-optin expected_gate_skip=optin-env
skip: set FM_CURSOR_PRIMARY_LIVE_E2E=1 to run the live Cursor primary guard
FM_TEST_END 2026-08-23T22:21:18Z tests/fm-cursor-primary-live-e2e.test.sh exit=0 duration_ms=11 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:21:18Z tests/fm-cursor-primary.test.sh family=watcher-wake-lock expected_gate_skip=none
ok - fm-turnend-guard: Cursor payload is inert without --cursor and blocks with it
ok - fm-turnend-guard: a non-Cursor payload keeps blocking
ok - fm-claude-stop-autoarm: inert on a Cursor-delivered payload
ok - fm-sessionstart-run: inert on a Cursor payload, unchanged otherwise
ok - fm-arm-pretool-check: Cursor duplicate allows, --cursor denies in Cursor's own shape
ok - fm-cd-pretool-check: Cursor duplicate allows, --cursor denies in Cursor's own shape
ok - cursor park: silent no-op when no supervision is needed
ok - cursor park: an actionable close is delivered as one watcher-kind follow-up
ok - cursor park: always exits 0, even when supervision is genuinely down
ok - cursor park: the repair nag is bounded and then goes quiet
ok - cursor park: a repair nag is emitted only after its budget persists
ok - cursor park: a delivered wake resets the bounded repair budget
ok - cursor park: the loop_count ceiling warns exactly once, then stops the loop
ok - cursor park: an older park stands down after a newer stop claim
ok - cursor park: the newest stop exclusively owns a concurrent commit
ok - cursor park: a superseded park cannot consume repair budget
ok - cursor park: inert while away mode is active
ok - cursor park: an away-mode transition wins before follow-up commit
ok - cursor park: inert when this session does not hold the home lock
ok - cursor park: session takeover stops polling without output or state mutation
ok - cursor park: inert inside a child crewmate worktree
ok - cursor park: malformed payloads fail open without arming
ok - fm-sessionstart-cursor: sessionStart injects context
ok - fm-sessionstart-cursor: silent inside a child crewmate worktree
ok - cursor registration: covers every primary event with a bounded stop loop
ok - cursor bounds nest: firstmate's default ceiling stops the loop before Cursor's loop_limit does
FM_TEST_END 2026-08-23T22:21:54Z tests/fm-cursor-primary.test.sh exit=0 duration_ms=35404 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:21:54Z tests/fm-daemon.test.sh family=watcher-wake-lock expected_gate_skip=none
ok - fm-afk-start.sh fails before daemon startup when the afk flag cannot be written
ok - fm-afk-start.sh ignores stale pidfile-only live pids
ok - fm-afk-start.sh reclaims stale daemon locks whose live pid identity no longer matches
ok - supervise daemon state root is scoped by FM_HOME
ok - routine signal self-handles
ok - captain-relevant status verbs escalate
ok - check + unknown escalate; heartbeat self-handles
ok - transient stale self-handles and records a persistence marker
ok - enriched stale wedges bypass status absorption without disturbing busy workers
ok - stale + terminal status escalates immediately
ok - paused reasons with captain phrases remain pause-classified
ok - handle_wake on a paused stale records a pause marker, drops the wedge marker, and does not escalate
ok - handle_wake records a declared pause from a routine signal for long-cadence rechecks
ok - a terminal signal clears pause and stale tracking across both supervisors
ok - housekeeping migrates a normal-watcher's declared pause into daemon tracking
ok - housekeeping clears an already-resumed watcher pause across both supervisors
ok - housekeeping seeds pause tracking from status without a watcher marker
ok - persistent stale escalates after threshold and clears its marker
ok - resumed (busy) stale clears its marker without escalating
ok - housekeeping re-surfaces a stale declared pause on the long cadence and resets its window
ok - housekeeping clears a paused marker whose pane became busy again, without escalating
ok - housekeeping clears a paused marker once the crew is no longer declaring the pause
ok - housekeeping moves an existing stale marker to pause before wedge escalation
ok - housekeeping clears tracking when a crew leaves pause
ok - persistent herdr stale resolves the target from metadata and escalates
ok - herdr idle busy-footer stale clears through capture corroboration
ok - resumed herdr stale clears through backend-aware busy state
ok - persistent Orca stale resolves the terminal from metadata
ok - multiple escalations flush as a single batched digest
ok - batch flush measures max-delay from the first append, not the last
ok - catch-all scan escalates a missed terminal once, not twice
ok - handle_wake routes routine->self and captain->escalate
ok - INJECT_SKIP forces self-handle, bypassing captain-relevant classification
ok - is_wake_reason distinguishes watcher wake reasons from singleton-status stdout
ok - terminal-stale escalate removes its marker so housekeeping does not re-escalate
ok - captain signal escalate marks seen so the catch-all scan does not re-fire
ok - _collapse_newlines replaces newlines with literal separator
ok - afk flag absent: daemon does not inject, buffer preserved
ok - busy-guard defers injection when supervisor pane is busy
ok - marker detection: marker -> stay afk, no marker -> exit afk
ok - /afk invocation is exempt from afk exit (no self-cancel)
ok - should_exit_afk returns false when afk is not active
ok - strip_injection_marker removes the sentinel marker cleanly
ok - pane_input_pending detects partial input on the cursor line
ok - pane_input_pending: a blank unidentified cursor row defers (strict container-proof rule)
ok - pane_input_pending: only proven empty agent prompts pass
ok - fm_tmux_composer_state: a bare shell prompt ($/%/#/>) reads unknown, never empty (dead-shell injection safety)
ok - fm_tmux_composer_state: a bordered composer box and bare agent glyphs (❯/›) still read empty
ok - fm_tmux_composer_state: only matching edge borders form a composer box
ok - pane_input_pending preserves bright placeholder-like drafts in styled captures
ok - classify_signal dedupes against the catch-all scan seen marker
ok - classify_stale dedupes against the signal path seen marker
ok - AFK nonterminal working:+merged keeps wedge aging and re-escalates at bound
ok - genuine done: and merge-check events still escalate
ok - pane_input_pending: an idle bordered composer is NOT pending (afk-invx-i5)
ok - pane_input_pending: text inside a bordered composer is still pending
ok - submit-ACK confirms a submit when the composer returns to a bordered-empty box
ok - submit-ACK reports pending on a persistently swallowed Enter (type-once)
ok - max-defer on an empty stuck pane types once, alarms, and preserves the buffer
ok - max-defer flushes and clears the buffer on an empty bordered pane
ok - max-defer on a pending composer alarms without typing
ok - normal flush clears a stale wedge marker
ok - below MAX_DEFER: no inject, no alarm, buffer preserved
ok - max-defer does not flush or alarm while afk is inactive
ok - library mode: sourcing the daemon defaults FM_WEDGE_ALARM_EXEC to discard (no test can fire a real notification)
ok - wake helpers replace inherited notifier overrides with the safe recorder
ok - the discard seam suppresses every notifier, including command: (fires nothing)
ok - direct notifier helpers honor the discard seam, including command:
ok - osascript channel routes through the notifier seam with the summary (never a real notification)
ok - herdr channel routes through the notifier seam with the summary (never a real notification)
ok - command channel runs the captain command with the summary on $1 and on stdin
ok - command channel failures redact configured commands while logging their exit status
ok - unknown channel directives are redacted while the alarm keeps running
ok - off disables every active alert regardless of directive position (marker and tmux flash are unaffected)
ok - auto resolves to the macOS osascript notifier on Darwin (default-on)
ok - auto on a non-macOS platform selects no built-in OS channel (the marker or a configured command carries it)
ok - config/wedge-alarm selects every configured channel and skips comment and blank lines
ok - a failing channel logs and falls back to the next channel, never crashing the alarm
ok - a hung notifier is bounded, logged, and falls through to the next channel
ok - a backgrounded command notifier remains bounded until its process group is reaped
ok - a hung notifier override is bounded, logged, and proceeds to the next channel
ok - daemon shutdown stops and reaps the active notifier process group
ok - inject_wedge_alarm writes the marker AND emits the active alert even with no tmux status-line (herdr backend)
ok - in-process wedge throttle prevents alert spam when the marker cannot persist
ok - fm-send returns 3 with a non-error no-resend warning when confirmation stays pending
ok - fm-send exits non-zero when initial text send fails
ok - fm-send exits non-zero unless delivery is proven empty
ok - discover_supervisor_backend: override > TMUX_PANE > HERDR_ENV+HERDR_PANE_ID > tmux fallback
ok - discover_supervisor_target: override > TMUX_PANE > herdr '<session>:<pane-id>' composition > firstmate:0 fallback
ok - pane_is_busy: herdr native busy_state='busy' short-circuits without a capture fallback
ok - primary busy guard isolates rendered signatures by detected harness
ok - pane_is_busy: omitted backend defaults to tmux for Grok's isolated fallback
ok - pane_input_pending: dispatches through fm_backend_composer_state for backend=herdr
ok - inject_msg: herdr busy-guard defers before ever attempting a submit
ok - inject_msg: herdr composer-guard defers before ever attempting a submit
ok - inject_msg: herdr pane-gone check defers before any busy/composer/submit call
ok - inject_msg: dispatches busy-guard/composer-guard/submit through the herdr backend and succeeds on a confirmed empty composer
ok - inject_msg: defers on a dead-shell/unreadable composer (unknown), never typing the escalation into a shell
ok - inject_msg: unrecognized composer states defer by default
FM_TEST_END 2026-08-23T22:22:07Z tests/fm-daemon.test.sh exit=0 duration_ms=13493 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:22:07Z tests/fm-documentation-audiences.test.sh family=pure-contract-unit expected_gate_skip=none
ok - documentation inventory classifies every maintained prose surface exactly once
ok - classification, setup routing, and maintained-prose scope fail safely
ok - required documentation owner pointers cannot silently disappear
ok - local links resolve while dates, versions, commands, and incident prose remain semantically reviewed
FM_TEST_END 2026-08-23T22:22:08Z tests/fm-documentation-audiences.test.sh exit=0 duration_ms=553 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:22:08Z tests/fm-fleet-snapshot-view.test.sh family=snapshot-bearings expected_gate_skip=optional-binary
ok - empty fleet snapshot and view use explicit absence markers
ok - fixture snapshot covers task rows, backlog rows, pointers, and stable ordering
ok - main_inventory discloses orphan/unstructured and clears when inventory is consistent
ok - backlog normalization preserves strict roles and resolves every blocker compatibly
ok - snapshot event hints follow reconciled current state
ok - durable fold keeps an open decision past a later unrelated event
ok - a live secondmate endpoint preserves unrelated open decisions
ok - durable captain-held transfer closes the duplicate live status decision
ok - durable fold clears a decision only on a keyed resolution
ok - a completed scout's stale decision surfaces as a report pointer, not pending
ok - a scout still parked at a decision stays pending (terminal clear does not over-fire)
ok - snapshot includes durable scout reports after teardown
ok - snapshot parses tasks-axi rows and respects operational overrides
ok - fleet view renders the snapshot without secondmate peek guidance
ok - fleet view renders secondmate agent liveness
FM_TEST_END 2026-08-23T22:22:12Z tests/fm-fleet-snapshot-view.test.sh exit=0 duration_ms=4273 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:22:12Z tests/fm-fleet-sync.test.sh family=session-bootstrap expected_gate_skip=none
ok - detached clean ancestor is re-attached and fast-forwarded (recovered)
ok - detached HEAD with unique commits is reported STUCK and left untouched
ok - detached clean ancestor with diverged local default is reported STUCK and left untouched
ok - dirty working tree is reported STUCK and left untouched
ok - non-default named branch is reported STUCK and left untouched
ok - diverged default branch is reported STUCK and left untouched
ok - on-default clean behind clone still fast-forwards
ok - already-current clone is reported unchanged
ok - no-origin clone is skipped (benign), not flagged STUCK
ok - local-only clone is skipped (benign), not flagged STUCK
ok - single-project form accepts a bare project name
ok - single-project bare name resolution is not cwd-sensitive
ok - single-project form accepts a projects/<name> relative name
ok - single-project projects/<name> resolution is not cwd-sensitive
ok - single-project form leaves a genuinely bad name unresolved
ok - whole-fleet form processes every clone under projects/
ok - bootstrap relays recovered: and STUCK: fleet-sync outcomes
ok - orphaned provably-stale packed-refs.lock is cleared and the clone syncs
ok - a live packed-refs.lock is never removed and the sync fails loudly
ok - a live process holding the clone worktree dir blocks lock removal (clone-dir liveness)
ok - a transient packed-refs.lock that self-clears is retried without a force-remove
ok - a non-packed-refs.lock fetch failure keeps today's behavior (no retry)
FM_TEST_END 2026-08-23T22:22:27Z tests/fm-fleet-sync.test.sh exit=0 duration_ms=14824 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:22:27Z tests/fm-gate-refuse.test.sh family=session-bootstrap expected_gate_skip=none
ok - fm-gate-refuse-lib: refuses when NO_MISTAKES_GATE is set
ok - fm-gate-refuse-lib: refuses when NO_MISTAKES_GATE is set empty
ok - fm-gate-refuse-lib: refuses from a gate worktree via git-common-dir (marker unset)
ok - fm-gate-refuse-lib: no-op for a normal session (neither signal, set -eu clean)
ok - fm-spawn: refuses on marker and gate-worktree backstop; a normal crew spawn is unaffected
ok - fm-send: refuses on marker and gate-worktree backstop; a normal steer is unaffected
ok - fm-teardown: refuses on marker and gate-worktree backstop; a normal teardown is unaffected
FM_TEST_END 2026-08-23T22:22:30Z tests/fm-gate-refuse.test.sh exit=0 duration_ms=3032 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:22:30Z tests/fm-gitignore-config.test.sh family=unclassified expected_gate_skip=none
ok - config/ is ignored as a directory, covering unlisted and nested paths
ok - an unrelated path outside config/ remains visible to git
FM_TEST_END 2026-08-23T22:22:30Z tests/fm-gitignore-config.test.sh exit=0 duration_ms=18 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:22:30Z tests/fm-gotmp.test.sh family=session-bootstrap expected_gate_skip=none
ok - fm-teardown removes the dir pointed to by tasktmp= in meta
ok - fm-teardown skips gracefully when tasktmp= is absent (backward compat)
ok - fm-teardown skips gracefully when tasktmp= points to a nonexistent dir
FM_TEST_END 2026-08-23T22:22:30Z tests/fm-gotmp.test.sh exit=0 duration_ms=329 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:22:30Z tests/fm-grok-continuity-live-e2e.test.sh family=live-harness-optin expected_gate_skip=optin-env
skip: set FM_GROK_LIVE_E2E=1 to run the interactive Grok continuity regression
FM_TEST_END 2026-08-23T22:22:30Z tests/fm-grok-continuity-live-e2e.test.sh exit=0 duration_ms=10 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:22:30Z tests/fm-grok-stop-live-e2e.test.sh family=live-harness-optin expected_gate_skip=optin-env
skip: set FM_GROK_STOP_LIVE_E2E=1 with FM_GROK_NATIVE_BIN and FM_GROK_LEGACY_BIN
FM_TEST_END 2026-08-23T22:22:30Z tests/fm-grok-stop-live-e2e.test.sh exit=0 duration_ms=10 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:22:30Z tests/fm-guard-stale-banner.test.sh family=watcher-wake-lock expected_gate_skip=none
ok - fm-guard stale banner: first stale call prints the full actionable banner
ok - fm-guard stale banner: repeated same-episode calls print a concise reminder only
ok - fm-guard stale banner: Pi and pi-signed primaries route themselves to the extension model
ok - fm-guard stale banner: extension-owned hand-off with a live session is healthy
ok - fm-guard stale banner: extension-owned empty lock is genuinely unheld
ok - fm-guard stale banner: held unhealthy extension locks stay loud
ok - fm-guard stale banner: extension model without ownership evidence stays loud
ok - fm-guard stale banner: every extension-ownership signal is load-bearing
ok - fm-guard stale banner: extension model still alarms on a genuinely stale beacon
ok - fm-guard stale banner: queued-wake warning survives the extension hand-off tolerance
ok - fm-guard stale banner: persistent primaries ignore Pi extension evidence
ok - fm-guard stale banner: extension model stays silent for a live watcher
ok - fm-guard stale banner: auto-arm fresh beacon without a live watcher is healthy
ok - fm-guard stale banner: auto-arm stale beacon alarms with the true reason
ok - fm-guard stale banner: auto-arm stale episode stays one episode across calls
ok - fm-guard stale banner: persistent no-watcher banner names the true reason
ok - fm-guard stale banner: a no-watcher episode survives a beacon mtime change
ok - fm-guard stale banner: a fresh beacon without a live watcher remains unhealthy
ok - fm-guard stale banner: X-mode polling without a live watcher remains unhealthy
ok - fm-guard stale banner: healthy recovery rearms the next stale episode
ok - fm-guard stale banner: concurrent same-episode calls claim exactly one full banner
ok - fm-guard stale banner: deduplication is isolated per FM_HOME
ok - fm-guard stale banner: queued-wake warning remains independent
ok - fm-guard stale banner: read-only before writable does not consume full banner
ok - fm-guard stale banner: read-only during episode observes without mutating marker
ok - fm-guard stale banner: healthy read-only does not clear marker
ok - fm-guard stale banner: read-only never mutates stale-banner state files
FM_TEST_END 2026-08-23T22:22:34Z tests/fm-guard-stale-banner.test.sh exit=0 duration_ms=4114 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:22:34Z tests/fm-harness-liveness-drift-live-e2e.test.sh family=live-harness-optin expected_gate_skip=optin-env
skip: set FM_HARNESS_LIVENESS_DRIFT=1 to run the installed-harness liveness drift guard
FM_TEST_END 2026-08-23T22:22:34Z tests/fm-harness-liveness-drift-live-e2e.test.sh exit=0 duration_ms=10 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:22:34Z tests/fm-herdr-session-cleanup.test.sh family=backend-dispatch expected_gate_skip=none
ok - process proof reads Linux Herdr argv arrays and rejects malformed executable identities
ok - exact stale projection closes one exact pane under task then presentation locks
ok - successful cleanup is idempotent on repeat
ok - malformed title preserves the candidate
ok - missing token preserves the candidate
ok - malformed journal preserves the candidate
ok - duplicate token preserves the candidate
ok - duplicate title token preserves the candidate
ok - zero journal match preserves the candidate
ok - multiple journal matches preserves the candidate
ok - cross-home journal preserves the candidate
ok - v2 workspace binding mismatch preserves the candidate
ok - v2 tab binding mismatch preserves the candidate
ok - v2 pane binding mismatch preserves the candidate
ok - v2 cleanup requires and accepts the exact journal endpoint binding
ok - current task metadata preserves the candidate
ok - registered agent preserves the candidate
ok - unknown agent preserves the candidate
ok - multiple tabs preserves the candidate
ok - multiple panes preserves the candidate
ok - non-idle shell preserves the candidate
ok - child process or shell job preserves the candidate
ok - unreadable snapshot preserves the candidate
ok - unreadable topology check preserves the candidate
ok - revalidation race preserves the candidate
ok - active target preserves the candidate
ok - focus refusal preserves the candidate
ok - standalone bootstrap cannot run lock-owned stale projection cleanup
ok - session start runs cleanup only after acquiring its home lock
all fm-herdr-session-cleanup tests passed
FM_TEST_END 2026-08-23T22:22:39Z tests/fm-herdr-session-cleanup.test.sh exit=0 duration_ms=4654 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:22:39Z tests/fm-herdr-version-floor-live-e2e.test.sh family=live-harness-optin expected_gate_skip=optin-env
skip: set FM_HERDR_VERSION_FLOOR_LIVE_E2E=1 to run the real-release Herdr version-floor guard
FM_TEST_END 2026-08-23T22:22:39Z tests/fm-herdr-version-floor-live-e2e.test.sh exit=0 duration_ms=10 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:22:39Z tests/fm-inactive-reconcile.test.sh family=watcher-wake-lock expected_gate_skip=none
actionable: inactive terminal outcome awaiting captain presentation: child=child state=done pr=https://example.test/owner/repo/pull/1
ok - main direct terminal presentation has a durable receipt
ok - secondmate reports its own inactive terminal child

@jk1rby

jk1rby commented Aug 23, 2026

Copy link
Copy Markdown

bin/fm-ci.sh full output — part 4/6

actionable: inactive terminal outcome needs parent report: child=child state=failed
ok - relative local parent homes fail closed
ok - invalid secondmate markers block routing and surface the obligation
ok - remote parent-replies mirror input is durable and idempotent
ok - reused task ids retain per-incarnation terminal receipts
ok - legacy metadata rewrites preserve terminal receipt identity
ok - relaunch cannot replace metadata during terminal snapshot
actionable: inactive terminal outcome awaiting captain presentation: child=child state=done pr=https://example.test/owner/repo/pull/1
ok - terminal reconciliation ignores heartbeat backoff state
actionable: inactive terminal outcome awaiting captain presentation: child=child state=done pr=https://example.test/owner/repo/pull/1
ok - scan marker replaces a symlink without overwriting its target
ok - nonterminal and captain-held workers remain outside inactive terminal reporting
ok - watcher hook wakes for terminal loss and preserves idle secondmate exemption
actionable: inactive terminal outcome awaiting captain presentation: child=b state=done pr=https://example.test/owner/repo/pull/1
ok - stalled state reads are bounded without starving later children
ok - aggregate scan budget includes durable wake operations
actionable: inactive terminal outcome needs parent report: child=child state=failed
ok - notice recovery remains idempotent across queue acknowledgement
actionable: inactive terminal outcome awaiting captain presentation: child=child state=done pr=https://example.test/owner/repo/pull/1
ok - reconciliation makes zero forge or PR API calls
all inactive reconciliation tests passed
FM_TEST_END 2026-08-23T22:23:17Z tests/fm-inactive-reconcile.test.sh exit=0 duration_ms=38377 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:23:17Z tests/fm-kimi-harness.test.sh family=pure-contract-unit expected_gate_skip=none
ok - Kimi hook install is idempotent and removal restores every foreign config byte
ok - Kimi hook removal preserves owned newline boundaries and pristine bytes
ok - Kimi hook install refuses missing, malformed, and surprising config without writing
ok - Kimi hook install refuses without jq before any config write
ok - fm-spawn: kimi launches, delivers its brief, and registers a guarded turn-end token
ok - Kimi hook stays silent and inert without a Firstmate registry token
ok - fm-spawn: unsafe Kimi global config refuses before pane creation
ok - fm-teardown: Kimi task pointer and registry token are removed
ok - fm-spawn: Kimi fallback expands the active HOME
ok - fm-spawn: missing Kimi executable refuses before pane creation
ok - fm-spawn: kimi treats a silent pointer drop as a failed spawn
ok - fm-spawn: kimi never sends the brief pointer before an observable ready signal
ok - fm-harness: markerless kimi is detected by ancestry after env-marker precedence
lock acquired: harness pid 3208212
ok - fm-lock recognizes Kimi ancestry and live lock holders
ok - busy detection: real Kimi moon-plus-middot captures require its harness while idle labels stay idle
ok - fm-watch classifies Kimi as unknown rather than from its spinner, and Grok's fallback stays isolated
ok - composer classifier: kimi's existing bordered > shape is already safe without an override
FM_TEST_END 2026-08-23T22:23:30Z tests/fm-kimi-harness.test.sh exit=0 duration_ms=12410 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:23:30Z tests/fm-lint-workflows.test.sh family=pure-contract-unit expected_gate_skip=none
ok - fm-lint-workflows.sh pins an explicit actionlint version (1.7.12)
ok - the repository's own .github/workflows state lints cleanly
ok - column-0 heredoc workflow fails validation with a clear error
ok - valid fixture workflow passes
ok - an empty workflows directory is a clean no-op
ok - an absent .github/workflows directory is a clean no-op
ok - a workflow added back later is still validated
ok - explicit malformed workflow path fails validation
ok - non-mapping workflow YAML root fails
ok - missing actionlint fails closed
ok - fm-lint-workflows.sh refuses to lint under a non-pinned actionlint version
ok - actionlint installer retries a transient download failure
ok - actionlint installer selects the official archive, URL, and checksum per OS/arch
ok - actionlint installer rejects a wrong checksum
ok - actionlint installer falls back to shasum -a 256 when sha256sum is absent
ok - actionlint installer prefers sha256sum when both hashers are present
ok - actionlint installer rejects an unsupported OS or architecture
ok - fm-lint.sh default path catches a malformed workflow
FM_TEST_END 2026-08-23T22:23:30Z tests/fm-lint-workflows.test.sh exit=0 duration_ms=577 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:23:30Z tests/fm-muse-harness.test.sh family=pure-contract-unit expected_gate_skip=none
ok - muse is detected through any versioned muse-bin ancestor
ok - muse detection does not claim unrelated muse-containing commands
ok - muse launch clears foreign harness markers before ancestry detection
ok - muse spawn launches with autonomy, privacy control, and a positional brief
ok - muse maps the shared effort vocabulary and reaches ultra only via explicit max
ok - muse spawn refuses when no credential can reach the provider
ok - muse spawn refuses a META_API_KEY that cannot reach the worker
ok - muse spawn accepts a stored credential without META_API_KEY
ok - muse resolves relative XDG roots before preflight and launch
ok - muse is refused as a secondmate harness
ok - muse spawn writes a session binding that teardown removes
ok - every accepted muse Escape alias clears the restored composer
ok - the composer clear is scoped to muse and does not touch other adapters
ok - a failed muse composer clear fails loudly instead of leaving stale input
ok - the run fold tracks open, settled, interrupted, reopened, and run-free logs
ok - a nested terminal record never settles an in-flight run
ok - the session binding folds only the log matching this task's worktree
ok - workspace bindings compare decoded paths literally
ok - spawn-time exclusions select the current session across equal mtimes
ok - the Muse session cache avoids rescans and refreshes safely across incarnations
ok - a changed Muse namespace revalidates cached session uniqueness
ok - sub-agent session logs are excluded from the parent's busy fold
ok - every unproven muse binding classifies unknown rather than idle
ok - a settled session log reads idle for both completed and interrupted turns
ok - muse trusts no busy record source
FM_TEST_END 2026-08-23T22:23:53Z tests/fm-muse-harness.test.sh exit=0 duration_ms=22116 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:23:53Z tests/fm-muse-signals-live-e2e.test.sh family=live-harness-optin expected_gate_skip=optin-env
skip: set FM_MUSE_SIGNALS_LIVE=1 to run the real Muse signal drift guard
FM_TEST_END 2026-08-23T22:23:53Z tests/fm-muse-signals-live-e2e.test.sh exit=0 duration_ms=11 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:23:53Z tests/fm-on.test.sh family=secondmate expected_gate_skip=none
ok - fm-on preserves argv, stdin, stdout, stderr, and exit status without shell interpretation
ok - fm-on arms a bounded SSH dead-peer detection window by default (15s x 3 = 45s)
ok - fm-on's dead-peer detection window is env-overridable
ok - fm-on rejects invalid dead-peer settings before launching ssh
ok - the fixed entrypoint runs every command in the worker's explicit environment
ok - the entrypoint composes a deduplicated discovered child PATH (kept 18 existing, omitted 8 absent)
ok - read-only doctor inspects worker gaps over plain SSH without repair
ok - the remote doctor reports the same PATH the entrypoint hands its children
ok - the remote doctor derives tool readiness from the installed worker
ok - the remote doctor reports its required runtime tool set and optional tools
ok - multiple fm-*.sh executables work without a command table
ok - tracked-command authorization excludes checkout-local git
ok - doctor bootstrap remains authenticated when git is unavailable
ok - transport rejects shell escape, traversal, symlink, and option-injection surfaces
ok - the fixed entrypoint refuses incompatible protocols and unsafe roots
ok - ambiguous aliases refuse while exact secondmate ids remain routable
ok - unreachable and ambiguous transport failures are surfaced without retry
ALL TESTS PASSED
FM_TEST_END 2026-08-23T22:23:58Z tests/fm-on.test.sh exit=0 duration_ms=5528 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:23:58Z tests/fm-opencode-primary-live-e2e.test.sh family=live-harness-optin expected_gate_skip=optin-env
skip: set FM_OPENCODE_LIVE_E2E=1 to run the interactive OpenCode continuity regression
FM_TEST_END 2026-08-23T22:23:58Z tests/fm-opencode-primary-live-e2e.test.sh exit=0 duration_ms=11 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:23:58Z tests/fm-operational-input.test.sh family=pure-contract-unit expected_gate_skip=none
ok - operational input: every current generic envelope retains its exact structured kind
ok - operational input: the established from-firstmate carrier remains structurally typed and byte-compatible
ok - operational input: untyped landed FIRSTMATE_OP transcripts are explicit legacy-operational input
ok - operational input: historical prose compatibility is isolated from current parsing
ok - operational input: quoted, ASCII-only, arbitrary-U+2063, altered-legacy, and label-only near misses stay genuine
ok - operational input: the OpenCode adapter constructs through the canonical owner
ok - operational input: current construction rejects legacy kinds and empty bodies
FM_TEST_END 2026-08-23T22:23:58Z tests/fm-operational-input.test.sh exit=0 duration_ms=220 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:23:58Z tests/fm-peek-remote.test.sh family=unclassified expected_gate_skip=none
ok - fm-peek remote: the capture routes over the remote transport, local adapters untouched
ok - fm-peek remote: an unreachable host fails loudly without a false death claim
all fm-peek-remote tests passed
FM_TEST_END 2026-08-23T22:23:59Z tests/fm-peek-remote.test.sh exit=0 duration_ms=275 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:23:59Z tests/fm-pending-reply.test.sh family=unclassified expected_gate_skip=none
ok - normal correlated reply resolves once (idempotent)
ok - completed turn with no report triggers exactly one recovery
ok - recovery attempts reconcile without reinjection
ok - recovery reply resolves the original expectation
ok - second missed turn escalates once and remains durable
ok - escalations and replies wake; the home's own escalation close stays quiet
ok - failed escalation publication remains retryable and publishes once
ok - legacy escalation closes under the shared default key
ok - legacy escalation cannot close an unrelated default-key decision
ok - foreign correlated blocker cannot impersonate a pending-reply escalation
ok - concurrent resolution closes one keyed escalation exactly once
ok - concurrent escalation yields to a late correlated reply
ok - transport success cannot masquerade as reply success
ok - undelivered records remain immutable across scan paths
ok - delivery confirmation fallback reconciles durably
ok - unrelated events and stale correlation ids cannot resolve
ok - restart preserves expectation and exact parent destination
ok - wrong-home reports are detected but do not silently acknowledge
ok - direct unmarked captain input creates no expectation
ok - fm-send marked secondmate path creates pending and embeds corr
ok - status-pointed document resolves the expectation
ok - optional helper report resolves without being required for correctness
ok - backend busy/idle observation covers Pi/Claude paths without conversation scrape
ok - tmux and zellij unknown states use bounded capture fallback
ok - pending replies scope Kimi capture fallback by recorded harness
ok - tick skips terminal records and reuses target observations
ok - correlations are reused only for matching open task records
ok - tick end-to-end: miss -> one recovery -> escalate -> durable
ok - failed transport discards undelivered expectation only
ok - all pending-reply tests passed
FM_TEST_END 2026-08-23T22:24:09Z tests/fm-pending-reply.test.sh exit=0 duration_ms=10019 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:24:09Z tests/fm-pi-primary-live-e2e.test.sh family=live-harness-optin expected_gate_skip=optin-env
skip: set FM_PI_LIVE_E2E=1 to run the isolated interactive Pi regression
FM_TEST_END 2026-08-23T22:24:09Z tests/fm-pi-primary-live-e2e.test.sh exit=0 duration_ms=9 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:24:09Z tests/fm-pi-watch-extension.test.sh family=watcher-wake-lock expected_gate_skip=none
ok - Pi extension reports external healthy watcher output
ok - Pi custom tool exposes repair-only metadata and returns automatic-continuation guidance
ok - Pi redundant tool call returns ownership guidance and spawns no second child
ok - Pi scheduled retry remains extension-owned after another tool call
ok - Pi actionable close starts one successor before wake delivery settles
ok - Pi hung successor falls back to one typed actionable wake
ok - Pi unretired successor falls back without an overlapping retry
ok - Pi late unretired closes resume classified supervision
ok - Pi clean empty close triggers a bounded continuity retry
ok - Pi established clean closes stop at the configured retry limit
ok - Pi close handler verifies session-lock ownership before successor launch
ok - Pi watcher arm distinguishes all session lock ownership states
ok - Pi session transitions use a generation owner across /new /resume /fork, stale callbacks, and quit
ok - Pi process-exit cleanup listener remains singular across session replacement
ok - Pi process-exit cleanup stops the attached arm child
ok - OpenCode plugins have an explicit ESM boundary even under a typeless parent package
ok - OpenCode watcher plugin uses the effective FM_HOME state
ok - OpenCode watcher plugin sources the effective config
ok - OpenCode watcher plugin requires session lock ownership
ok - OpenCode watcher coordinator respects primary scope
ok - OpenCode watcher plugin starts one successor before wake prompt delivery settles
ok - OpenCode pre-ready actionable close preserves its successor
ok - OpenCode hung successor falls back to one typed actionable wake
ok - OpenCode unretired successor falls back without an overlapping retry
ok - OpenCode late unretired closes resume classified supervision
ok - OpenCode clean empty close triggers a bounded continuity retry
ok - OpenCode established clean closes stop at the configured retry limit
ok - OpenCode close handler verifies session-lock ownership before successor launch
ok - OpenCode watcher plugin coordinates with the turn-end guard
ok - OpenCode healthy arm output does not suppress the turn-end guard
FM_TEST_END 2026-08-23T22:24:21Z tests/fm-pi-watch-extension.test.sh exit=0 duration_ms=12356 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:24:21Z tests/fm-pid-alive.test.sh family=unclassified expected_gate_skip=none
ok - fm_pid_alive: a live process reports alive and a missing or malformed pid reports dead
ok - fm_pid_alive: an unreaped process reports dead even though kill -0 succeeds
ok - fm_pid_alive: a suspended process keeps its lock when no dwell can be measured
ok - fm_pid_alive: a suspended process is swept only after the dwell, with a witness that it has not run since
ok - fm_pid_alive: writes and clears only its own sighting record, never the session lock's
ok - fm_pid_alive: D, running and unreadable states keep their lock; Z, X, x, T and t do not
ok - fm_pid_alive: an empty or out-of-tree sighting record name is refused loudly and removes nothing
ok - fm_pid_alive: costs no external command per call on a /proc platform
FM_TEST_END 2026-08-23T22:24:22Z tests/fm-pid-alive.test.sh exit=0 duration_ms=511 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:24:22Z tests/fm-pr-check-security.test.sh family=pr-forge expected_gate_skip=none
ok - raw-byte parser accepts canonical URLs and rejects the complete adversarial matrix
ok - GitLab merge requests are followed on any instance and never wake falsely
ok - validated merged polls notify once and retire before the next watcher cycle
ok - merged poll retirement preserves every persistent secondmate lifecycle artifact
ok - queue, receipt, and every fixed-path removal crash point recover without loss or repeated execution
ok - open/red, closed-unmerged, malformed, and forge errors remain armed until an exact merged transition
ok - replacement, nonterminal, tampered, and custom results receive no deletion authority
ok - queue failure and untrusted receipts preserve canonical poll evidence
ok - GitHub and GitLab exact merged results share one retirement path
ok - PR and teardown entrypoints reject invalid arguments before every side effect
ok - valid direct and merge flows record exact metadata and reject multiline head metadata
ok - rejected metacharacter bytes remain inert at generation and watcher time
ok - static poll is silent except for one merged line and remains watcher-bounded
ok - interrupted atomic preparation cleans private temporaries and publishes nothing
ok - concurrent watchers observe only complete private poll publications
ok - post-rename poll validation faults revoke both names and allow a clean retry
ok - migration creates and validates private state before watcher exclusion
ok - migration pauses older watchers and acquires exclusion before its first scan or marker
ok - poll, marker, diagnostic, and quarantine paths refuse symlinks and directories
ok - marker and diagnostic rename errors and signals fail closed and recover durably on retry
ok - post-rename marker, diagnostic, and obligation faults are revoked and reconstructed on retry
ok - quarantine type and mode faults fail closed and recover only when a retry can validate them
ok - canonical and ambiguous failure obligations block every retry until all task artifacts are repaired
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WORKTREE TANGLE - PRIMARY CHECKOUT IS ON A FEATURE BRANCH
●  /home/jk/.treehouse/firstmate-b4b40d/2/firstmate is on 'fm/fm-briefs-ignore-devpod-rebase', not its default branch 'main'.
●  A crewmate likely branched/committed in the primary instead of its own worktree.
●  The work is SAFE on the 'fm/fm-briefs-ignore-devpod-rebase' ref.
●  Restore the primary to 'main':
●      git -C /home/jk/.treehouse/firstmate-b4b40d/2/firstmate checkout main
●  then re-validate 'fm/fm-briefs-ignore-devpod-rebase' in a proper isolated worktree.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  1 task(s) in flight, but no watcher has a fresh beacon (last beat: never, grace 300s).
●  Trust the emitted supervision protocol for this harness; do not use shell & for watcher repair.
●  This is a supervision warning only; the guarded operation WILL still run.
●  watcher supervision needs Stop-owned automatic recovery; inspect the hook registration and startup status before ending the turn.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ok - ambiguous migration recovery accepts an explicitly validated replacement poll
ok - ambiguous repair rejects copied, metadata- or task-mismatched, forged, and partial poll publications
ok - all live, marker, diagnostic, X, custom-check, obligation, and teardown boundaries require single-link files
ok - canonical publication failure remains incomplete until a later clean retry rebuilds the poll
ok - legacy reserved obligations and delimiter-bearing task IDs retry without ambiguity
ok - migration never executes legacy checks, preserves X mode, quarantines ambiguity, and is idempotent
ok - historical X shims migrate only from the exact single-link mode-0755 identity
ok - direct registration refreshes authenticated v1 X shims across marker states
ok - bootstrap runs the non-executing migration at the locked session boundary
ok - bootstrap isolates incomplete poll migration from unrelated recovery sweeps
ok - watcher signals promptly stop custom checks and clean private state
ok - returned custom check descendants are drained on installed and fallback timeout paths
ok - teardown removes safe poll artifacts and refuses quarantine-directory symlinks without traversal
FM_TEST_END 2026-08-23T22:26:36Z tests/fm-pr-check-security.test.sh exit=0 duration_ms=134501 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:26:36Z tests/fm-procevent-when.test.sh family=unclassified expected_gate_skip=none
ok - arm binds, refuses duplicates, and retire cleans up
ok - concurrent arms publish exactly one complete watch
ok - a stable true fires the action exactly once and wakes with the outcome
ok - a flapping condition never reaches the action
ok - an action failure wakes with the captured error
ok - a repeatedly erroring condition wakes firstmate instead of firing
ok - an expired deadline wakes with never-true
ok - a late true poll cannot fire after its deadline
ok - action timeouts terminate the complete process group
ok - command output staging stays within its byte bound
ok - a restart after a claimed fire reports ambiguity instead of double-firing
ok - a mutated spec is refused without executing anything
ok - mutated action bytes are refused before claiming the fire
all fm-procevent-when tests passed
FM_TEST_END 2026-08-23T22:26:49Z tests/fm-procevent-when.test.sh exit=0 duration_ms=12357 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:26:49Z tests/fm-procevent.test.sh family=unclassified expected_gate_skip=none
ok - no configured source means no generated state and no process
ok - one blocking completion yields exactly one bounded normalized event
not-autohandled: direct-src (left for the handler; still unacknowledged)
ok - public start owns the process group recorded by its claim
ok - public start never claims an inherited caller process group
ok - an unhandled result survives restart and repeat drains, and only explicit acknowledgement stops its re-announcement
ok - publication cannot race a handled acknowledgement
ok - handled acknowledgement creation is private and fails safely
ok - automatic application waits for durable publication and failed publication remains recoverable
ok - a self-announcing adapter applies quietly and still publishes what it could not apply
not-autohandled: ends-src (left for the handler; still unacknowledged)
ok - an adapter-classified terminal result is captured once, announced, and retires its source automatically
not-autohandled: open-src (left for the handler; still unacknowledged)
ok - a source stays armed unless its own adapter classifies the result terminal
not-autohandled: replace-src (left for the handler; still unacknowledged)
ok - terminal retirement preserves and releases a concurrently replaced registration
ok - failed terminal retirement is fail-closed and idempotently recoverable
ok - one Send & End yields exactly one captured result, automatic retirement, and no recurring poll
ok - a drained-but-unhandled result survives a replacement session and is retired only by explicit handling, never twice
ok - pending results preserve numeric order and distinct wake identity
ok - one owner per canonical source across homes
ok - retiring a never-completing source stops its runner and its blocked child
ok - reconcile reaps a runner whose source registration is gone
not-autohandled: stale-src (left for the handler; still unacknowledged)
ok - stale-owner recovery removes abandoned output without displacing a live owner
not-autohandled: cross-home-src (left for the handler; still unacknowledged)
ok - cross-home stale recovery removes abandoned output from the old state directory
not-autohandled: race-src (left for the handler; still unacknowledged)
not-autohandled: race-src (left for the handler; still unacknowledged)
ok - concurrent stale-claim replacement starts exactly one runner
ok - a crashed runner leader never lets a live owned group be reclaimed as stale
ok - a truly dead generation with no surviving group is still safely reclaimed
ok - claim replacement cannot produce a torn ownership snapshot
ok - retirement and start share one serialized lifecycle boundary
ok - PID reuse cannot signal an unrelated process
ok - transient identity failure preserves the live source for retry
ok - bounded home sweep preflights then retires every locally owned source
ok - home sweep leaves foreign-home claims and runners untouched
ok - home sweep refuses safely until runner identity is readable
ok - healthy runtime behavior remains registration-only
ok - registration rejects unrepresentable newline arguments
ok - nonzero exit with no output stays armed and silent
ok - oversized output is bounded rather than published whole or dropped
ok - live output stays bounded and retirement reaps the whole source group
ok - invalid output bounds fail closed
ok - the adapter derives physical identity without newline path corruption
ok - source-only homes trigger the general supervision guard
ok - the adapter classifies published poll output safely
ok - the adapter owns which Lavish results end a source, and payload text cannot forge one
ok - the published interfaces state the loss limitation and claim no lossless delivery

all procevent tests passed
FM_TEST_END 2026-08-23T22:27:34Z tests/fm-procevent.test.sh exit=0 duration_ms=45039 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:27:34Z tests/fm-project-origin.test.sh family=unclassified expected_gate_skip=none
ok - ordinary clone URLs are accepted and clone with the command the remote host runs
ok - executable transports, option-shaped values, and unusable spellings are refused
ALL TESTS PASSED
FM_TEST_END 2026-08-23T22:27:34Z tests/fm-project-origin.test.sh exit=0 duration_ms=178 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:27:34Z tests/fm-public-followup.test.sh family=unclassified expected_gate_skip=none
ok - outcome text is collapsed to one line, bounded by codepoint, and never corrupts characters
ok - restart end-to-end: typed result reconciles from disk and delivers one reply to the original thread
ok - duplicate terminal results, restart replay, and repeated delivery are all no-ops
ok - wrong source, wrong work id, stale generation, malformed, unsupported deliverable, and forged identity are all refused
ok - a relay transport failure is held as retryable with no false completion, and the retry posts once
ok - a dry-run records no public delivery and leaves the commitment retryable
ok - a late success receipt closes the exact attempt with no second post, and a mismatched attempt is refused
ok - typed terminal cleanup clears the legacy link without posting
ok - a delivery interrupted between post and receipt refuses to repost
ok - a child home reports typed results but can never become the outward-post owner
ok - typed delivery refuses to post when its cleanup registration is missing
ok - marked secondmate teardown resolves its parent and fails closed when unavailable
ok - local seeding publishes durable parent state before its identity marker
ok - a lost launch-time parent binding is recovered from the durable local record
ok - a durable local parent record does not bypass a genuinely missing parent-side registration
ok - unknown durable parent fields remain forward-compatible
ok - conflicting live and durable parent bindings fail closed
ok - unsafe durable parent records fail closed before cleanup
ok - a NUL-bearing durable parent record fails closed before cleanup
ok - relay-disabled unmarked teardown runs no public-followup work
ok - a marked child proceeds without tasks-axi when its parent relay is disabled
ok - secondmate parent resolution matches the durable registry id literally
ok - traversal-shaped registrations are rejected before path construction or posting
ok - pending keeps registrations when tasks-axi returns malformed JSON
ok - the retained private request context keeps the original thread deliverable after inbox cleanup
ok - cleanup refuses while a public reply is owed and proceeds once it has landed
ok - a relay-disabled home runs no tasks-axi call, prints nothing, and gains no artifact
ok - a relay-enabled home with no commitments makes no backlog call and stays silent
ok - a relay-exhausted follow-up binding is escalated rather than retried into the thread
ok - the relay poll stays inert without a token, silent with no commitments, and surfaces a new result once
ok - startup surfaces unresolved public commitments only in a relay home that owes one
ok - typed public-followup records carry only public-safe summaries and deliverables
FM_TEST_END 2026-08-23T22:27:53Z tests/fm-public-followup.test.sh exit=0 duration_ms=19017 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:27:53Z tests/fm-push-guard.test.sh family=pure-contract-unit expected_gate_skip=none
ok - an ordinary push to a forge remote is refused
ok - a push into the gate repository is allowed by URL shape
ok - a push to the remote named no-mistakes is allowed
ok - the pipeline's own push is allowed by the gate env marker
ok - the path backstop allows a gate push with no env marker
ok - a delete-only push is allowed, matching the removed check's scope
ok - a deletion cannot smuggle a ref update past the guard
ok - an empty ref list refuses instead of stepping aside
ok - FM_ALLOW_DIRECT_PUSH allows a deliberate push and records why
ok - a broken hook installation is a usage error, not an allowed push
ok - an unwired clone reports COULD NOT CHECK, never a pass
ok - a hooksPath pointing elsewhere reports COULD NOT CHECK
ok - a deleted tracked hook fails rather than reporting a local gap
ok - a non-executable tracked hook fails rather than reporting a local gap
ok - a no-mistakes gate worktree is exempt from the hook wiring requirement
ok - the gate exemption never excuses a broken tracked hook
ok - the installer wires the guard and verification then passes
ok - installer --check reports without changing the clone
ok - the installer refuses to overwrite an existing hooks path
ok - the tracked hook refuses when its guard is missing
FM_TEST_END 2026-08-23T22:27:53Z tests/fm-push-guard.test.sh exit=0 duration_ms=442 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:27:53Z tests/fm-quota-array-dispatch-live-e2e.test.sh family=live-harness-optin expected_gate_skip=optin-env
skip: set FM_QUOTA_ARRAY_DISPATCH_LIVE_E2E=1 to run the credentialed Pi dispatch-selection regression
FM_TEST_END 2026-08-23T22:27:53Z tests/fm-quota-array-dispatch-live-e2e.test.sh exit=0 duration_ms=9 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:27:53Z tests/fm-remote-backlog-handoff.test.sh family=secondmate expected_gate_skip=none
ok - confined put rejects incomplete and superseded payload generations
ok - confined put rejects directory replacement without external writes
ok - ambiguous receipt leaves one durable outbox and no duplicate dispatchable source
ok - re-delivery after unknown completion converges without duplication
ok - dropped transfer recovery overwrites scratch and delivers exactly once
ok - concurrent handoffs serialize staging through confirmed cleanup
ok - receiver removes one proven dead stale lock and retries once
ok - bootstrap detects pending outbox handoffs without a journal
ok - route classification serializes with retirement before staging
ok - unconfigured bootstrap has no remote handoff behavior
ALL TESTS PASSED
FM_TEST_END 2026-08-23T22:28:07Z tests/fm-remote-backlog-handoff.test.sh exit=0 duration_ms=14069 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:28:07Z tests/fm-remote-doctor.test.sh family=secondmate expected_gate_skip=none
ok - a missing herdr CLI is a human gap that --fix never claims to close
ok - an absent launch agent is a fixable gap and the read-only run changes nothing
ok - --fix installs the dedicated fm-remote launch agent without touching default
ok - --fix is idempotent once the host is ready
ok - a loaded and running launch agent must match the complete owned contract
ok - a failed reload leaves stale effective launch-agent state unready
ok - a launch agent outside the Aqua session scope is rewritten in place
ok - launchd failures are reported and delayed server readiness is awaited
ok - human gaps are reported with their operator step and never claimed as fixed
ok - a non-darwin host skips launch agents and starts its herdr server directly
ok - --fix creates only owned version-manager wrappers and never clobbers an operator file
ok - doctor refreshes stale worker identity before probing tools
ok - the entrypoint symlink is recreated when absent and never overwritten when operator-owned
FM_TEST_END 2026-08-23T22:28:10Z tests/fm-remote-doctor.test.sh exit=0 duration_ms=2944 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:28:10Z tests/fm-remote-entrypoint.test.sh family=unclassified expected_gate_skip=none
ok - fm-remote-entrypoint.sh invoked via a PATH symlink resolves SCRIPT_DIR to the real bin/ directory
ok - fm-remote-entrypoint.sh invoked directly still resolves SCRIPT_DIR correctly
FM_TEST_END 2026-08-23T22:28:11Z tests/fm-remote-entrypoint.test.sh exit=0 duration_ms=155 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:28:11Z tests/fm-remote-job-orphan-reap.test.sh family=secondmate expected_gate_skip=none
ok - the Linux start path puts the whole worker tree in its own process group
ok - removing the state root and killing the recorded worker pid leaves the tree running at ppid 1
ok - a worker whose code root still exists is never reaped
ok - a worker stops its whole tree once its code root is pruned
ok - a dry run reports the abandoned worker and signals nothing
ok - the reaper stops an abandoned worker's whole tree
ok - the reaper is idempotent
FM_TEST_END 2026-08-23T22:28:14Z tests/fm-remote-job-orphan-reap.test.sh exit=0 duration_ms=3013 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:28:14Z tests/fm-remote-job.test.sh family=secondmate expected_gate_skip=none
ok - default queue and execution bounds independently cover long polls
ok - operator PATH excludes a symlinked local bin
ok - operator PATH honors nvm defaults with a deterministic fallback
ok - operator PATH resolves the authorized Nix profile bin link
ok - the worker preserves bounded argv and stdin in an empty environment
ok - active jobs keep the worker ready for concurrent requests
ok - ensure replaces a live worker after its code changes
ok - worker identity binds the canonical configured code root
ok - stale ownership is reclaimed without signaling a reused pid
ok - a dead worker's abandoned lock scratch file does not strand the lock
ok - lock reclamation clears only this protocol's own scratch files
ok - the worker enforces the job timeout and publishes its result
ok - the worker expires queued jobs before they can mutate
ok - queued jobs receive a fresh bounded execution window
ok - a queued short command preempts a running long poll instead of waiting its window
ok - a poll re-armed after preemption reads the same cursor with nothing lost
ok - sibling polls never preempt each other into a re-arm churn loop
ok - worker shutdown terminates the active command tree before replacement
ok - Linux supervision recovers crashes and stops orphaned commands
ok - pre-execution validation obeys the job timeout
ok - the worker drains bounded output without changing command results
ok - the worker refuses symlinked job fields before command execution
ok - failed shutdown quarantines ownership against replacement workers
ok - quarantine clears only after recorded execution has stopped
ALL TESTS PASSED
FM_TEST_END 2026-08-23T22:29:00Z tests/fm-remote-job.test.sh exit=0 duration_ms=46032 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:29:00Z tests/fm-remote-reply.test.sh family=secondmate expected_gate_skip=none
ok - a blocking non-destructive remote delta reaches durable process-event capture
ok - a captured delta is applied, acknowledged, and re-armed without a handler
ok - ingest appends one validated line, fetches its document, and advances the cursor
ok - replayed capture has one deduplicated append and one durable handling identity
ok - later generations cannot invalidate an unacknowledged ingested result
ok - the remote status and decision model mirrors and the cursor advances
ok - a remote mate's new decision folds open exactly as a local mate's does
ok - a replayed mirrored delta is idempotent in both the stream and the cursor
ok - transported control bytes are normalized in place and never stop the stream
ok - payload protocol-field names cannot collide with transport metadata
ok - NUL bytes are normalized in place before shell line processing
ok - local document storage failures remain retryable until delivery succeeds
ok - a mirrored reserved-key line cannot squat or clear the parent's own decision
ok - a reply that arrives after escalation resolves it and clears the open decision
ok - a cursor-loss whole-log recapture is acknowledged quietly with no duplicate wake
ok - truncation is detected, escalated once, and not silently rebased
ok - remote reply retirement quiesces and refuses unhandled captured results
ALL TESTS PASSED
FM_TEST_END 2026-08-23T22:29:17Z tests/fm-remote-reply.test.sh exit=0 duration_ms=17743 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:29:17Z tests/fm-remote-secondmate-lifecycle-e2e.test.sh family=secondmate expected_gate_skip=none
ok - overlapping remote home provisioning serializes through publication and rollback
ok - remote seed rollback preserves serialized competing routes
ok - unknown readiness preserves its route and brief for reconciliation
ok - remote seeding checks, repairs, and re-checks readiness, then stops on a remaining gap
ok - remote seeding proceeds once the repair closes every gap
ok - remote seeding provisions a supplied origin without touching the primary project tree
ok - remote provisioning re-validates a supplied origin at the receiving host
ok - seeding carries bitbucket, self-hosted, and scp-like origins through to the remote clone
ok - remote seed registers the route and provisions the whole home and project clone on that host
ok - remote inheritance rejects incomplete and superseded payload generations
ok - mixed local and remote routes validate without migration
ok - remote spawn launches on the remote-local backend and records a host-qualified route
ok - legacy and mismatched remote endpoints fail closed before backend access
ok - non-herdr remote endpoints are refused without changing either route
ok - remote spawn serializes inheritance through launch publication
ok - marked send and routed reply complete through the existing parent correlation owner
PR_CHECK_MIGRATION: watcher ownership is ambiguous; review state/.watch.lock before rearming polls
ok - partial remote inheritance retains reread intent through bootstrap convergence
ok - config push and bootstrap serialize remote inheritance convergence
ok - remote inherited config retains and retries a failed live reread nudge
ok - fleet snapshot projects mixed local and remote structured state
ok - remote update imports and fast-forwards the persistent home on its configured host
PR_CHECK_MIGRATION: watcher ownership is ambiguous; review state/.watch.lock before rearming polls
ok - startup repairs remote readiness before probing without relaunching
PR_CHECK_MIGRATION: watcher ownership is ambiguous; review state/.watch.lock before rearming polls
ok - startup reports alive legacy backends without changing their routes
ok - unreachable remote state remains unknown with no local respawn or failover
ok - remote retirement refuses child work, then removes only its own endpoint while a shared-session sibling survives
ALL TESTS PASSED
FM_TEST_END 2026-08-23T22:31:09Z tests/fm-remote-secondmate-lifecycle-e2e.test.sh exit=0 duration_ms=111307 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:31:09Z tests/fm-remote-secondmate-parent-binding.test.sh family=unclassified expected_gate_skip=none
ok - remote provisioning publishes durable parent state before its completion marker
ok - a remote secondmate's finished worker cleans up when the remote code root's own .env looked relay-active
ok - a remote secondmate's finished worker cleans up when only an ambient exported token looked relay-active
ok - a remote secondmate's finished worker cleans up with no relay signal anywhere
ok - a remote secondmate's own committed relay token still refuses cleanup
ALL TESTS PASSED
FM_TEST_END 2026-08-23T22:31:16Z tests/fm-remote-secondmate-parent-binding.test.sh exit=0 duration_ms=7527 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:31:16Z tests/fm-remote-secondmate-trace-context.test.sh family=secondmate expected_gate_skip=none
ok - disabled: a remote-routed second mate records and receives no carrier and stays enabled-off end to end
ok - enabled: a remote-routed second mate receives one carrier in its pane, identical to the parent's recorded identity, before launch
ok - relaunch: a remote-routed second mate keeps one stable identity across restarts
ok - boundary: each remote-routed second mate roots its own trace and never adopts the spawning environment's carrier
ok - allowlist: the remote receiver accepts exactly the declared inherited-material set, including the enablement flag
ok - delivery: a parent-supplied carrier is accepted only for a secondmate launch and only as a strict W3C value
ALL TESTS PASSED
FM_TEST_END 2026-08-23T22:31:39Z tests/fm-remote-secondmate-trace-context.test.sh exit=0 duration_ms=22747 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:31:39Z tests/fm-secondmate-harness.test.sh family=secondmate expected_gate_skip=none
ok - A1 fm-harness.sh secondmate resolves the fallback chain; crew mode unchanged
ok - fm-harness detects only Cursor Agent CLI's exact invocation marker
ok - C1 fm-harness.sh secondmate-model/secondmate-effort resolve the optional tokens; bare harness stays empty (backward-compat)
ok - pi-signed identity: authoritative launch selection distinguishes shared wrapper ancestry
ok - harness identity: dash-leading ps command names are basename operands, not options
ok - B1 propagate_inheritable_config: copy, idempotence, convergence, absence-mirror, exclusion, no-op, skip diagnostics
ok - B2 spawn: secondmate runs the secondmate harness; its home inherits declared config
ok - B3 spawn: an absent secondmate-harness falls back to the crew harness (backward-compat)
ok - B4 spawn: no config at all -> own harness and no propagation side effects
ok - B5 spawn: an explicit per-spawn harness arg overrides config/secondmate-harness
ok - B6 spawn: an unverified resolved secondmate harness is refused (guard intact)
skip: cursor executable not resolvable in this environment, so the launch could not be built
ok - B5b spawn: FM_BACKEND wins over inherited config/backend
ok - B5c spawn: explicit --backend wins over FM_BACKEND and inherited config/backend
ok - C2 spawn: a bare harness-only secondmate-harness file launches with no model/effort flag (backward-compat)
ok - C3 spawn: config/secondmate-harness's model token threads --model into the launch and meta
ok - C4 spawn: config/secondmate-harness's model+effort tokens thread into the launch and meta
ok - C5 spawn: an explicit --model overrides config/secondmate-harness's model token; the file's effort token still applies
ok - C6 spawn: an explicit --effort overrides config/secondmate-harness's effort token; the file's model token still applies
ok - C7 spawn: an explicit --harness starts with clean model/effort defaults
ok - C8 spawn: an explicit --harness still honors explicit model/effort flags
ok - C9 spawn: secondmate launch pins supervision to its own harness
ok - C9 spawn: the harness fallback chain still resolves with no tokens; crew/scout launches are unaffected by this feature
ok - B7 bootstrap sweep pushes, re-converges, and mirrors absence; never inherits secondmate-harness
ok - B8 bootstrap sweep propagates config even when the home's tracked files are already current
ok - B9 bootstrap sweep defers new inherited config until the home ignores it
ok - B10 bootstrap sweep materializes and inherits the startup-memory default while fast-forwarding
ok - B12b backend inheritance: present values and primary absence converge exactly
ok - B12c presentation inheritance: the primary default converges on, and only an explicit opt-out propagates off
ok - B11 bootstrap sweep surfaces config propagation failures
ok - B11 bootstrap rereads completed config writes after partial propagation
ok - B12 config-push propagates via shared live discovery, reports items, rereads on change only, and does not fast-forward
ok - B13 config-push reports dirty, non-allowing, and invalid homes without failing warnings-only runs
ok - B14 config-push exits nonzero on real propagation errors
ok - B14 config-push rereads completed config writes after partial propagation
ok - B15 config reread is per-home, exact-byte, ordered, and pointer-only
ok - B16 config reread isolation, ABSENT, generation safety, send failure, and retry
ok - B20 config reread publication failures retain exact generations for retry
ok - B21 config reread instruction-write failures retain exact retry generations
ok - B21 config reread preserves exact bytes when temporary adoption also fails
ok - B21 config reread serializes concurrent propagation and delivery
ok - B22 full config reread retry queues drain before new publication
ok - B23 mixed config reread delivery failures still bound sent history
ok - B26 config reread delivery stops after the oldest failed generation
ok - B17 config reread skips unchanged homes and reads destination post-write bytes
ok - B18 bootstrap config reread path works; spawn flexibility remains defaults-only
ok - B19 bootstrap respawns before inherited-config reread
ok - B25 spawn quarantines stale rereads without blocking relaunch
ok - B24 bootstrap detect-only mode remains filesystem read-only
# all fm-secondmate-harness tests passed
FM_TEST_END 2026-08-23T22:33:12Z tests/fm-secondmate-harness.test.sh exit=0 duration_ms=92941 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:33:12Z tests/fm-secondmate-lifecycle-e2e.test.sh family=secondmate expected_gate_skip=none
ok - seed: registry scope+projects, charter copied, clones+origins, no-mistakes init in subhome only
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WORKTREE TANGLE - PRIMARY CHECKOUT IS ON A FEATURE BRANCH
●  /home/jk/.treehouse/firstmate-b4b40d/2/firstmate is on 'fm/fm-briefs-ignore-devpod-rebase', not its default branch 'main'.
●  A crewmate likely branched/committed in the primary instead of its own worktree.
●  The work is SAFE on the 'fm/fm-briefs-ignore-devpod-rebase' ref.
●  Restore the primary to 'main':
●      git -C /home/jk/.treehouse/firstmate-b4b40d/2/firstmate checkout main
●  then re-validate 'fm/fm-briefs-ignore-devpod-rebase' in a proper isolated worktree.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
warning: secondmate design sync skipped before launch: diverged from ec9e9546a4c3ba6601198380ea9295ef1ceab0f6
ok - spawn: launches in the subhome with persistent charter, records routing meta
ok - send: a bare fm-<id> secondmate routes to the meta window with the from-firstmate marker
ok - handoff: in-scope items move verbatim, out-of-scope stays, idempotent
ok - recovery: respawns from the durable registry and persistent home
ok - teardown: removes the home, then clears meta and the registry route
FM_TEST_END 2026-08-23T22:33:17Z tests/fm-secondmate-lifecycle-e2e.test.sh exit=0 duration_ms=4573 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:33:17Z tests/fm-secondmate-liveness.test.sh family=secondmate expected_gate_skip=none
ok - fm_backend_tmux_agent_state: separates live, dead, missing, ambiguous, and unreadable
ok - fm_backend_tmux_agent_state: rejects malformed targets before probing tmux
ok - fm_backend_herdr_agent_state: preserves missing/no-agent/live/unknown husk behavior
ok - fm_backend_agent_state: routes tmux/Herdr and keeps Zellij unverified
ok - sweep: a confirmed-dead secondmate endpoint is killed and respawned
ok - sweep: an already-live secondmate is untouched and distinguishable in verbose diagnostics
ok - sweep: an authoritatively missing Pi secondmate window is relaunched
ok - sweep: an authoritatively missing pi-signed secondmate window is relaunched
ok - sweep: an existing ambiguous Pi process prevents duplicate recovery
ok - sweep: transient target unreadability never licenses recovery
ok - sweep: failed relaunch diagnostics distinguish a confidently missing endpoint
ok - sweep: an unverified harness blocks recovery with a concrete diagnostic
ok - sweep: idempotent by construction - a live secondmate is never re-touched on a later run
ok - sweep: skipped entirely under FM_BOOTSTRAP_DETECT_ONLY=1, exactly like the other mutating sweeps
ok - sweep: a silent no-op with no kind=secondmate meta present (a secondmate home's own natural scoping)
# all fm-secondmate-liveness tests passed
FM_TEST_END 2026-08-23T22:33:28Z tests/fm-secondmate-liveness.test.sh exit=0 duration_ms=11245 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:33:28Z tests/fm-secondmate-safety.test.sh family=secondmate expected_gate_skip=none
ok - FM_HOME parameterizes data and state paths
ok - fm-lock status is scoped per home
ok - seed allows overlapping project clone lists and drops the owns/owner routing
ok - home-seed validation rejects registry records no operational parser can consume
ok - home seeding refuses broken registry symlinks before provisioning
ok - home seeding refuses unreadable registries before provisioning
ok - home seed validation rejects duplicate home routes
ok - home seed validation rejects duplicate id routes
ok - home seed validation rejects nested home routes
leased worktree for dash
ok - home seeding durably leases treehouse-acquired dash homes under the secondmate id
ok - home seeding returns rejected acquired homes through treehouse
ok - home seed rollback warns when treehouse-acquired return fails
ok - home seeding leaves unsafe acquired active homes untouched
ok - home seeding rolls back failed clone attempts without residue
ok - home seeding refuses direct seed without filled charter text
ok - home seeding refuses unfilled placeholder charters
ok - home seeding refuses empty normalized charter fields
ok - home seeding scaffolds, registers, and spawns a project-less home end to end
ok - secondmate spawn resolves home validation and projects from punctuated registry fields
ok - secondmate spawn refuses ambiguous, supplied-home, and metadata-home registry bindings
ok - home seeding validates reused project-less charters before mutation
ok - home seeding refuses project-less conversion of a populated home
ok - home seeding refuses project-less homes whose projects directory cannot be inspected
ok - home seeding refuses project-less homes with symlinked projects directories
ok - home seeding refuses project-less homes with non-directory projects paths
ok - home seeding refuses project-less homes whose project registry cannot be inspected
ok - home seeding fails loudly on accidental project omission and rejects mixed --no-projects
ok - home seeding refuses local-only projects
ok - home seeding refuses registry delimiter home paths
ok - home seeding refuses active home and repo root
ok - home seeding refuses homes marked for another id
ok - home seeding refuses homes registered to another id
ok - home seeding refuses same-id reassignment to a different home
ok - home seeding refuses registered home overlaps
ok - remote-backed subhome seeding requires a source origin
ok - remote-backed subhome seeding validates existing destination origins
ok - home seeding resolves relative source origins against the source project
ok - home seeding skips initialized existing no-mistakes clones
ok - home seeding refuses uninitialized existing no-mistakes clones
ok - home seeding refuses project destinations outside the subhome
ok - home seeding refuses operational directories outside the subhome
ok - home seeding refuses symlinked and non-regular leaf files
ok - home reseeding preserves and enforces the durable parent binding
ok - secondmate spawn validates homes before launch
ok - secondmate spawn refuses operational directories outside the subhome
ok - fm-send refuses a bare firstmate window with no metadata in this home
ok - secondmate teardown retires empty homes and releases routing
ok - secondmate teardown refuses ambiguous and identity-mismatched registry bindings
ok - normal secondmate teardown sweeps process events before removal
ok - secondmate teardown preserves state when process-event sweeping is unavailable
ok - later teardown refusals preserve active process-event sources
ok - force teardown sweeps nested secondmate homes before deletion
ok - force teardown preserves nested process-event restoration status and recovery state
ok - secondmate teardown refuses to hide failed leased-home return
ok - secondmate teardown raw-removes plain-clone homes
ok - secondmate force teardown discards child work
ok - secondmate force teardown prevalidates child quarantine cleanup without following symlinks
ok - secondmate force teardown preserves child worktree after unproven lock refusal
ok - force teardown allows non-state operational directory symlinks inside the subhome
ok - force teardown refuses operational directory symlinks outside the subhome
ok - secondmate teardown refuses homes containing registered nested homes
ok - secondmate teardown refuses nested homes from the child registry
ok - force teardown validates subhome before child cleanup
ok - forced teardown refuses a non-directory descendant state path
ok - forced teardown refuses a symlinked descendant state path
ok - forced teardown locks a descendant whose state directory was absent
ok - forced teardown refuses while a fresh task is being published in the home
ok - a fresh spawn refuses to publish while a forced teardown owns the task set
ok - a fresh remote secondmate spawn refuses while the task set is owned
ok - force teardown refuses child worktrees inside the active home
ok - force teardown refuses child worktrees inside the firstmate repo
ok - force teardown refuses unregistered child worktree paths
ok - secondmate teardown path-boundary matrix refuses unmarked/ancestor/active-descendant/repo-descendant homes
ok - idle kind=secondmate pane is healthy and not stale
ok - secondmate charter brief is idle by default and does not self-initiate work
ok - fm-backlog-handoff aborts atomically on unmatched, in-flight, and unregistered targets
ok - fm-backlog-handoff refuses Done items under whitespace section headings and unsafe homes
FM_TEST_END 2026-08-23T22:33:52Z tests/fm-secondmate-safety.test.sh exit=0 duration_ms=23648 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:33:52Z tests/fm-secondmate-sync.test.sh family=secondmate expected_gate_skip=none
ok - T1 updated: a behind home fast-forwards to the primary's local HEAD

@jk1rby

jk1rby commented Aug 23, 2026

Copy link
Copy Markdown

bin/fm-ci.sh full output — part 5/6

ok - T2 current: an already-current home is a no-op and reports no instruction change
ok - T3 dirty: an uncommitted home is skipped, its edit preserved
ok - T4 diverged: a home that is not an ancestor of the primary's HEAD is skipped
ok - T5 in-flight: a home on a feature branch is skipped, its work preserved
ok - T6 no fetch: the local-HEAD sync never invokes git fetch
ok - T7 sweep nudges on a real instruction change only, but still fast-forwards
ok - T8 bootstrap sweeps live homes and sends exactly one marked nudge for the instruction change
ok - T8a bootstrap nudge send respects FM_STATE_OVERRIDE
ok - T8f bootstrap nudge retry rejects malformed marker ids
ok - T8c failed bootstrap nudge is surfaced and recorded for retry
ok - T8d bootstrap nudge retry is idempotent after success
ok - T8e bootstrap nudge retry refuses a changed home instead of guessing
ok - T8b stale herdr nudge failures leave a retry marker after respawn rotates fm-<id> metadata
ok - T9 bootstrap surfaces a skipped dirty live secondmate home
ok - T10 spawn fast-forwards a secondmate worktree to the primary's local HEAD before launch
ok - T11 spawn warns when pre-launch sync is skipped
ok - T12 gitignored marker: a freshly seeded home reads clean to fleet-sync and the ff sweep
ok - T13 gitignored marker: an existing marker-only-dirty home converges, then reads clean
ok - T14 marker tolerance does not mask a genuinely dirty home
# all fm-secondmate-sync tests passed
FM_TEST_END 2026-08-23T22:34:02Z tests/fm-secondmate-sync.test.sh exit=0 duration_ms=10491 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:34:02Z tests/fm-send-remote-delivery.test.sh family=unclassified expected_gate_skip=none
ok - fm-send remote: delivered-unconfirmed reports delivered, exits 0, keeps the expectation armed
ok - fm-send remote: a real remote failure still fails loudly with the remote diagnostics
ok - fm-send remote: ssh 255 still refuses loudly and preserves the expectation as delivery_unknown
ok - fm-send remote: a delivered-unconfirmed answer closes its --resolve-key decision
ok - fm-send local: an unconfirmed submit exits 3 with an honest non-error report
ok - fm-send local: an unconfirmed submit still never closes a --resolve-key decision
all fm-send-remote-delivery tests passed
FM_TEST_END 2026-08-23T22:34:03Z tests/fm-send-remote-delivery.test.sh exit=0 duration_ms=1351 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:34:03Z tests/fm-send-resolve-key.test.sh family=backend-dispatch expected_gate_skip=none
ok - fm-send --resolve-key: the answer send itself closes the open decision
ok - fm-send --resolve-key: the close never re-wakes its own home, later lines still do
ok - fm-send --resolve-key: a colon-first stated key is open under that key and answerable
ok - fm-send --resolve-key: an answer that starts a workstream leaves no orphaned decision
ok - fm-send: a send without --resolve-key never closes a decision, and working/done still cannot
ok - fm-send --resolve-key: a key that is not open refuses loudly before anything is sent
ok - fm-send --resolve-key: a failed send never closes the decision
ok - fm-send --resolve-key: one answer closes each named key and only those
ok - fm-send --resolve-key: a marked local-secondmate answer closes with the plain answer text
ok - fm-send --resolve-key: a remote-secondmate answer closes the same local ledger, transport-only difference
ok - fm-send --resolve-key: a remote reply's leading [corr=...] tag no longer blocks closing its stated key
ok - fm-send --resolve-key: a failed remote transport never closes the decision
ok - fm-send --resolve-key: --key, empty message, explicit targets, and malformed keys refuse loudly
FM_TEST_END 2026-08-23T22:34:08Z tests/fm-send-resolve-key.test.sh exit=0 duration_ms=4279 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:34:08Z tests/fm-send-secondmate-marker-herdr-e2e.test.sh family=live-harness-optin expected_gate_skip=optin-env
skip: set FM_SEND_MARKER_HERDR_E2E=1 to run the real Pi/Herdr secondmate-marker regression
FM_TEST_END 2026-08-23T22:34:08Z tests/fm-send-secondmate-marker-herdr-e2e.test.sh exit=0 duration_ms=131 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:34:08Z tests/fm-send-secondmate-marker.test.sh family=secondmate expected_gate_skip=none
ok - fm-send: a kind=secondmate target gets the from-firstmate marker and corr prepended
ok - fm-send: an exact kind=secondmate task id is marked with corr exactly once
ok - fm-send: exact-id and stable-label kind=ship selectors are sent unmarked
ok - fm-send: explicit endpoints stay unmarked with or without local metadata
ok - fm-send: the --key path carries no marker (no literal text is typed)
ok - fm-send: the marker is '[fm-from-firstmate]' + terminal-safe U+2063, while direct captain text stays unmarked
ok - fm-marker: from-firstmate transformation is idempotent
ok - fm-send: marked secondmate payload preserves trailing newline bytes
FM_TEST_END 2026-08-23T22:34:09Z tests/fm-send-secondmate-marker.test.sh exit=0 duration_ms=1383 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:34:09Z tests/fm-session-lock-ancestry.test.sh family=watcher-wake-lock expected_gate_skip=none
ok - session-lock: a version-named Claude Code session is identified from its install path and argv[0]
ok - session-lock: ordinary script paths under a harness directory are not harness processes
ok - session-lock: the Windows bridge re-seeds past a dangling fork-stub parent and reaches the session
ok - session-lock: re-seeding the Windows bridge never invents a harness that is not in the ancestry
ok - session-lock: ownership stops at the first non-harness gap above the contiguous run
ok - session-lock: CLAUDE_PID confirms self across a ps-ancestry gap without weakening the gap's existing refusal
ok - session-lock: a matching harness session id confirms self across a ps-ancestry gap the walk and CLAUDE_PID both cannot cross
ok - session-lock: a live version-named session holding the lock is not mistaken for a stale owner
ok - session-lock: a genuinely running harness process still holds its lock
ok - session-lock: a holder suspended past the dwell no longer holds a usable lock
ok - session-lock: resuming a suspended holder restarts the dwell clock
ok - session-lock: a suspension sighting cannot outlive the suspension it describes
ok - session-lock: an exited-but-unreaped harness never holds a usable lock
ok - session-lock: D, running, and unreadable states keep their lock; only T and t are swept
ok - session-lock e2e: a version-named session claims the home and arms supervision
ok - session-lock e2e: a session parented by a harness-named daemon claims the home and arms supervision
ok - session-lock e2e: a version-named session under a harness-named daemon keeps its own lock
ok - session-lock e2e: a lock refusal names the holder's state, the watcher beat, and the way out
ok - session-lock e2e: a matching harness session id reclaims the lock across a background-job process boundary, and is refused without one
ok - session-lock e2e: an unrelated harness session id is still refused, and the refusal names the undrained wake count
FM_TEST_END 2026-08-23T22:36:20Z tests/fm-session-lock-ancestry.test.sh exit=0 duration_ms=130433 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:36:20Z tests/fm-session-start.test.sh family=session-bootstrap expected_gate_skip=none
ok - context digest distinguishes ABSENT, empty-but-present, and populated files
ok - a lock refusal prints a loud read-only banner, skips every mutating step, and still completes the digest
ok - session start stays read-only when lock ownership cannot be published
ok - locked session start freezes trace context and lock refusal leaves it unchanged
ok - concurrent session-lock acquisition admits exactly one live harness
ok - digest sections are ordered safety-preamble first, live fleet state before curated memory
ok - the read-once contract is stated once, ahead of the sources it governs
ok - session start: configured and auto-detected Herdr homes never require tmux
ok - session start: an absent recorded tmux window relaunches its Pi secondmate exactly once, off the blocking path
ok - session start: a deferred relaunch is always reported, so the digest's stale endpoint record cannot stand
ok - session start: an unreachable host delays a reported check, not the digest
ok - session start: a deferred result the digest outran still reaches the agent as a wake
ok - session start: a read-only session declares its skipped network checks rather than dropping them
ok - session start: the tasks-axi compatibility verdict is computed once and reused
ok - session start: an existing ambiguous Pi process prevents duplicate recovery
ok - session start: transient tmux unreadability never licenses a relaunch
ok - session start: the proven bare-shell recovery path remains intact
ok - session start: a confirmed Herdr husk is closed and relaunched
ok - status tail is bounded to the configured line count, with the full log path always printed
ok - status tail lines are capped with a truncation marker while the full log stays reachable
ok - orphan status logs are printed once with bounded tails
ok - tmux endpoint liveness is reported per task: alive for a live window, dead for a gone one
ok - herdr endpoint liveness is reported per task: alive for a live pane, dead for a gone one
ok - fm-session-start.sh composes the real fm-lock.sh, fm-bootstrap.sh, and fm-wake-drain.sh output verbatim
ok - compatible tasks-axi backlog rendering drops done rows and keeps every in-flight, held, and blocked row
ok - the startup backlog bound cuts only dispatchable queued rows and discloses the remainder exactly
ok - manual backlog rendering drops done rows, keeps every held or blocked title line, and bounds the rest
ok - unavailable or incompatible tasks-axi falls back to compact manual backlog rendering
ok - an empty fleet reports (none) for in-flight tasks and an absent AFK flag
ok - session start emits X-mode cadence guidance in the harness supervision block
ok - next step delegates watcher ownership to the AFK daemon
ok - session start emits exactly one detected harness block and reports Pi extension load state
ok - session start preserves pi-signed primary identity while applying Pi extension guarantees
ok - session start rejects stale Pi loaded markers
ok - session start accepts current Pi markers written before lock acquisition
ok - session start rejects Pi sessions missing the turn-end guard marker
ok - session start rejects Pi loaded markers from previous sessions
ok - the pure-Bash watchdog bounds session start, kills its hung grandchild, and emits the truncation contract
ok - the portable timeout path force-kills a command that ignores TERM
ok - a session start inside its budget prints no truncation banner
ok - the runtime bound leaves enough ancestry headroom for a deeply nested session to take the lock
ok - --reemit reprints the digest without repeating startup's mutating sweeps and still drains queued wakes
ok - true-start AGENTS baselines stay immutable while every drifted Pi compact re-emits the current contract
ok - read-only Pi compact refreshes against the rebuilding session identity without mutation
ok - Codex reset sources do not claim an unavailable instruction-refresh channel
ok - instruction baselines require SHA-256 and successful startup completion
ok - --reemit re-verifies lock ownership and keeps repair ownership with whoever holds it
# fm-session-start.test.sh: all assertions passed
FM_TEST_END 2026-08-23T22:38:06Z tests/fm-session-start.test.sh exit=0 duration_ms=105924 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:38:06Z tests/fm-sessionstart-hook-live-e2e.test.sh family=live-harness-optin expected_gate_skip=optin-env
skip: set FM_SESSIONSTART_HOOK_LIVE_E2E=1 to run the live session-open hook regression
FM_TEST_END 2026-08-23T22:38:06Z tests/fm-sessionstart-hook-live-e2e.test.sh exit=0 duration_ms=8 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:38:06Z tests/fm-sessionstart-instruction-refresh-live-e2e.test.sh family=live-harness-optin expected_gate_skip=optin-env
skip: set FM_SESSIONSTART_INSTRUCTION_REFRESH_LIVE_E2E=1 to run the isolated real-Pi instruction-refresh regression
FM_TEST_END 2026-08-23T22:38:06Z tests/fm-sessionstart-instruction-refresh-live-e2e.test.sh exit=0 duration_ms=8 gate_skip=true
FM_TEST_BEGIN 2026-08-23T22:38:06Z tests/fm-sessionstart-nudge.test.sh family=session-bootstrap expected_gate_skip=none
ok - fm-sessionstart-nudge: a genuine primary gets one explicitly marked instruction line
ok - fm-sessionstart-nudge: NO_MISTAKES_GATE is silent
ok - fm-sessionstart-nudge: .no-mistakes gate common-dir is silent
ok - fm-sessionstart-nudge: an unmarked linked task worktree is silent
ok - fm-sessionstart-nudge: a marked linked secondmate home is a primary
ok - fm-sessionstart-nudge: a checkout without state is silent
ok - fm-sessionstart-nudge: a lock holder in process ancestry is already run
ok - OpenCode session.created delivers the exact wrapper nudge once per session
ok - run wrapper: startup runs the full digest and never also nudges
ok - run wrapper: clear and compact re-emit the digest without repeating startup sweeps
ok - run wrapper forwards only stale-cache rebuild sources to immutable-baseline instruction refresh
ok - run wrapper refreshes a compact even when startup completion is unproven
ok - run wrapper: clear falls back to full startup when completion is unproven
ok - run wrapper: clear accepts completion only from the current harness
ok - run wrapper: resume delegates to the nudge instead of re-running the digest
ok - run wrapper: the hook payload's source field drives routing with no explicit argument
ok - run wrapper: an unrecognized or absent source takes the helm rather than skipping it
ok - run wrapper: a gate agent and an unmarked task worktree never run a session start
ok - run wrapper: a session start that cannot take the lock still opens the session and says so
ok - Pi distinguishes header-proven restored CLI sessions from named create-if-missing startups
ok - Pi retains a bounded digest prefix and loudly marks oversized delivery
FM_TEST_END 2026-08-23T22:38:18Z tests/fm-sessionstart-nudge.test.sh exit=0 duration_ms=12385 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:38:18Z tests/fm-shared-captain-inheritance.test.sh family=secondmate expected_gate_skip=none
ok - shared captain first copy converges, is read-only, and preserves local captain/learnings files
ok - shared captain drift is quarantined collision-safely and repeated convergence is idempotent
ok - missing primary shared file mirrors absence only after quarantining a local copy
ok - unsafe shared captain artifacts are rejected and failure restores read-only mode
ok - spawn convergence point propagates data/captain-shared.md from FM_DATA_OVERRIDE
ok - bootstrap convergence point propagates data/captain-shared.md from FM_DATA_OVERRIDE
ok - fm-config-push convergence point updates changed shared captain source bytes from FM_DATA_OVERRIDE
ok - session-start digest renders data/captain-shared.md with the shared read-only label
# all fm-shared-captain-inheritance tests passed
FM_TEST_END 2026-08-23T22:38:22Z tests/fm-shared-captain-inheritance.test.sh exit=0 duration_ms=3463 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:38:22Z tests/fm-spawn-dispatch-profile.test.sh family=backend-dispatch expected_gate_skip=none
ok - no --model/--effort records defaults and types the claude launch instructions
ok - non-cursor launches clear inherited Cursor identity markers
ok - relative home overrides ignore CDPATH and become absolute before spawn launch construction
ok - FM_HOME defaults resolve relative paths and preserve absolute spellings
ok - absolute override spellings are preserved in spawn launch paths
ok - unresolvable relative spawn overrides fail with named diagnostics
ok - active crew-dispatch profile requires an explicit harness for ship spawns
ok - active crew-dispatch profile requires an explicit harness for scout spawns
ok - active crew-dispatch profile allows an explicit resolved harness
ok - active crew-dispatch profile allows the legacy positional harness form
ok - active crew-dispatch profile allows the raw launch-command escape hatch
ok - claude receives --model and --effort profile flags
ok - codex receives --model and model_reasoning_effort profile flags
ok - codex omits unsupported max effort instead of passing a bad config value
ok - grok receives --model and --reasoning-effort profile flags
ok - grok omits unsupported max reasoning effort
ok - grok omits unsupported xhigh reasoning effort
ok - cursor receives its model-qualified reasoning class and exact task workspace
ok - cursor refuses model ids absent from its resolved binary's live catalog
ok - cursor preserves the requested model when its live catalog is unreachable
ok - opencode receives --model and omits the unsupported effort axis
ok - pi receives --model and --thinking max profile flags
ok - Pi launch probing omits --tui-mode on older Pi and preserves it on supporting Pi
ok - pi-signed shares Pi launch semantics while preserving its configured and recorded identity
ok - pi-signed refuses safely and actionably when the selected executable is unavailable
ok - pi-signed is a distinct persistent secondmate runtime with shared Pi supervision semantics
ok - batch dispatch forwards shared --harness, --model, and --effort to every pair
ok - claude forwards firstmate's CLAUDE_CONFIG_DIR so the crewmate uses the same credential store
ok - claude omits the config-dir prefix when firstmate runs with the single-store default
ok - non-claude harnesses do not receive the claude CLAUDE_CONFIG_DIR prefix
ok - active crew-dispatch profile does not block secondmate launches
# all fm-spawn-dispatch-profile tests passed
FM_TEST_END 2026-08-23T22:39:16Z tests/fm-spawn-dispatch-profile.test.sh exit=0 duration_ms=54293 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:39:16Z tests/fm-spawn-git-identity.test.sh family=unclassified expected_gate_skip=none
ok - a pool worktree with no resolvable git identity anywhere refuses to launch
Author identity unknown

*** Please tell me who you are.

Run

  git config --global user.email "you@example.com"
  git config --global user.name "Your Name"

to set your account's default identity.
Omit --global to set the identity only in this repository.

fatal: unable to auto-detect email address (got 'jk@jk-omarchy.(none)')
ok - firstmate's fallback identity is exported before launch and lands in GIT_AUTHOR_IDENT
ok - a worktree that already resolves a correct git identity is left unchanged
# all fm-spawn-git-identity tests passed
FM_TEST_END 2026-08-23T22:39:21Z tests/fm-spawn-git-identity.test.sh exit=0 duration_ms=4839 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:39:21Z tests/fm-spawn-pool-base-freshen.test.sh family=unclassified expected_gate_skip=none
ok - a stale pooled worktree refreshes to current origin/main before a crew branch is created
ok - a stale pooled worktree resolves and refreshes a non-main default branch
ok - direct-PR ships and scouts both refresh stale pooled worktrees before launch
ok - a dirty pooled worktree is refused without discarding its local work
ok - an unresolved remote default branch refuses the pooled worktree
ok - an unreachable origin refuses a potentially stale pooled worktree
# all fm-spawn-pool-base-freshen tests passed
FM_TEST_END 2026-08-23T22:39:33Z tests/fm-spawn-pool-base-freshen.test.sh exit=0 duration_ms=12544 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:39:33Z tests/fm-spawn-worktree-settle.test.sh family=backend-dispatch expected_gate_skip=none
ok - a single transient stale pane_current_path read is not accepted as the worktree
ok - an already-settled pane confirms via the existing inter-poll sleep, not an extra full cycle
# all fm-spawn-worktree-settle tests passed
FM_TEST_END 2026-08-23T22:39:38Z tests/fm-spawn-worktree-settle.test.sh exit=0 duration_ms=4689 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:39:38Z tests/fm-startup-memory-budget.test.sh family=secondmate expected_gate_skip=none
ok - primary bootstrap materializes only the visible default and preserves valid captain choices
ok - budget parser accepts one exact positive value and rejects malformed or unsafe inputs
ok - budget accounting sums the three startup files and reports safe failures
ok - budget propagation converges through config push with exact rereads, absence, and safe rejection
# all fm-startup-memory-budget tests passed
FM_TEST_END 2026-08-23T22:39:42Z tests/fm-startup-memory-budget.test.sh exit=0 duration_ms=3896 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:39:42Z tests/fm-startup-network.test.sh family=session-bootstrap expected_gate_skip=none
ok - fm-startup-network: wait fails when no deferred stage publishes before its deadline
ok - fm-startup-network: start returns immediately and never holds the caller's stdout open
ok - fm-startup-network: exactly one of the digest and the wake reports each result
ok - fm-startup-network: a claimant crash after publication still surfaces the result
ok - fm-startup-network: a report-publication failure is failed, diagnosed, and still wakes
ok - fm-startup-network: manual callers cannot forge mutation authority
ok - fm-startup-network: an aggregate bound turns a wedged sweep into an actionable line
ok - fm-startup-network: an abandoned run reports as needing a rerun, never as in progress forever
ok - fm-startup-network: a second start never launches a competing worker
ok - fm-startup-network: start atomically reserves the generation harvest observes
ok - fm-startup-network: a new lock owner gets a distinct worker generation
ok - fm-startup-network: fleet-lock takeover cannot overlap a mutating sweep
ok - fm-startup-network: timing records share one origin so their offsets form a timeline
ok - fm-startup-network: timings are durable and printed only on demand
ok - fm-startup-network: a timed-out run still publishes the partial timings it recorded
ok - fm-startup-network: the timing artifact cannot carry a command line or forge records
# fm-startup-network.test.sh: all assertions passed
FM_TEST_END 2026-08-23T22:40:27Z tests/fm-startup-network.test.sh exit=0 duration_ms=44599 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:40:27Z tests/fm-stow-cascade.test.sh family=secondmate expected_gate_skip=none
ok - each home is accounted against its own allowance instead of a fleet total
ok - the cascade emits one stanza per registered home and refuses a double-counted registry
ok - transport follows placement and live-agent state, and a remote home without an agent defers
ok - each stanza carries the facts a per-home receipt needs, before and after curation
ok - one slow or unreachable home is bounded and every other home still reports
ok - the cascade stays silent with no secondmates and never runs from a secondmate home
FM_TEST_END 2026-08-23T22:40:29Z tests/fm-stow-cascade.test.sh exit=0 duration_ms=2718 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:40:29Z tests/fm-subagent-pretool-check.test.sh family=pure-contract-unit expected_gate_skip=none
ok - the guard independently denies every work-creating delegation tool by shape
ok - the guard denies delegation-shaped tools that no deny list knows about yet
ok - the guard leaves ordinary tools and observe-or-stop operations alone
ok - the guard leaves the session-local todo list alone
ok - the plan-only exclusion releases exactly two names and nothing that merely contains them
ok - MCP tool names are never classified as harness delegation
ok - deny defers to intake classification and degrades gracefully without fm-scout.sh
ok - the single documented escape hatch releases the guard only on the exact opt-in value
ok - the guard is inert in a crewmate task worktree and in a non-firstmate repo
ok - a marked secondmate home is guarded even though it is a linked worktree
ok - both stdin transports classify correctly and Claude's deny keeps stdout empty
ok - malformed, empty, and tool-name-less payloads fail open rather than blocking every tool call
ok - missing jq for stdin transport fails open rather than denying every tool call
FM_TEST_END 2026-08-23T22:40:30Z tests/fm-subagent-pretool-check.test.sh exit=0 duration_ms=695 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:40:30Z tests/fm-supervision-events.test.sh family=watcher-wake-lock expected_gate_skip=none
ok - handle_push_transition: a blocked crew enqueues a stale wake naming its window and wakes the supervisor
ok - handle_push_transition: enqueue failure cannot commit the Herdr dedupe marker
ok - handle_push_transition: a declared-pause crew is absorbed (no fast wake), left to the poll loop's long cadence
ok - event_wait_or_sleep: herdr windows go on the event pane list, but kind=secondmate endpoints are excluded
ok - event_wait_or_sleep: one cached capability probe owns validation across bounded waits
ok - event_wait_or_sleep: a home with no push-capable window is inert (sleeps POLL, never touches the event path)
ok - event_wait_or_sleep: consecutive event-path failures disable the fast-path and revert to pure polling (fail-closed)
# fm-supervision-events.test.sh: all assertions passed
FM_TEST_END 2026-08-23T22:40:30Z tests/fm-supervision-events.test.sh exit=0 duration_ms=374 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:40:30Z tests/fm-tangle-guard.test.sh family=session-bootstrap expected_gate_skip=none
ok - fm_primary_tangle_branch: feature branch alarms; default/detached/non-git stay silent
ok - fm-guard: bordered tangle banner fires only for a feature branch and suppresses repair commands in read-only mode
ok - fm-bootstrap: TANGLE problem line fires only for a feature branch and suppresses repair commands in detect-only mode
ok - fm-brief: ship brief asserts worktree isolation before the branch step
ok - fm-spawn: aborts unless the resolved worktree is a genuine, isolated worktree
ok - fm-spawn: appends windows by session-colon, pins the name, and targets the window id
FM_TEST_END 2026-08-23T22:40:39Z tests/fm-tangle-guard.test.sh exit=0 duration_ms=8871 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:40:39Z tests/fm-task-delivery.test.sh family=pure-contract-unit expected_gate_skip=none
ok - fm-spawn: a ship spawn requires a valid explicit mode and yolo before anything is created
ok - fm-spawn: scout and secondmate spawns refuse ship delivery flags
ok - fm-spawn: the brief's recorded mode and the spawn's explicit mode must agree
ok - fm-spawn: a rigor downgrade against the registered posture is announced, never blocked
ok - fm-spawn: a scout spawn resolves no delivery posture from the registry
ok - fm-promote: promotion requires the delivery contract and records it exactly once
ok - fm-project-mode: the conditional policy is accepted, mapped for mechanical callers, and readable raw
ok - fm-spawn: real fm-brief.sh output stays bound to the spawn delivery check
# all fm-task-delivery tests passed
FM_TEST_END 2026-08-23T22:40:41Z tests/fm-task-delivery.test.sh exit=0 duration_ms=1897 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:40:41Z tests/fm-teardown-endpoint-safety.test.sh family=backend-dispatch expected_gate_skip=none
ok - fm-teardown: missing, empty, malformed, ambiguous, and task-mismatched endpoints refuse before every mutation or runtime call
ok - fm-teardown: a concurrent lifecycle action refuses before mutation
ok - fm-teardown: destructive cleanup serializes with metadata writers
ok - cleanup identity: valid tmux, Herdr, Zellij, Orca, and cmux records validate while every empty backend target refuses
ok - tmux backend: direct empty target returns nonzero without invoking tmux
ok - process cleanup: creation-time PID identity removes only the exact child and preserves the control child
ok - fm-teardown: exact tmux cleanup preserves invalid and prefix-matched neighbors while removing only the recorded target
FM_TEST_END 2026-08-23T22:40:44Z tests/fm-teardown-endpoint-safety.test.sh exit=0 duration_ms=3072 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:40:44Z tests/fm-teardown.test.sh family=pr-forge expected_gate_skip=none
ok - local-only worktree with HEAD on a fork remote is torn down (fix holds)
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WORKTREE TANGLE - PRIMARY CHECKOUT IS ON A FEATURE BRANCH
●  /home/jk/.treehouse/firstmate-b4b40d/2/firstmate is on 'fm/fm-briefs-ignore-devpod-rebase', not its default branch 'main'.
●  A crewmate likely branched/committed in the primary instead of its own worktree.
●  The work is SAFE on the 'fm/fm-briefs-ignore-devpod-rebase' ref.
●  Restore the primary to 'main':
●      git -C /home/jk/.treehouse/firstmate-b4b40d/2/firstmate checkout main
●  then re-validate 'fm/fm-briefs-ignore-devpod-rebase' in a proper isolated worktree.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
warning: lsof is unavailable; cannot resolve the tmux pane process group for task-x1
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WORKTREE TANGLE - PRIMARY CHECKOUT IS ON A FEATURE BRANCH
●  /home/jk/.treehouse/firstmate-b4b40d/2/firstmate is on 'fm/fm-briefs-ignore-devpod-rebase', not its default branch 'main'.
●  A crewmate likely branched/committed in the primary instead of its own worktree.
●  The work is SAFE on the 'fm/fm-briefs-ignore-devpod-rebase' ref.
●  Restore the primary to 'main':
●      git -C /home/jk/.treehouse/firstmate-b4b40d/2/firstmate checkout main
●  then re-validate 'fm/fm-briefs-ignore-devpod-rebase' in a proper isolated worktree.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ok - teardown prompts tasks-axi backlog refresh when compatible
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WORKTREE TANGLE - PRIMARY CHECKOUT IS ON A FEATURE BRANCH
●  /home/jk/.treehouse/firstmate-b4b40d/2/firstmate is on 'fm/fm-briefs-ignore-devpod-rebase', not its default branch 'main'.
●  A crewmate likely branched/committed in the primary instead of its own worktree.
●  The work is SAFE on the 'fm/fm-briefs-ignore-devpod-rebase' ref.
●  Restore the primary to 'main':
●      git -C /home/jk/.treehouse/firstmate-b4b40d/2/firstmate checkout main
●  then re-validate 'fm/fm-briefs-ignore-devpod-rebase' in a proper isolated worktree.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
warning: lsof is unavailable; cannot resolve the tmux pane process group for task-x1
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WORKTREE TANGLE - PRIMARY CHECKOUT IS ON A FEATURE BRANCH
●  /home/jk/.treehouse/firstmate-b4b40d/2/firstmate is on 'fm/fm-briefs-ignore-devpod-rebase', not its default branch 'main'.
●  A crewmate likely branched/committed in the primary instead of its own worktree.
●  The work is SAFE on the 'fm/fm-briefs-ignore-devpod-rebase' ref.
●  Restore the primary to 'main':
●      git -C /home/jk/.treehouse/firstmate-b4b40d/2/firstmate checkout main
●  then re-validate 'fm/fm-briefs-ignore-devpod-rebase' in a proper isolated worktree.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ok - teardown honors config/backlog-backend=manual even when tasks-axi is compatible
ok - local-only worktree with truly unpushed work is refused (safety preserved)
ok - local-only worktree with work merged into local main is torn down (no regression)
ok - no-mistakes worktree with HEAD on origin is torn down (no regression)
ok - no-mistakes worktree with genuinely unlanded work is refused (safety preserved)
ok - local-only worktree with unpushed work is torn down under --force (escape hatch)
ok - teardown completes when an exact busy-state sidecar is already absent
ok - herdr teardown removes pane-owned escalation dedupe state
ok - herdr flat teardown refuses before returning the isolated copy under lock contention and the retry completes cleanly
ok - herdr flat teardown never erases records when pane presence is unparseable
ok - herdr flat teardown preflight refuses before every destructive change
ok - forced secondmate teardown preflights every Herdr child before cleanup mutation
ok - forced secondmate teardown holds every descendant lifecycle and metadata lock
ok - forced secondmate teardown retains Herdr child identity until exact pane disappearance
ok - forced teardown retains a nested secondmate home and its grandchild's Herdr identity when the grandchild close is unconfirmed
ok - herdr projection teardown retires its journal only after confirming the exact recorded pane is gone
ok - herdr projection teardown retains every record when post-close presence is unknown
ok - herdr projection teardown surfaces failed focus restoration without turning confirmed cleanup into a hard failure
ok - squash-merged + deleted-branch worktree (PR merged) is torn down (the fix)
ok - squash-merged PR accepts a local HEAD that is an ancestor of the final PR head
ok - teardown discovers a merged PR by branch name and tears down when no pr= was ever recorded
ok - squash-merged PR accepts replayed unpushed local patches contained in the PR head
ok - merged PR does not allow teardown after a later local commit
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WORKTREE TANGLE - PRIMARY CHECKOUT IS ON A FEATURE BRANCH
●  /home/jk/.treehouse/firstmate-b4b40d/2/firstmate is on 'fm/fm-briefs-ignore-devpod-rebase', not its default branch 'main'.
●  A crewmate likely branched/committed in the primary instead of its own worktree.
●  The work is SAFE on the 'fm/fm-briefs-ignore-devpod-rebase' ref.
●  Restore the primary to 'main':
●      git -C /home/jk/.treehouse/firstmate-b4b40d/2/firstmate checkout main
●  then re-validate 'fm/fm-briefs-ignore-devpod-rebase' in a proper isolated worktree.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WORKTREE TANGLE - PRIMARY CHECKOUT IS ON A FEATURE BRANCH
●  /home/jk/.treehouse/firstmate-b4b40d/2/firstmate is on 'fm/fm-briefs-ignore-devpod-rebase', not its default branch 'main'.
●  A crewmate likely branched/committed in the primary instead of its own worktree.
●  The work is SAFE on the 'fm/fm-briefs-ignore-devpod-rebase' ref.
●  Restore the primary to 'main':
●      git -C /home/jk/.treehouse/firstmate-b4b40d/2/firstmate checkout main
●  then re-validate 'fm/fm-briefs-ignore-devpod-rebase' in a proper isolated worktree.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ok - fm-pr-check does not refresh PR head after HEAD moves
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WORKTREE TANGLE - PRIMARY CHECKOUT IS ON A FEATURE BRANCH
●  /home/jk/.treehouse/firstmate-b4b40d/2/firstmate is on 'fm/fm-briefs-ignore-devpod-rebase', not its default branch 'main'.
●  A crewmate likely branched/committed in the primary instead of its own worktree.
●  The work is SAFE on the 'fm/fm-briefs-ignore-devpod-rebase' ref.
●  Restore the primary to 'main':
●      git -C /home/jk/.treehouse/firstmate-b4b40d/2/firstmate checkout main
●  then re-validate 'fm/fm-briefs-ignore-devpod-rebase' in a proper isolated worktree.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ok - fm-pr-check records the remote PR head when the local worktree lags
ok - worktree whose content already landed in the default branch is torn down (content fallback)
ok - content fallback refreshes origin default before comparing trees
ok - dirty worktree is refused even when its committed work has landed (dirty always wins)
ok - gh lookup error with content not in default refuses (fail-safe)
ok - provably-stale worktree index.lock (old, no live holder) is cleared and teardown succeeds
ok - live-held worktree index.lock is never removed and teardown refuses
ok - lsof errors leave worktree index.lock in place and refuse teardown
ok - stale lock cleanup rechecks and refuses dirty worktree before return
ok - normal repo index.lock is resolved from the worktree and cleared when stale
ok - lock mtime read failures leave worktree index.lock in place and refuse teardown
ok - transient index.lock cleared after first failed return is retried successfully without force-remove
ok - persistent index.lock exhausts retries and refuses without force-removing the lock
ok - empty retry wait overrides use the default without aborting teardown
ok - fractional legacy retry wait remains supported without arithmetic
ok - a task's own parked no-mistakes run is aborted, not orphaned, before the worker is removed
ok - teardown refuses before reap or removal when a task-owned run remains parked
ok - a different run cannot confirm the targeted abort
ok - empty post-abort status is not accepted as confirmation
ok - the CLI's exact run-not-found signal confirms completion
ok - a parked run on another branch is never aborted by this task's teardown (ownership is precise)
ok - a task-owned autonomous running step is left alone rather than aborted
not ok - leaked-process-reap: leaked worktree process survived teardown
FM_TEST_END 2026-08-23T22:41:14Z tests/fm-teardown.test.sh exit=1 duration_ms=29827 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:41:14Z tests/fm-test-fixture-cleanup.test.sh family=unclassified expected_gate_skip=none
ok - fm_test_tmproot cleans up its fixture root on normal exit
ok - fm_test_tmproot cleans up its fixture root on SIGTERM
ok - the cleanup registry cannot be injected through path precreation
ok - failed fixture registration rolls back the new root
ok - the orphan sweep reaps only old fixtures without a live owner
FM_TEST_END 2026-08-23T22:41:15Z tests/fm-test-fixture-cleanup.test.sh exit=0 duration_ms=793 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:41:15Z tests/fm-test-isolation-proof.test.sh family=pure-contract-unit expected_gate_skip=none
ok - candidate --list is non-empty, sorted, unique, and real
ok - serial classes remain excluded from the parallel candidate set
ok - audited fake-backend and stub-network extras are candidates
ok - exclusion list documents serial reasons
ok - isolation-proof contract test is family-mapped
ok - parallel shards consume the proven-isolated set only
FM_TEST_END 2026-08-23T22:41:15Z tests/fm-test-isolation-proof.test.sh exit=0 duration_ms=387 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:41:15Z tests/fm-tmux-agent-liveness.test.sh family=backend-dispatch expected_gate_skip=none
ok - tmux liveness: a harness-named foreground process classifies alive
ok - tmux liveness: muse's version-suffixed muse-bin-<version> classifies alive
ok - tmux liveness: unrelated muse-containing command names stay ambiguous
ok - tmux liveness: a version-named executable under a harness install path classifies alive
ok - tmux liveness: a version-named executable under a decoy path stays ambiguous
ok - tmux liveness: a process neither name source attributes stays ambiguous rather than inventing an agent
ok - tmux liveness: a launcher whose own identity reads as a bare shell classifies alive from its harness child
ok - tmux liveness: an idle shell pane classifies dead
ok - tmux liveness: a harness-named background process in an idle pane still classifies dead
ok - tmux liveness: an absent window classifies missing rather than inheriting tmux's active-window fallback
ok - cursor composer: an idle Cursor pane reads empty even though the cursor row is blind
ok - cursor composer: real typed text still reads pending, so the injection guard holds
ok - cursor composer: an identical screen stays unknown when the pane is not Cursor
ok - cursor composer: a stale Cursor screen over a dead shell never reads empty
FM_TEST_END 2026-08-23T22:41:17Z tests/fm-tmux-agent-liveness.test.sh exit=0 duration_ms=1709 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:41:17Z tests/fm-trace-context-lib.test.sh family=pure-contract-unit expected_gate_skip=none
ok - fm_trace_context_valid accepts a conformant W3C traceparent
ok - fm_trace_context_valid rejects all-zero ids, ff version, uppercase, wrong length, missing field, shell metacharacters, and empty
ok - a command-substitution-shaped value is rejected as inert data, never executed
ok - fm_trace_context_hex yields exact-length lowercase hex, distinct per call
ok - fm_trace_context_mint starts a valid sampled root trace
ok - every mint is an unrelated fresh root - one trace per task, no parent adoption
ok - a minted root is the fixed 55-char W3C form (hex and hyphens only), so firstmate originates no free-form content in the carrier
ok - enablement is default-off; FM_TRACE_CONTEXT overrides with truthy/other precedence, and unset or empty defers to config/trace-context
ok - session start normalizes config and environment precedence into frozen on/off state
ok - session state is atomically published through a same-directory replacement
ok - a stale on record is inactive when publication fails in a new locked session
ok - missing or invalid frozen session state defaults off
ok - resolve omits the carrier and returns success when the capability is off (byte-identical default)
ok - resolve mints a valid traceparent when enabled
ok - Secondmate home-session state stays off or on despite later file state; ambient TRACEPARENT is never adopted, so each routed task roots its own trace
ok - resolve reuses a valid recorded traceparent verbatim on relaunch (stable identity across restarts)
ok - disabling the capability omits the carrier even for a task with a recorded identity
ok - a corrupt recorded traceparent is re-minted rather than propagated
ok - resolve yields exactly one carrier per logical task, so the recorded and injected values are identical by construction
ok - entropy failure omits telemetry safely: mint reports failure, resolve returns success with no carrier
ok - the resolver has no sleep/timeout/command hang source and always returns success
ok - the carrier is minted identically for every harness, backend, and spawn kind (no such branching in the lib code)
ok - the lib code never reads a brief, prompt, report, or status - it cannot leak content
ok - config/trace-context is inherited into secondmate homes, keeping the nested chain enabled end to end
# fm-trace-context-lib.test.sh: all assertions passed
FM_TEST_END 2026-08-23T22:41:17Z tests/fm-trace-context-lib.test.sh exit=0 duration_ms=212 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:41:17Z tests/fm-trace-context-spawn.test.sh family=backend-dispatch expected_gate_skip=none
ok - enabled: one resolved carrier is recorded in meta and the identical TRACEPARENT is exported before launch
ok - disabled: neither traceparent= in meta nor a TRACEPARENT export is produced
ok - failed TRACEPARENT delivery omits metadata while the source task still launches
ok - uncleared TRACEPARENT input stops before the launch command is appended
ok - failed traceparent metadata append removes the carrier from the launched task
ok - duplicate secondmate preflight leaves trace-context unchanged
ok - relaunch reuses the recorded carrier verbatim for both the meta record and the injected export
ok - session start freezes the env override and later config or environment edits do not alter spawns
ok - two-level: env-on/file-absent keeps the nested worker enabled, rooting its own per-task trace
ok - two-level: env-off/file-present keeps the nested worker disabled even though the config file was copied into the secondmate home
ok - two unrelated routed tasks through one persistent Secondmate root distinct traces, adopt nothing from its environment, and keep per-task identity across relaunch
ok - secondmate carrier and FM_TRACE_CONTEXT snapshot always agree, both derived from one frozen decision (file-decided path)
# all fm-trace-context-spawn tests passed
FM_TEST_END 2026-08-23T22:41:49Z tests/fm-trace-context-spawn.test.sh exit=0 duration_ms=31744 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:41:49Z tests/fm-turnend-guard.test.sh family=watcher-wake-lock expected_gate_skip=none
ok - fm_supervision_unhealthy: false with no state/*.meta at all
ok - fm_supervision_unhealthy: true with in-flight task and no beacon ever
ok - fm_supervision_unhealthy: true with in-flight task and a beacon far outside the grace window
ok - fm_supervision_unhealthy: false with in-flight task and a fresh beacon
ok - fm_supervision_status: FM_SUP_QUEUE_PENDING tracks state/.wake-queue
ok - fm_supervision_needed: X-mode relay poll needs supervision
ok - fm_supervision_unhealthy: source-only home needs supervision
ok - fm_supervision_status: a finished, idle task is not counted as in flight
ok - fm_supervision_status: a crew running a turn behind a finished status line is still in flight
ok - fm_supervision_status: unreadable, ambiguous, and unresolved tasks all stay in flight
ok - fm_supervision_status: an armed poll keeps supervision on without faking in-flight work
ok - fm-turnend-guard: silent no-op with nothing in flight
ok - fm-turnend-guard: blocks when a fresh beacon has no live watcher lock, worded as a cycle that ended
ok - fm-turnend-guard: blocks when a stale beacon has no live watcher lock, worded as prolonged silence
ok - fm-turnend-guard: non-Claude path blocks a source-only home
ok - fm-turnend-guard: blocks on a dead watcher lock even when the beacon is fresh
ok - fm-turnend-guard: silent no-op with a live watcher lock and fresh beacon
ok - fm-turnend-guard: healthy non-Claude harness paths ignore Claude episode contention
ok - fm-turnend-guard: blocks on a live watcher lock with an ancient beacon
ok - fm-turnend-guard: blocks with the exact required reason in the primary when unhealthy
ok - fm-turnend-guard: silent for finished work and for an armed poll, loud again the moment it resumes
ok - fm-turnend-guard: blocks from active FM_HOME state, not only repo-root state
ok - fm-turnend-guard: X-mode repair reason sources the cadence config
ok - fm-turnend-guard: X-mode-only supervision remains guarded in default mode
ok - fm-turnend-guard: ignores stale repo-root state when FM_HOME is set
ok - fm-turnend-guard: uses FM_STATE_OVERRIDE ahead of FM_HOME/state
ok - fm-turnend-guard: stop_hook_active=true always allows the stop (never blocks twice in one turn)
ok - fm-turnend-guard: blocks a blind turn end in a secondmate's own home (.fm-secondmate-home no longer excludes it)
ok - fm-turnend-guard: idle-by-default - silent in a secondmate home with nothing in flight
ok - fm-turnend-guard: stop_hook_active=true allows the stop in a secondmate home (never blocks twice in one turn)
ok - fm-turnend-guard: secondmate deferred-death recovery - silent while watched, forces re-arm once the watcher exits
ok - fm-turnend-guard: inert in a secondmate's own child worktree (linked git worktree) even when unhealthy
ok - fm-turnend-guard: blocks a blind turn end in a treehouse-leased LINKED secondmate home (marker force-include)
ok - fm-turnend-guard: an invalid (empty) marker cannot spoof inclusion; linked worktree stays exempt
ok - fm-turnend-guard: a non-ASCII marker cannot spoof inclusion; linked worktree stays exempt
ok - fm-turnend-guard: inert in a crewmate/scout task worktree (linked git worktree) even when unhealthy
ok - fm-turnend-guard: fails open (never blocks) when jq is missing
ok - fm-turnend-guard: silent no-op on empty stdin
ok - fm-turnend-guard: runs well under the generous timing margin (0s)
ok - fm-turnend-guard-grok: forces one explicitly marked same-session resume when the shared predicate blocks
ok - fm-turnend-guard-grok: legacy environment loop guard prevents a nested resume loop
ok - fm-turnend-guard-grok: native false delegates blocking feedback with zero resume processes
ok - fm-turnend-guard-grok: native true remains bounded and starts no resume process
ok - fm-turnend-guard-grok: both spellings are typed and camelCase has deterministic precedence
ok - fm-turnend-guard-grok: malformed, invalidly typed, and missing-prerequisite payloads start neither path
ok - fm-turnend-guard-grok: missing jq and no-supervision-needed stops stay silent and bounded
ok - tracked .claude/settings.json entries: 5 inert under grok, the documented subagent exception still armed, all live under Claude
ok - .codex/hooks.json: Stop hook uses hook process root when payload cwd is outside
ok - .codex/hooks.json: Stop hook ignores nested git root guard scripts
ok - .opencode primary plugin: guard path is anchored to worktree, not directory
ok - .pi primary extension: no-tool and multi-tool runs each inject exactly one guard follow-up
ok - .pi primary extension: delivery failure resets the logical-run latch
ok - fm-turnend-guard --claude: re-blocks a loop-guarded stop while unhealthy and unclaimed (incident regression)
ok - fm-turnend-guard --claude: X-mode-only homes re-block when auto-arm recovery is absent
ok - fm-turnend-guard --claude: a live arming epoch advances once and repeated observation is idempotent
ok - fm-turnend-guard --claude: a suspended auto-arm owner stops owning recovery only after its dwell, and regains it on resume
ok - fm-turnend-guard --claude: repeated failed-to-arming races make bounded monotonic progress
ok - fm-turnend-guard --claude: terminal owner boundary excludes a concurrent start without deadlock
ok - fm-turnend-guard --claude: fresh rewake epoch prevents a duplicate continuation for the same event
ok - fm-turnend-guard --claude: fresh failed epochs preserve and advance monotonic fail-open progression
ok - fm-turnend-guard --claude: integrated fresh failures reach one bounded fail-open, stop continuation, and reset on recovery
ok - fm-turnend-guard --claude: reset contention preserves all episode state until retry
ok - fm-turnend-guard --claude: concurrent auto-arm and guard resets are idempotent and deadlock-free
ok - fm-turnend-guard --claude: stale rewake epoch does not allow a blind stop
ok - fm-turnend-guard --claude: budget exhaustion alone cannot permit a blind stop
ok - fm-turnend-guard --claude: verified fail-open is loud, bounded, attended, and non-repeating
ok - fm-turnend-guard --claude: an identity-decline marker alone reaches the bounded attended fail-open
ok - fm-turnend-guard --claude: an identity-decline marker surfaces in the repair banner before the budget is exhausted
ok - fm-turnend-guard --claude: fail-open requires both exhausted retries and consumed notice
ok - fm-turnend-guard --claude: away ownership excludes the Stop-autoarm fail-open
ok - fm-turnend-guard --claude: positive watcher recovery resets failure episode state
ok - fm-turnend-guard --claude: bounded claim wait avoids a token-consuming forced continuation
ok - fm-turnend-guard --claude: secondmate home re-blocks unclaimed and allows auto-arm-claimed stops
FM_TEST_END 2026-08-23T22:42:04Z tests/fm-turnend-guard.test.sh exit=0 duration_ms=14802 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:42:04Z tests/fm-update.test.sh family=session-bootstrap expected_gate_skip=none
ok - T1 main + secondmate fast-forward (single-parent), reread + nudge signalled
ok - T3 reread gates on instruction surface, nudge on advancement
ok - T4 dirty secondmate skipped, local edit preserved
ok - T5 diverged secondmate skipped, local commit preserved
ok - T6 idempotent: a second run is a no-op
ok - T7 registry backstop resolves, dedups meta+registry, excludes the firstmate repo
ok - T9 firstmate off its default branch is skipped, not forced
ok - T10 firstmate detached HEAD is skipped
ok - T11 unsafe secondmate home is not fast-forwarded
# all fm-update tests passed
FM_TEST_END 2026-08-23T22:42:05Z tests/fm-update.test.sh exit=0 duration_ms=1595 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:42:05Z tests/fm-upstream-sync.test.sh family=unclassified expected_gate_skip=none
ok - reports current when the fork already has every upstream commit
ok - dry run reports a fast-forward and writes nothing
ok - --push fast-forwards the fork's default branch to upstream
ok - diverged fork gets a sync branch and its default branch is left untouched
ok - reports the conflicting paths without touching anything
ok - creates the upstream remote when the clone lacks one
ok - repoints an upstream remote that points somewhere else
ok - resolves the default branch from origin refs when origin/HEAD is unset
ok - an unreachable upstream fails loudly instead of reporting a false result
ok - --help prints usage and exits 0
FM_TEST_END 2026-08-23T22:42:06Z tests/fm-upstream-sync.test.sh exit=0 duration_ms=886 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:42:06Z tests/fm-vendor-auth-probe.test.sh family=pure-contract-unit expected_gate_skip=none
ok - the probe accepts no harness, model, or provider and so can hold no routing mapping
ok - the probe never reads quota, leaving one intake snapshot to the dispatch owner
ok - every probe result exits alike because the script renders no verdict
ok - only a registered probe name runs, and an unregistered one is a usage error
ok - an authenticated vendor session is reported as ground truth
ok - an unauthenticated vendor session is reported as ground truth
ok - unrecognized, blank-led, and silent probe output is indeterminate, never authenticated
ok - an absent vendor CLI is reported rather than assumed authenticated
ok - a hanging vendor CLI is hard-bounded, reported, and cannot wedge an intake
ok - zero and all-zero bounds fall back to the default instead of removing the hard bound
ok - a malformed bound is replaced by the default rather than forwarded
ok - the bounded probe runs with stdin closed and cannot read caller input
ok - the vendor CLI is invoked only through its two fixed, non-destructive argv forms
ok - the fact line is one sanitized line with no raw vendor output or credential material
ok - a vendor CLI version change is recorded and disclosed for re-verification
ok - the pinned verified vendor version is recognized
ok - --help succeeds and names the registered probes
FM_TEST_END 2026-08-23T22:42:49Z tests/fm-vendor-auth-probe.test.sh exit=0 duration_ms=42856 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:42:49Z tests/fm-wake-daemon-lifecycle-e2e.test.sh family=watcher-wake-lock expected_gate_skip=none
ok - lifecycle: routine self-handles, terminal survives a watcher restart, buffers once, no dup, injects once
ok - lifecycle: stale pane transient self-handles, persistent escalates once and clears, resumed clears quietly
FM_TEST_END 2026-08-23T22:42:54Z tests/fm-wake-daemon-lifecycle-e2e.test.sh exit=0 duration_ms=4719 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:42:54Z tests/fm-wake-drain-open-decisions-cursor.test.sh family=unclassified expected_gate_skip=none
ok - a truncated/rewritten log falls back to a full re-fold instead of dropping or misreading the decision
ok - a same-size file rotation (new inode) is detected and falls back to a full re-fold
ok - a failed presentation read preserves status state for retry
ok - a cursor-cache read failure refolds decisions without replaying handled unread status
ok - a pre-fix cursor is rebuilt so a previously skipped corr-tagged decision surfaces
ok - an old fold cache is rebuilt once before same-version incremental reads resume

@jk1rby

jk1rby commented Aug 23, 2026

Copy link
Copy Markdown

bin/fm-ci.sh full output — part 6/6

ok - a buried decision survives many growing drains with bounded read cost, and resolution durably clears it at bounded cost too
FM_TEST_END 2026-08-23T22:43:04Z tests/fm-wake-drain-open-decisions-cursor.test.sh exit=0 duration_ms=9883 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:43:04Z tests/fm-wake-drain-open-decisions.test.sh family=unclassified expected_gate_skip=none
ok - a needs-decision buried under later routine/other-key lines still reports as open
ok - an over-long open decision is cut to its per-item budget with the shared truncation marker
ok - an explicit resolved [key=X] closes the keyed decision
ok - a later unrelated terminal line never clears an open decision
ok - a reserved decision key can only be opened or closed by its owning library
ok - no open decisions across the fleet prints nothing
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 1755915.1787524985.esjh21
ok - the open-decision section is fleet-wide, not scoped to this drain's own queued records
ok - a buried open decision surfaces even when the wake queue itself is empty
ok - the fleet-wide decision scan does not follow status symlinks
FM_TEST_END 2026-08-23T22:43:06Z tests/fm-wake-drain-open-decisions.test.sh exit=0 duration_ms=2099 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:43:06Z tests/fm-wake-drain-unread-status.test.sh family=watcher-wake-lock expected_gate_skip=none
ok - a note: answer buried under a later routine note: is surfaced with both lines
ok - already-presented note: lines are not re-surfaced on the next drain
ok - a brand-new note: after presentation is surfaced without replaying handled lines
ok - a queued status signal annotates every unread note, not only the newest
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 1760764.1787524988.iPROzo
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 1760764.1787524988.iPROzo
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 1760764.1787524988.iPROzo
ok - a pending-reply resolution buried under a later note surfaces once and closes OPEN DECISIONS
ok - unread status over the former byte cap preserves every line
ok - presentation cursor advances only through its captured endpoint
ok - a reused task id starts its replacement status log unread at byte zero
ok - OPEN DECISIONS still folds needs-decision/blocked independently of unread notes
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 1765061.1787524990.yYNbPJ
ok - an empty-queue drain preserves routine status for a later signal annotation
ok - routine working/done lines still print nothing on an empty-queue drain
FM_TEST_END 2026-08-23T22:43:10Z tests/fm-wake-drain-unread-status.test.sh exit=0 duration_ms=4261 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:43:10Z tests/fm-wake-queue.test.sh family=watcher-wake-lock expected_gate_skip=none
ok - an abandoned same-process lock hold is reclaimed; a parent's live hold is not
ok - self-announced appends suppress only their own bytes and fail toward waking
ok - historical annotations replay nothing already announced and keep everything new
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 31 --recovery-generation 1769400.1787524991.PEFTll
ok - concurrent append plus drain preserves durable records through acknowledgement
ok - signal written while no watcher runs is caught on next run
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 1792037.1787524999.XhqUQw
WARNING: queued wakes pending - drain them with bin/fm-wake-drain.sh before anything else.
ok - stale wake is queued before suppressor state is advanced
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 1794584.1787525000.D43pxe
WARNING: queued wakes pending - drain them with bin/fm-wake-drain.sh before anything else.
ok - a not-provably-working stale wake is queued before its suppressor is advanced
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 1796802.1787525001.gQ1dF8
WARNING: queued wakes pending - drain them with bin/fm-wake-drain.sh before anything else.
ok - registered custom check output is queued before cadence suppression
ok - concurrent drains replay until one post-handling acknowledgement consumes records
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 4 --recovery-generation 1799328.1787525002.If9JQo
ok - drain collapses obvious duplicate heartbeat and signal records
ok - drain asserts watcher liveness: warns on a lapse, stays silent for a live watcher with a fresh beacon
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 8 --recovery-generation 1801007.1787525002.O7lI5r
ok - structural signal enrichment is separate, deduped, home-local, and tier-zero for other wakes
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 13 --recovery-generation 1802766.1787525003.cQsIRH
ok - every readable unread status line is annotated in full while invalid status files preserve their raw wakes
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 1806073.1787525003.evG05j
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 2 --recovery-generation 1806073.1787525003.evG05j
ok - slow annotation releases the append lock and a deleted status file fails open
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation existing
ok - wake append publishes atomic recovery evidence before durable rows
ok - wake drain: generation-less legacy wakes are adopted and acknowledged
ok - wake drain: a stale acknowledgement cannot retire or consume a newer recovery episode
ok - wake drain: recovery acknowledgement failures are explicit and retryable
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 2 --recovery-generation 1812300.1787525008.MXVSrE
ok - interruptions preserve durable rows until post-handling acknowledgement
FM_TEST_END 2026-08-23T22:43:34Z tests/fm-wake-queue.test.sh exit=0 duration_ms=23572 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:43:34Z tests/fm-watch-arm.test.sh family=watcher-wake-lock expected_gate_skip=none
ok - watch-arm: an attached arm reports the wake its cycle delivered instead of a false failure
ok - watch-arm: a delivered wake consumed by the handling turn still closes the attached arm cleanly
ok - watch-arm: a cycle that delivered no wake of its own still fails loudly
/home/jk/.treehouse/firstmate-b4b40d/2/firstmate/bin/fm-watch-arm.sh: line 566: 1881941 Killed                     "$WATCH" > "$child_out"
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 2 --recovery-generation 1882514.1787525041.AUYFbt
ok - watch-arm: re-arm surfaces every queued wake and an open remote decision after downtime
watcher: recovery state could not be persisted; retaining stale lock evidence
ok - watch-arm: marker publication failure retains stale-lock recovery evidence
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 2 --recovery-generation 1898095.1787525049.0N0ZMN
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 3 --recovery-generation 1899501.1787525050.uSBGhY
ok - watch-arm: a wake queued after handling drain is recovered once at successor arm
ok - watch-arm: interrupted handling leaves its wake durable for successor re-drain
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 0 --recovery-generation 1908141.1787525055.tDqoOK
ok - watch-arm: malformed recovery state is quarantined without a successor loop
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 1910953.1787525057.PnkLNR
ok - watch-arm: publication after recovery handoff is surfaced
ok - watch-arm: restart publishes recovery before clearing a reused-pid watcher lock
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 7 --recovery-generation 1913393.1787525059.sS6hJn
ok - watch-arm: markerless legacy queues are adopted and recovered
ok - watch-arm: a watcher close during handling keeps the printed acknowledgement valid
ok - watch-arm: a moved recovery generation consumes handled rows and names its remedy
ok - watch-arm: downtime marker publication does not follow symlinks
FM_TEST_END 2026-08-23T22:44:29Z tests/fm-watch-arm.test.sh exit=0 duration_ms=55460 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:44:29Z tests/fm-watch-checkpoint.test.sh family=watcher-wake-lock expected_gate_skip=none
ok - quiet checkpoint exits 124 with a clean checkpoint line and no live lock
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 2 --recovery-generation 1941347.1787525073.utbR4s
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WORKTREE TANGLE - PRIMARY CHECKOUT IS ON A FEATURE BRANCH
●  /home/jk/.treehouse/firstmate-b4b40d/2/firstmate is on 'fm/fm-briefs-ignore-devpod-rebase', not its default branch 'main'.
●  A crewmate likely branched/committed in the primary instead of its own worktree.
●  The work is SAFE on the 'fm/fm-briefs-ignore-devpod-rebase' ref.
●  Restore the primary to 'main':
●      git -C /home/jk/.treehouse/firstmate-b4b40d/2/firstmate checkout main
●  then re-validate 'fm/fm-briefs-ignore-devpod-rebase' in a proper isolated worktree.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ok - checkpoint passes through a real watcher wake and leaves the queue for drain
ok - checkpoint preserves watcher environment for registered custom checks
ok - checkpoint rejects an existing watcher singleton as unowned
FM_TEST_END 2026-08-23T22:44:34Z tests/fm-watch-checkpoint.test.sh exit=0 duration_ms=4254 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:44:34Z tests/fm-watch-triage.test.sh family=watcher-wake-lock expected_gate_skip=none
ok - signal_reason_is_actionable: benign absorbed, captain verbs and coalesced batches surfaced
ok - stale_is_terminal: terminal status surfaces, non-terminal and no-status are benign
ok - scan_captain_relevant_statuses lists only captain-relevant statuses
ok - classifier primitives: keyed decisions and activity phases, captain relevance, window-to-task, and overrides
ok - crew_is_provably_working: only working+run-step/pane is provable; idle/finished/parked/failed/unknown surface
ok - status_is_paused: only the leading paused verb matches, and paused is not captain-relevant
ok - crew_absorb_class: working/paused/none from one read; crew_is_paused and crew_is_provably_working agree
ok - signal_crew_provably_working: benign only when every referenced crew is provably working
ok - a secondmate's status signal is never absorbed as provably working; crewmates are unaffected
ok - a no-verb signal whose crew is provably working is absorbed (no exit, no queue, suppressor advanced, beacon present)
ok - a bare turn-end whose crew is provably working (busy pane) is absorbed
ok - a bare turn-end whose crew is not provably working is surfaced (the swallowed-finish fix)
ok - a no-verb working: note whose crew is idle with no running pipeline is surfaced
ok - a secondmate's status note surfaces even while its own agent is busy
ok - a self-announced close never wakes its own home, and the next real note still does
ok - captain-relevant signal is surfaced (queue + exit) and marked surfaced
ok - watcher-continuity: a status line appended during a fully unsupervised gap is preserved and surfaced by the next watcher cycle
ok - a stale pane sitting on a terminal status is surfaced (queue + exit)
ok - a stale terminal-looking status is overridden and absorbed while a run is actively working, then wedge-escalated
ok - provably-working non-terminal stale is absorbed on first sight, then wedge-escalated past the threshold
ok - consecutive wedge escalations on the same pane accumulate and demand deep inspection at the threshold
ok - a pane becoming active again resets the consecutive wedge-escalation counter
ok - a busy worker below the turn-age bound remains working with no escalation
ok - a busy worker with a stable pane hash still escalates once its completed-turn age reaches the bound
ok - a busy worker whose pane hash changes every poll still escalates once its completed-turn age reaches the bound
ok - touching a busy worker's completed-turn marker resets the age and prevents an old-age escalation
ok - repeated busy turn-age escalations reuse the existing escalation counter and demand deep inspection at the threshold
ok - the production default busy-turn-age bound is 3600s (5min under does not wedge, 66min over does)
ok - a not-provably-working non-terminal stale is surfaced immediately (never left to wait out the timer)
ok - a declared pause is absorbed on first sight, then re-surfaced as a recheck past the threshold, never wedge-escalated
ok - exited declared-pause and captain-held panes use bounded pause cadence while a live decision gate still surfaces once
ok - a declared paused secondmate re-surfaces on the bounded normal-mode cadence
ok - a non-paused secondmate retains normal stale suppression
ok - a resumed secondmate clears pause and stale tracking before stale exemption
ok - unchanged stale hashes reclassify when a crew enters or leaves pause
ok - a declared pause is periodically rechecked against authoritative active-run state
ok - a paused status overridden by authoritative working preserves its wedge timer and escalates
ok - matching non-terminal stale suppressors repair missing or corrupt stale-since timers
ok - triage log capping handles wc byte counts with leading spaces
ok - a captured process-event result wakes a healthy watcher proactively, with no manual drain
ok - an unacknowledged process-event result re-drains until handling is acknowledged
ok - complete process-event queue keys map to distinct seen markers
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 2136832.1787525188.Dn2GzE
ok - queue revalidation, proactive output, and marker commit serialize with drain
/home/jk/.treehouse/firstmate-b4b40d/2/firstmate/bin/fm-push-transition-lib.sh: line 96: echo: write error: Broken pipe
tests/wake-helpers.sh: line 281: 2141458 Killed                     PATH="$dir/fakebin:$PATH" FM_HOME="$dir" FM_PROCEVENT_CLAIM_ROOT="$dir/claims" FM_CREW_STATE_BIN="$dir/fakebin/fm-crew-state.sh" FM_POLL=0.2 FM_SIGNAL_GRACE=1 FM_CHECK_INTERVAL=999999 FM_HEARTBEAT=999999 "$WATCH" > "$out"
tests/wake-helpers.sh: line 281: 2142338 Killed                     PATH="$dir/fakebin:$PATH" FM_HOME="$dir" FM_PROCEVENT_CLAIM_ROOT="$dir/claims" FM_CREW_STATE_BIN="$dir/fakebin/fm-crew-state.sh" FM_POLL=0.2 FM_SIGNAL_GRACE=1 FM_CHECK_INTERVAL=999999 FM_HEARTBEAT=999999 "$WATCH" > "$out"
ok - surfacing failures replay until post-handling acknowledgement
ok - marker failure exits through the shared wake owner, releases its lock, and replays later
ok - a heartbeat with no captain-relevant change is absorbed and backs off the cadence
ok - heartbeat backstop fail-safe surfaces a captain-relevant status the per-wake path missed
ok - the liveness beacon stays fresh while the watcher absorbs benign wakes (fm-guard never false-alarms)
ok - with .afk present the watcher reverts to one-shot so the daemon owns triage (no double-triage)
ok - AFK changed paused panes hand off plain stale identities for daemon-owned pause triage
FM_TEST_END 2026-08-23T22:46:45Z tests/fm-watch-triage.test.sh exit=0 duration_ms=131357 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:46:45Z tests/fm-watcher-lock.test.sh family=watcher-wake-lock expected_gate_skip=none
ok - simultaneous watcher starts leave exactly one live process
ok - fm_pid_identity real ps fallback is locale-invariant
ok - fm_pid_identity is locale-invariant across LC_ALL/LC_TIME
ok - /proc process identity ignores simulated btime changes
ok - /proc process identity detects pid reuse
ok - MSYS /proc process identity regression skipped on non-Windows host
ok - killed watcher stale lock is reclaimed
tests/fm-watcher-lock.test.sh: line 1038: 2178270 Killed                     FM_STATE_OVERRIDE="$state" bash -c '
    . "$1"
    fm_lock_remove_path() {
      if [ "$1" = "$STATE/.watch.lock" ]; then
        kill -KILL "${BASHPID:-$$}"
      fi
      return 1
    }
    fm_lock_try_acquire "$2"
  ' _ "$LIB" "$lockdir" > /dev/null 2>&1
ok - stale watcher reclaim publishes durable recovery evidence before clear
ok - live watcher lock with stale heartbeat is actionable
ok - guard banner leads when down with pending wakes (repair-after-drain) and stays silent when live and fresh
ok - concurrent fm_lock_try_acquire yields exactly one winner
ok - dead-pid stale lock is reclaimed by a single acquirer
ok - concurrent stale-lock steal yields exactly one winner
ok - live steal mutex is not reclaimed
ok - live-held lock is not stolen
ok - empty mid-acquire lock keeps a minimum grace
ok - late original claimant cannot claim a recreated lock
ok - paused mid-acquire claimant backs off to active stealer
ok - watch restart preserves recovery without signaling a reused pid
ok - watch restart attaches to a verified healthy peer and later surfaces a successor gap
ok - watcher self-evicts when the lock pid no longer names it
ok - arm turns clean self-eviction without a successor into a typed failure
ok - arm attaches to a live fresh watcher and fails loudly when that cycle has no successor
ok - attached arm signals record a classified lifecycle entry
ok - arm starts cleanly and resurfaces recovery after a dead-pid lock
ok - arm cleans child watcher and temp output on HUP
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 2229131.1787525244.4GfPkK
WARNING: queued wakes pending - drain them with bin/fm-wake-drain.sh before anything else.
ok - arm propagates an immediate watcher wake before confirmation
ok - arm attaches to a peer watcher after child stands down and surfaces a missing successor
watcher: lock held by live pid 2231802 but heartbeat is stale for 840840447s (>300s); inspect or stop that watcher before re-arming.
ok - arm reports FAILED and exits non-zero when no fresh watcher can be confirmed
ok - cycle-exit ledger links a verified successor and remains size-capped
ok - SIGSTOP distinguishes live PID from stale beacon and termination records the exit class
FM_TEST_END 2026-08-23T22:48:11Z tests/fm-watcher-lock.test.sh exit=0 duration_ms=85893 gate_skip=false
FM_TEST_BEGIN 2026-08-23T22:48:11Z tests/fm-windows-path-portability.test.sh family=unclassified expected_gate_skip=none
ok - native_path windows conversion (skipped: no cygpath on this host)
ok - canonical_socket_path windows form (skipped: no cygpath on this host)
ok - identity proof agreement (skipped: no cygpath on this host)
ok - mixed spellings (skipped: no cygpath on this host)
ok - windows path portability
FM_TEST_END 2026-08-23T22:48:11Z tests/fm-windows-path-portability.test.sh exit=0 duration_ms=233 gate_skip=false
FM_TEST_SUMMARY total=119 failed=2 skipped_gate=20 duration_ms=1920648
FM_TEST_SUMMARY_FAMILY family=afk count=2 duration_ms=35435 failed=0
FM_TEST_SUMMARY_FAMILY family=backend-dispatch count=11 duration_ms=147415 failed=0
FM_TEST_SUMMARY_FAMILY family=cmux count=2 duration_ms=2245 failed=0
FM_TEST_SUMMARY_FAMILY family=live-harness-optin count=16 duration_ms=377 failed=0
FM_TEST_SUMMARY_FAMILY family=orca count=1 duration_ms=9786 failed=0
FM_TEST_SUMMARY_FAMILY family=pr-forge count=2 duration_ms=164328 failed=1
FM_TEST_SUMMARY_FAMILY family=pure-contract-unit count=15 duration_ms=110040 failed=0
FM_TEST_SUMMARY_FAMILY family=secondmate count=18 duration_ms=380237 failed=0
FM_TEST_SUMMARY_FAMILY family=session-bootstrap count=9 duration_ms=214177 failed=0
FM_TEST_SUMMARY_FAMILY family=snapshot-bearings count=2 duration_ms=45541 failed=0
FM_TEST_SUMMARY_FAMILY family=unclassified count=24 duration_ms=245889 failed=1
FM_TEST_SUMMARY_FAMILY family=watcher-wake-lock count=15 duration_ms=558869 failed=0
FM_TEST_SUMMARY_FAMILY family=zellij count=2 duration_ms=4370 failed=0
FM_TEST_SLOWEST rank=1 script=tests/fm-pr-check-security.test.sh duration_ms=134501
FM_TEST_SLOWEST rank=2 script=tests/fm-watch-triage.test.sh duration_ms=131357
FM_TEST_SLOWEST rank=3 script=tests/fm-session-lock-ancestry.test.sh duration_ms=130433
FM_TEST_SLOWEST rank=4 script=tests/fm-remote-secondmate-lifecycle-e2e.test.sh duration_ms=111307
FM_TEST_SLOWEST rank=5 script=tests/fm-session-start.test.sh duration_ms=105924
FM_TEST_SLOWEST rank=6 script=tests/fm-secondmate-harness.test.sh duration_ms=92941
FM_TEST_SLOWEST rank=7 script=tests/fm-watcher-lock.test.sh duration_ms=85893
FM_TEST_SLOWEST rank=8 script=tests/fm-claude-stop-autoarm.test.sh duration_ms=60710
FM_TEST_SLOWEST rank=9 script=tests/fm-watch-arm.test.sh duration_ms=55460
FM_TEST_SLOWEST rank=10 script=tests/fm-spawn-dispatch-profile.test.sh duration_ms=54293
FM_TEST_SLOWEST rank=11 script=tests/fm-remote-job.test.sh duration_ms=46032
FM_TEST_SLOWEST rank=12 script=tests/fm-procevent.test.sh duration_ms=45039
FM_TEST_SLOWEST rank=13 script=tests/fm-startup-network.test.sh duration_ms=44599
FM_TEST_SLOWEST rank=14 script=tests/fm-vendor-auth-probe.test.sh duration_ms=42856
FM_TEST_SLOWEST rank=15 script=tests/fm-bearings-snapshot.test.sh duration_ms=41268
fm-test-run: wrote timing artifact: /tmp/fm-ci.41J2ND/fm-test-timing-portable-serial.json
FM_CI_CHECK behavior-serial result=fail duration_ms=1921032
FM_CI_CHECK behavior-herdr result=unavailable duration_ms=0
fm-ci.sh: SKIPPED behavior-herdr: herdr 0.8.0 found but the pin is 0.7.4; install it with bin/fm-install-herdr.sh
FM_CI_SUMMARY verdict=FAIL checks=7 passed=4 failed=1 unavailable=2 duration_ms=2096693

fm-ci: FAIL
  PASS         lint               shell and workflow lint
  PASS         invariants         repository invariants
  PASS         coverage           regression partition is complete
  UNAVAILABLE  push-guard         the check reported it could not run; see its output above
  PASS         behavior-parallel  behavior suite, proven-isolated set
  FAIL         behavior-serial    exited 1
  UNAVAILABLE  behavior-herdr     herdr 0.8.0 found but the pin is 0.7.4; install it with bin/fm-install-herdr.sh

  COULD NOT CHECK means the listed checks did not run here.
  That is an absence of evidence, not a pass.
EXIT_CODE_MARKER=1

notno added a commit to nathan-rosquist/firstmate that referenced this pull request Sep 8, 2026
…henguid#9)

* fix(bin): strip record annotations from markdown backlog titles

The markdown-backlog path only removed annotation groups anchored at the
end of the row, so a row with any text after its annotations kept the raw
bookkeeping tokens inside the emitted title while the sibling fields
parsed correctly.

Strip known annotation groups wherever they appear in the row instead,
and trim the result so the existing end-anchored title-artifact rules
still match. Field parsing is unchanged, so the annotations' meaning is
preserved in repo, kind, priority, hold, blocker, and date fields.

* no-mistakes: apply CI fixes

(cherry picked from commit 8ec5973)
NewAiCoder referenced this pull request in NewAiCoder/firstmate Sep 26, 2026
… telemetry (#9)

* feat(gate): round cap at 3 with a severity escape and a rolling deferral issue

Updates the stale cross-references in tests/fm-brief.test.sh that
asserted the old two-round convergence text, since fm-dod-lib.sh is the
one owner of that contract and fm-brief.test.sh exercises it directly.

* feat(lanes): one surface per lane, worker-side size check, lane size in telemetry

fm-pr-check.sh is the script that durably records a task's PR URL (no
fm-pr-register.sh exists); the lane-size record call lands there.

* no-mistakes(review): {"summary": "Make severity, not category, sole criterion for round-3 defer rule"}

* no-mistakes(document): Document new lane-size target/cap/telemetry feature in owner docs

* no-mistakes(ci): Root cause: the PR's new `tests/fm-diff-size.test.sh` imported `~/.claude/telemetry/store.py` (a personal harness file living outside the git repo, on the owner's machine) to build its sqlite schema. On a fresh CI checkout that path doesn't exist, so the `import store` step fails, and the test aborts before exercising `fm-lane-size-record.sh` at all — this is exactly the "unclassified" family failure (count=2, failed=1, 1.15s) buried in the "Behavior portable serial 4" run, since the test never reached its `ok -` lines for the telemetry-record assertions. Fix: replaced the `sys.path.insert(...); import store` call with an inline `CREATE TABLE IF NOT EXISTS lanes (...)` matching the one table `fm-lane-size-record.sh` writes to, so the test is self-contained and no longer depends on host-specific state. Verified: `bash tests/fm-diff-size.test.sh` passes (exit 0, all 10 assertions "ok"), and re-ran it (plus the other newly-added, previously-unclassified tests `fm-dod-round-cap.test.sh`, `fm-dod-wait.test.sh`, `fm-nm-state-condition.test.sh`) under a synthetic empty `$HOME` to confirm no other hidden dependency on `~/.claude` — all pass cleanly, confirming the fix and ruling out similar issues in the sibling new tests

---------

Co-authored-by: NewAiCoder <170579485+NewAiCoder@users.noreply.github.com>
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.

2 participants