Skip to content

test(harness): pin crew-harness realignment safety for secondmates and crewmates - #1921

Open
sbracewell64 wants to merge 2 commits into
kunchenguid:mainfrom
sbracewell64:fm/cfvc-14-e05-expiry
Open

sbracewell64 wants to merge 2 commits into
kunchenguid:mainfrom
sbracewell64:fm/cfvc-14-e05-expiry

Conversation

@sbracewell64

Copy link
Copy Markdown

Intent

Split config/crew-harness's two jobs so the expiring temporary Claude crew-routing authorization (E-05, expires 2026-08-08) expires into a CORRECT default rather than a stale one. config/crew-harness has read 'claude' since 2026-07-22 and answers two different questions at once: it is a DEAD crewmate fallback (config/crew-dispatch.json is always active and its default is harness pi, and fm-spawn refuses an unresolved crewmate spawn rather than reading the file) and the LIVE second link of the secondmate launch chain (bin/fm-harness.sh secondmate resolves config/secondmate-harness -> config/crew-harness -> own). An exception that expires into a stale default has not expired.

Ordering is load-bearing and was specified up front: config/secondmate-harness must be set explicitly FIRST, then config/crew-harness aligned with the dispatch default. The reverse order changes secondmate launch behaviour as a side effect of a crewmate correction. No secondmate exists today, so this is the cheap moment.

DELIBERATE SCOPE DECISION, which is why this diff is tests-only: config/ and data/captain.md are LOCAL, gitignored files in the operator's private home. They cannot be changed from a worktree and are not tracked material, so the tracked deliverable is the resolution logic plus tests, and the exact local config writes were reported separately to the operator. The two local writes are: config/secondmate-harness = claude (already applied by the operator after the captain answered the one engineer question - secondmates launch on Claude, with Pi kept available as a deliberate later option), and config/crew-harness claude -> pi, to be applied on or after 2026-08-08 together with deleting the expiring captain-preferences section.

SECOND DELIBERATE DECISION: bin/fm-harness.sh was NOT modified. Its fallback chain already resolves an explicit config/secondmate-harness ahead of the crew fallback, verified directly against the live config bytes on a scratch copy. What was missing was ENFORCEMENT, not logic - nothing pinned the ordering property that makes the crew-harness realignment safe. So the change adds three regressions to the existing split suite tests/fm-secondmate-harness.test.sh (new section D), extending that file rather than adding a new runner:

  • D1: with config/secondmate-harness pinned, editing config/crew-harness leaves fm-harness.sh secondmate unchanged.
  • D2: the same property at the spawn chokepoint, which re-resolves on every launch, so the pin survives a respawn after the edit.
  • D3: config/crew-harness cannot reach a crewmate launch while dispatch profiles are active, for each matching rule AND the default - identical launch command and recorded profile across crew-harness values - and an unresolved spawn refuses instead of reading the file.

Each case deliberately carries a NEGATIVE CONTROL, because all three assertions would also hold if config/crew-harness had simply stopped working everywhere: the unpinned secondmate must move with the edit, and with no dispatch file the crewmate launch must diverge by crew-harness value. The launch-command comparisons normalize the task id out of the embedded brief path, because otherwise the divergence control would pass vacuously on the differing ids alone. Both controls were confirmed non-vacuous by mutation testing: disabling the pin resolution fails D1/D2, and removing the dispatch backstop fails D3.

This touches worker-launch harness resolution and was treated with R1-RUNTIME rigor per the plan's certification note. bin/fm-lint.sh is clean, the full split suite and tests/fm-spawn-dispatch-profile.test.sh pass.

KNOWN PRE-EXISTING FAILURE, not caused by this change and deliberately not fixed here: tests/fm-secondmate-harness.test.sh's test_spawned_secondmate_uses_its_harness_supervision_model fails whenever the checkout sits on a named feature branch, because the worktree-tangle guard fires on it and its output reaches that assertion. Confirmed identical at base commit ed376cf with this change absent.

What Changed

  • Adds section D (three regressions) to tests/fm-secondmate-harness.test.sh: with config/secondmate-harness pinned, editing config/crew-harness leaves fm-harness.sh secondmate resolution unchanged (D1) and the pin survives a respawn through the spawn chokepoint, which re-resolves on every launch (D2); while dispatch profiles are active, config/crew-harness cannot reach a crewmate launch — launch command and recorded profile are identical across crew-harness values for each matching rule and the default, and an unresolved crewmate spawn refuses instead of reading the file (D3).
  • Each case carries a negative control so the assertions can't pass vacuously — the unpinned secondmate must move with the crew-harness edit, and with no dispatch file the crewmate launch must diverge by crew-harness value — with task ids normalized out of the embedded brief path in launch-command comparisons; both controls were confirmed non-vacuous by mutation testing. Also fixes the stale capability count in the suite's header comment.
  • No bin/ or config changes: bin/fm-harness.sh already resolves an explicit config/secondmate-harness ahead of the crew fallback, and these tests enforce that ordering so the expiring E-05 crew-harness realignment (claude → pi on 2026-08-08) lands safely. The remaining files in the nominal range vs base 33a4287 are fork-trunk commits already landed through PRs feat(bin): guard watcher liveness across supervision scripts #8–Crash/reboot resilience + atomic O_EXCL lock (fix WSL2 mkdir non-atomicity) #48 that origin/main does not yet carry.

Risk Assessment

✅ Low: A tests-only additive commit whose new assertions I verified statically against the unmodified production logic in bin/fm-harness.sh and bin/fm-spawn.sh (resolution chain, per-launch re-resolution, dispatch backstop message and exit code), each carrying non-vacuity negative controls, satisfying every required intent constraint and touching no production code.

Testing

Ran the full fm-secondmate-harness suite (all 50 tests pass, D1–D3 included) plus the adjacent dispatch-profile suite, proved all three new regressions non-vacuous by mutating the pin resolution and the dispatch backstop in turn (each mutation is caught by its intended test), and captured a CLI transcript of the E-05 expiry scenario showing the pinned secondmate harness survives the crew-harness claude→pi realignment while the unpinned control moves; no visual evidence applies since this is a shell-CLI test harness with no rendered surface, and the worktree was left clean.

Evidence: E-05 expiry scenario CLI transcript (pinned secondmate survives crew-harness claude→pi; unpinned control moves)

$ # STEP 1 - the 2026-08-08 realignment: crew-harness claude -> pi $ echo pi > config/crew-harness $ bin/fm-harness.sh secondmate # MUST stay claude: the explicit pin insulates secondmate launches claude $ bin/fm-harness.sh crew # crewmate fallback now matches the dispatch default (pi) pi $ # CONTROL - the same realignment with NO pin $ echo pi > config/crew-harness && bin/fm-harness.sh secondmate # unpinned: the edit moves secondmate launches too pi

$ # E-05 expiry scenario on a scratch config (FM_CONFIG_OVERRIDE), CLAUDECODE=1 (primary runs on claude)
$ # STEP 0 - state today: temporary E-05 authorization in effect, plus the operator's already-applied pin
$ cat config/secondmate-harness config/crew-harness
claude
claude
$ bin/fm-harness.sh secondmate   # second link of the launch chain resolves the pin
claude
$ bin/fm-harness.sh crew
claude

$ # STEP 1 - the 2026-08-08 realignment: crew-harness claude -> pi (align with the crew-dispatch default)
$ echo pi > config/crew-harness
$ bin/fm-harness.sh secondmate   # MUST stay claude: the explicit pin insulates secondmate launches
claude
$ bin/fm-harness.sh crew         # crewmate fallback now matches the dispatch default (pi)
pi

$ # CONTROL - the same realignment with NO pin (the ordering the change guards against)
$ rm config/secondmate-harness; echo claude > config/crew-harness
$ bin/fm-harness.sh secondmate
claude
$ echo pi > config/crew-harness && bin/fm-harness.sh secondmate   # unpinned: the edit moves secondmate launches too
pi
Evidence: New D-section regressions passing (baseline, unmodified bin/)

ok - D1 a config/crew-harness realignment leaves a PINNED secondmate harness alone; an unpinned one moves with it (the pin-first ordering is load-bearing) ok - D2 spawn: a pinned config/secondmate-harness keeps launching its own harness across a crew-harness realignment; an unpinned home takes the edit ok - D3 spawn: config/crew-harness cannot reach a crewmate launch while dispatch profiles are active, for every matching rule and the default; with no dispatch file it is live again

=== BASELINE: the three new section-D regressions (trimmed runner over tests/fm-secondmate-harness.test.sh, unmodified bin/) ===
$ bash tests/.tmp-d-only.test.sh
ok - D1 a config/crew-harness realignment leaves a PINNED secondmate harness alone; an unpinned one moves with it (the pin-first ordering is load-bearing)
ok - D2 spawn: a pinned config/secondmate-harness keeps launching its own harness across a crew-harness realignment; an unpinned home takes the edit
ok - D3 spawn: config/crew-harness cannot reach a crewmate launch while dispatch profiles are active, for every matching rule and the default; with no dispatch file it is live again
# all D-section tests passed
exit=0
Evidence: Mutation A: pin resolution disabled in fm-harness.sh — D1 and D2 each fail (tests are non-vacuous)

--- D1 alone --- not ok - pinned: secondmate resolved 'claude' before the edit, expected the pinned pi exit=1 --- D2 alone --- not ok - pinned spawn: pre-edit secondmate launched on 'claude', expected the pinned pi exit=1

=== MUTATION A: bin/fm-harness.sh resolve_secondmate() ignores config/secondmate-harness (pin resolution disabled) ===
--- diff applied ---
+++ bin/fm-harness.sh	2026-08-07 18:29:55.124262743 -0400
@@ -133,9 +133,7 @@
 # launched on the crew harness). config/secondmate-harness is the PRIMARY's own
 # setting and is never inherited downstream - secondmates do not spawn secondmates.
 resolve_secondmate() {
-  local sm
-  sm=$(secondmate_field 1)
-  if [ -z "$sm" ] || [ "$sm" = "default" ]; then resolve_crew; else echo "$sm"; fi
+  resolve_crew
 }
 
 # Print the optional model token (2nd field) from config/secondmate-harness, or
--- D1 alone ---
not ok - pinned: secondmate resolved 'claude' before the edit, expected the pinned pi
exit=1
--- D2 alone ---
not ok - pinned spawn: pre-edit secondmate launched on 'claude', expected the pinned pi
exit=1
Evidence: Mutation B: dispatch backstop removed from fm-spawn.sh — D3 fails (backstop assertion is live)

--- D3 alone --- not ok - an unresolved crew spawn should refuse while dispatch profiles are active (crew-harness='claude'): expected exit 1, got 0 exit=1

=== MUTATION B: bin/fm-spawn.sh single-spawn dispatch backstop removed (unresolved crew spawn falls back to config/crew-harness) ===
--- diff applied ---
@@ -897,10 +897,6 @@
       HARNESS=$("$FM_ROOT/bin/fm-harness.sh" secondmate)
       harness_src='config/secondmate-harness (falling back to config/crew-harness)'
     else
-      if [ -f "$CONFIG/crew-dispatch.json" ]; then
-        echo "error: config/crew-dispatch.json is active - pass an explicit harness resolved from the dispatch rules (the consultation backstop, so the rules are never silently skipped)." >&2
-        exit 1
-      fi
       HARNESS=$("$FM_ROOT/bin/fm-harness.sh" crew)
       harness_src='config/crew-harness'
     fi
--- D3 alone ---
not ok - an unresolved crew spawn should refuse while dispatch profiles are active (crew-harness='claude'): expected exit 1, got 0
exit=1
Evidence: Full fm-secondmate-harness suite run (50 tests, exit 0)
ok - A1 fm-harness.sh secondmate resolves the fallback chain; crew mode unchanged
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)
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
ok - D1 a config/crew-harness realignment leaves a PINNED secondmate harness alone; an unpinned one moves with it (the pin-first ordering is load-bearing)
ok - D2 spawn: a pinned config/secondmate-harness keeps launching its own harness across a crew-harness realignment; an unpinned home takes the edit
ok - D3 spawn: config/crew-harness cannot reach a crewmate launch while dispatch profiles are active, for every matching rule and the default; with no dispatch file it is live again
# all fm-secondmate-harness tests passed
EXIT=0

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

⏭️ **Rebase** - skipped

Push main to origin, or rebase your branch onto origin/main, before gating.

⚠️ **Review** - 2 infos
  • ℹ️ tests/fm-secondmate-harness.test.sh:2590 - crew_ship_spawn does not pin CLAUDE_CONFIG_DIR, unlike the mirrored run_spawn helper in tests/fm-spawn-dispatch-profile.test.sh which pins it explicitly (with a rationale comment) because fm-spawn.sh prefixes 'CLAUDE_CONFIG_DIR=...' onto claude launch commands. No current D3 assertion can be affected — the dispatch-profile rows use grok/pi/codex (never claude) and the negative control only does a substring check — but any future tightening of D3 to exact-launch comparisons involving claude would inherit a dependency on the developer's ambient environment. Purely a hardening/consistency note.
  • ℹ️ tests/fm-secondmate-harness.test.sh:1 - Scope note: the supplied base 33a4287 is the upstream merge-base, so the nominal range spans ~45 already-landed fork-trunk commits (each merged via its own PR, ending at ed376cf / PR Crash/reboot resilience + atomic O_EXCL lock (fix WSL2 mkdir non-atomicity) #48). The branch's actual change is the single tests-only tip commit ef1e200 (+216 lines in tests/fm-secondmate-harness.test.sh), which is what this review assessed in depth and which matches the intent's 'tests-only' claim exactly — no non-test files are touched by the tip commit.
✅ **Test** - passed

✅ No issues found.

  • bash tests/fm-secondmate-harness.test.sh — full changed suite, all 50 tests pass (exit 0), including new D1/D2/D3; the known branch-dependent supervision test passes on this detached-HEAD checkout, consistent with the intent's claim
  • Trimmed D-only runner over the suite: test_pinned_secondmate_survives_a_crew_harness_realignment (D1), test_pinned_secondmate_spawn_survives_a_crew_harness_realignment (D2), test_crew_harness_is_inert_for_dispatch_resolved_crewmates (D3) — all pass on unmodified bin/
  • bash tests/fm-spawn-dispatch-profile.test.sh — adjacent suite covering the dispatch backstop D3 leans on, all pass
  • Mutation A (non-vacuity): made resolve_secondmate() in bin/fm-harness.sh ignore config/secondmate-harness — D1 and D2 each fail independently; reverted
  • Mutation B (non-vacuity): removed the crew-dispatch.json consultation backstop from bin/fm-spawn.sh's single-spawn path — D3 fails; reverted
  • Manual end-to-end E-05 expiry scenario via FM_CONFIG_OVERRIDE scratch config: pinned secondmate-harness=claude holds fm-harness.sh secondmate=claude across crew-harness claude→pi while crew moves to pi; unpinned control moves to pi with the edit
  • Diff audit of ef1e200 vs its parent: single-file, tests-only (+216 lines), bin/ and config untouched — matches both deliberate scope decisions in the intent
  • Post-test cleanup: mutations reverted via git checkout, temp runners deleted, git status clean, no leaked /tmp fixtures
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

…d crewmates

`config/crew-harness` answers two questions with a single value: it is the
crewmate fallback and the second link of the secondmate resolution chain
(`config/secondmate-harness` -> `config/crew-harness` -> own). Correcting it
for one of those roles can silently re-answer the other, and nothing enforced
that an explicit `config/secondmate-harness` insulates a secondmate launch from
such an edit, or that an active `config/crew-dispatch.json` keeps the edit away
from crewmate launches.

Add three regressions to the existing split suite:

- D1 pins the resolution-level property: with `config/secondmate-harness` set,
  editing `config/crew-harness` leaves `fm-harness.sh secondmate` unchanged.
- D2 pins the same property at the spawn chokepoint, which re-resolves on every
  launch, so the pin survives a respawn after the edit.
- D3 pins that `config/crew-harness` cannot reach a crewmate launch while
  dispatch profiles are active, for each matching rule and the default: a
  resolved profile produces an identical launch command and recorded profile
  across crew-harness values, and an unresolved spawn refuses rather than
  reading the file.

Each case carries a negative control, because all three would also hold if
`config/crew-harness` had simply stopped working: the unpinned secondmate must
move with the edit, and with no dispatch file the crewmate launch must diverge
by crew-harness value. Both controls were confirmed to fail when the pin
resolution and the dispatch backstop were mutated in turn.

No behavior change; resolution logic is unmodified.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant