Skip to content

fix(harness): stop BSD basename spraying stderr on the ancestry walk - #846

Open
tkt028 wants to merge 1 commit into
kunchenguid:mainfrom
tkt028:fix/harness-detect-bsd-login-shell-dash
Open

fix(harness): stop BSD basename spraying stderr on the ancestry walk#846
tkt028 wants to merge 1 commit into
kunchenguid:mainfrom
tkt028:fix/harness-detect-bsd-login-shell-dash

Conversation

@tkt028

@tkt028 tkt028 commented Jul 22, 2026

Copy link
Copy Markdown

Intent

Make a failed spawn on macOS diagnosable. Today the harness detector emits a spurious
basename: illegal option -- z immediately above spawn's genuine refusal, and the two adjacent
lines read as cause and effect. They aren't related at all — but a reader can't tell that from
the output, so the first thing they investigate is the wrong thing.

This is a diagnostics change. It fixes no behaviour and changes no verdict.

When this bites

Detection short-circuits on Layer 1 env markers, so this never fires when CLAUDECODE=1
(or PI_CODING_AGENT / GROK_AGENT) is set. It bites when detection falls through to the
Layer 2 ancestry walk on a BSD userland:

  • a harness that sets no env marker (codex / opencode) on macOS
  • any direct CLI invocation of fm-harness.sh or fm-spawn.sh from a terminal
  • any spawn whose parent chain reaches the user's login shell

ps -o comm= reports a login shell with a leading dash (-zsh), and BSD basename parses that
-z as an option:

$ basename -zsh
basename: illegal option -- z
usage: basename string [suffix]
       basename [-a] [-s suffix] string [...]

GNU basename accepts it, so Linux users never see this and it doesn't show up in CI.

Outcome

What a macOS user sees on a refused spawn today:

basename: illegal option -- z
error: no launch template for harness 'unknown' (from config/crew-harness or detection)

What they see after this change:

error: no launch template for harness 'unknown' (from config/crew-harness or detection)

The remaining line is true, actionable, and points at the real fix (set config/crew-harness,
or pass a launch command). The removed line pointed at nothing.

Evidence this is noise-only, not behaviour

Worth stating explicitly, because I originally got this wrong myself and don't want to hand
anyone a rationale I can't back.

fm-harness.sh runs set -u without set -e, and fm-spawn.sh invokes it as a
subprocess (HARNESS=$("$FM_ROOT/bin/fm-harness.sh" crew)), so fm-spawn.sh's set -eu
never propagates in. A failed $(basename …) yields an empty string, matches no case arm, and
the walk simply continues to the next ancestor.

Verified against a faked ps so the ancestry could be controlled:

Simulated ancestry Verdict stderr (before)
-zsh only unknown — correct, no harness present basename: illegal option -- z
-zshclaude claude — detected through the dash basename: illegal option -- z
zshclaude claude (clean)

Row 2 is decisive: the dash never blocked detection. And row 1 shows unknown was the correct
answer — a dispatch from a plain login shell genuinely has no harness in its ancestry.

Why I know the confusion is real, not hypothetical. Downstream we hit exactly this output,
read the two lines as cause→effect, and pursued "BSD basename breaks harness detection" as the
root cause. It isn't. The actual cause was mundane — a crew harness that was never configured,
so detection was consulted at all. The misleading line is what sent the investigation sideways,
and it will do the same to the next person.

Change

Take the command name with parameter expansion instead of basename(1):

name=${comm##*/}   # strip any directory prefix (what basename gave us)
name=${name#-}     # strip a login shell's leading dash

Immune to option parsing by construction — there is no argv to misparse. Behaviour is otherwise
deliberately identical: an absolute comm still reduces to its basename, and the exact-match arm
(pi) still matches exactly.

(It also drops one fork per ancestor. Minor — each iteration still forks ps twice plus tr, so
this is roughly a quarter of an already-cheap loop, not a meaningful speedup. Noted for accuracy,
not offered as a reason to merge.)

Tests

New tests/fm-harness-detect-login-shell.test.sh (6 assertions), registered in the
pure-contract-unit family. It drives a faked ps via the existing fm_fakebin shim so an
ancestry can be described without depending on the real process tree, and pins both halves —
no stderr noise, and verdicts unchanged:

  • a login shell in the chain produces no stderr
  • a bare login shell still resolves to unknown (no harness in the ancestry)
  • a harness above a login shell is still detected, silently
  • an absolute comm path still resolves to its basename
  • the exact-match pi arm still matches after stripping
  • -bash is not mistaken for a harness

Verified to fail without the fix — the test is not decorative:

not ok - login shell '-zsh' in the chain wrote to stderr: basename: illegal option -- z

Checks

  • shellcheck --norc clean on all three touched files (0.11.0, the pinned/CI version)
  • bin/fm-test-run.sh on the affected suites — fm-harness-detect-login-shell (6),
    fm-secondmate-harness (27), fm-secondmate-liveness (10), fm-session-start (20),
    fm-turnend-guard (40), fm-test-run (10) — 113 ok, 0 failures
  • fm-backend shows 1 failure (old fm-teardown.sh (scout, report present)
    fm-decision-hold: compatible tasks-axi is required, local tasks-axi 0.1.2). It is
    pre-existing and environmental: reproduced identically with this change reverted.

Tested on macOS (BSD userland), which is where the defect is observable.

ps -o comm= reports a login shell with a leading dash ("-zsh"), and BSD
basename parses that "-z" as an option and fails with "illegal option -- z".
Every harness detection that walks past a login shell therefore writes that to
stderr on macOS/BSD. GNU basename accepts it, so the noise is BSD-only.

This is a DIAGNOSTICS fix, not a behaviour fix — it changes no verdict.
fm-harness.sh runs `set -u` without `set -e`, and fm-spawn.sh invokes it as a
subprocess, so a failed $(basename ...) yields an empty string, matches no case
arm, and the walk continues to the next ancestor. Verified against a faked ps:
a `claude` ancestor above a `-zsh` is still detected.

It is worth fixing anyway because the noise is emitted immediately before
spawn's genuine "no launch template for harness 'unknown'" refusal, and the two
adjacent lines read as cause and effect. Downstream we misdiagnosed exactly
that way; the real cause was mundane and correct — a dispatch from a plain
login shell has no harness anywhere in its ancestry.

Take the command name with parameter expansion instead: ${comm##*/} strips any
directory prefix (what basename gave us) and ${name#-} strips the login-shell
dash. Besides being immune to option parsing, this removes up to 8 subprocess
forks per detection from a path that runs on every spawn and session start.

Adds tests/fm-harness-detect-login-shell.test.sh (6 assertions, registered in
the pure-contract-unit family), which drives a faked ps so an ancestry can be
described independently of the real process tree. It pins both halves — no
stderr noise, AND verdicts unchanged — and is verified to fail without the fix.
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