Skip to content

fix(bin): handle dash-leading harness commands portably - #1263

Open
harsh9200 wants to merge 2 commits into
kunchenguid:mainfrom
harsh9200:fm/fm-stop-guard-basename
Open

fix(bin): handle dash-leading harness commands portably#1263
harsh9200 wants to merge 2 commits into
kunchenguid:mainfrom
harsh9200:fm/fm-stop-guard-basename

Conversation

@harsh9200

Copy link
Copy Markdown

Intent

Fix firstmate's recurring macOS Claude Stop-hook wake noise caused when BSD basename interprets a login-shell command such as -zsh as an option. Reproduce the stderr failure first, then make every process-command extraction in the session-lock and harness detection composition portable without adding GNU coreutils, using pure-shell suffix extraction. Sweep bin/ for the same basename or dirname GNU-flag portability class, including bin/fm-harness.sh detect_own, add or extend colocated regression coverage proving the Stop auto-arm stale-owner path and harness detection emit no stderr under BSD basename semantics, and keep touched shell scripts ShellCheck-clean. Limit the change to this portability fix and its focused tests.

What Changed

  • Replace basename process-command parsing with portable shell suffix extraction in harness detection and session-lock ancestry/liveness checks, preventing BSD basename errors for commands such as -zsh.
  • Add regression coverage proving dash-leading commands emit no stderr and the Claude Stop auto-arm stale-owner flow continues successfully.

Risk Assessment

✅ Low: Captain, the change is narrowly scoped, preserves command-name extraction semantics, removes BSD basename option parsing from all identified process-command paths, and adds focused regression coverage for both affected flows.

Testing

Reproduced the native and base-commit BSD stderr leak first, then verified the target harness emits no stderr and manually exercised stale-owner Stop auto-arm through lock reclamation to a clean persisted epoch. Both focused automated suites completed successfully, evidence transcripts were preserved, and the worktree remained clean. ShellCheck/static analysis was not run because this testing step explicitly prohibited it; no visual artifact applies to this silent shell-hook/CLI change.

Evidence: BSD failure reproduction

Native macOS basename -zsh exited 1 with the BSD illegal-option diagnostic.

Native macOS BSD basename reproduction
Command: basename -zsh
Exit: 1
Stderr:
basename: illegal option -- z
usage: basename string [suffix]
       basename [-a] [-s suffix] string [...]
Evidence: Base-to-target harness comparison

Base commit leaked 108 stderr bytes for comm=-zsh; the target returned unknown with zero stderr.

Base-commit harness detection with ps comm=-zsh
Command: base bin/fm-harness.sh at 99533c5d
Exit: 0
Stdout: unknown
Stderr bytes: 108
Stderr:
basename: illegal option -- z
usage: basename string [suffix]
       basename [-a] [-s suffix] string [...]
Evidence: Stale `-zsh` Stop-hook evidence

The manual Stop-hook flow exited silently, reclaimed the stale lock, executed auto-arm, and recorded a clean epoch.

Manual Claude Stop auto-arm stale-login-shell verification
Simulated stale owner command: -zsh
Hook exit: 0
Hook stdout bytes: 0
Hook stderr bytes: 0
Original stale owner pid: 13994
Reclaimed lock owner pid: 97982
Auto-arm executed pid: 14212
Epoch: epoch=2 owner_pid=13996 outcome=clean updated_at=1785355110
Evidence: Focused Stop auto-arm tests

Focused regression transcript covering BSD basename semantics and stale-owner Stop auto-arm behavior.

FM_TEST_BEGIN 2026-07-29T19:56:29Z tests/fm-claude-stop-autoarm.test.sh family=unclassified expected_gate_skip=none
ok - settings.json registers the asyncRewake auto-arm with timeout >= 28800 and a foreground arm
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: stale -zsh lock-owner composition is silent with BSD basename semantics
ok - auto-arm: inert without arm, rewake, or lock replacement when another live harness owns the home
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: watcher: FAILED translates to an exit-2 alarm rewake
ok - auto-arm: clean close exits silently with a clean epoch
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-07-29T19:57:39Z tests/fm-claude-stop-autoarm.test.sh exit=0 duration_ms=70236 gate_skip=false
FM_TEST_SUMMARY total=1 failed=0 skipped_gate=0 duration_ms=70408
FM_TEST_SUMMARY_FAMILY family=unclassified count=1 duration_ms=70236 failed=0
FM_TEST_SLOWEST rank=1 script=tests/fm-claude-stop-autoarm.test.sh duration_ms=70236
Evidence: Harness regression tests

Surrounding harness-resolution and launch compatibility regression transcript.

FM_TEST_BEGIN 2026-07-29T19:59:56Z 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 - 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 - 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: 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 with no inherited config is a config no-op and still fast-forwards
ok - B12b backend inheritance: present values and primary absence converge exactly
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-07-29T20:03:26Z tests/fm-secondmate-harness.test.sh exit=0 duration_ms=210132 gate_skip=false
FM_TEST_SUMMARY total=1 failed=0 skipped_gate=0 duration_ms=210240
FM_TEST_SUMMARY_FAMILY family=secondmate count=1 duration_ms=210132 failed=0
FM_TEST_SLOWEST rank=1 script=tests/fm-secondmate-harness.test.sh duration_ms=210132

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.

  • basename -zsh using native macOS BSD basename
  • Base-commit bin/fm-harness.sh from 99533c5d with an exported ps probe returning comm=-zsh
  • Target bin/fm-harness.sh with the same comm=-zsh probe
  • bin/fm-test-run.sh tests/fm-claude-stop-autoarm.test.sh
  • Manual Claude Stop-hook stale-owner flow using comm=-zsh, a BSD-failing basename shim, and persisted lock/arm/epoch inspection
  • bin/fm-test-run.sh tests/fm-secondmate-harness.test.sh
  • rg sweep of bin/**/*.sh for GNU-style basename/dirname options and process-command extraction sites
  • git status --short --untracked-files=all
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

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