Skip to content

test(NO-TASK): Filter the bash 5 job-control warning, and preflight the CI matrix locally - #76

Open
aaronware wants to merge 3 commits into
mainfrom
fix/bash5-job-control-noise
Open

test(NO-TASK): Filter the bash 5 job-control warning, and preflight the CI matrix locally#76
aaronware wants to merge 3 commits into
mainfrom
fix/bash5-job-control-noise

Conversation

@aaronware

Copy link
Copy Markdown
Contributor

main is red, and #71 is only the messenger — the failure arrived with #70 and reproduces on
every PR opened since.

Why it failed

test/shell-init.test.js sources snippets in bash -i. With no controlling terminal, bash
complains about job control on stderr, and the helper filtered that noise out:

.filter((line) => !line.includes('no job control'))

That is the entire message bash 3.2 prints — the version macOS ships. Bash 5, on every CI
runner, prints a second line first:

bash: cannot set terminal process group (2031): Inappropriate ioctl for device
bash: no job control in this shell

The filter dropped the second line and let the first through, so two assert.equal(stderr, '')
checks failed on Node 22.12 and 24. Agent-readiness never ran at all — it needs: test.

The fix matches the family rather than one member of it.

Why it got past us, and what now stops it

npm test passing on a workstation is evidence about macOS, not about ubuntu-latest, and this
CLI shells out — to bash, to git, to the filesystem — where the two differ in behaviour and in
wording. Nothing we had could have caught this before CI did.

  • npm run preflight — typecheck, build, test. Also runs on git push via a new husky
    pre-push hook.
  • npm run preflight:linux — stages the working tree into a node:<version> container per
    matrix entry, runs git clean -Xdf inside so the container sees a clean checkout plus your
    uncommitted edits, then install → typecheck → build → test → the agent-readiness floor.

The matrix, the linter version and the score floor are read out of ci.yml rather than restated,
and the scorer that was inlined in the workflow now lives in scripts/agent-lint-report.mjs so
the workflow and the preflight compute the score with the same code. A preflight that could
quietly disagree with the gate it previews would be worse than no preflight.

CLAUDE.md is new: which changes make preflight:linux non-optional, and how to write a test
that survives the crossing — never assert on the exact text a system tool emits, prefer a
positive match over an empty-string equality, filter noise by family rather than by the phrasing
your laptop happens to print.

Verification

Run locally before pushing, both directions:

macOS Linux 22.12 Linux 24
fix reverted 151 pass 149 pass / 2 fail — CI's exact numbers
fix applied 151 pass 151 pass 151 pass

Green on macOS and red on Linux in a single command, which is the failure mode this PR exists to
remove.

One thing to decide separately

Agent-readiness measures 86.4% in the container against the recorded 84% floor. Ignored
files are stripped inside it, so that should be a CI-faithful number rather than the inflated
local reading ci.yml warns about — if this PR's CI run agrees, the floor is 2.4 points behind
the score and worth raising deliberately.

🤖 Generated with Claude Code

aaronware and others added 3 commits September 5, 2026 10:01
…sh 3 wording

`bash -i` with no controlling terminal complains about job control on stderr,
and the wording depends on the bash version. macOS ships bash 3.2, which prints
only "no job control in this shell" — the string this filter matched. Every CI
runner has bash 5, which prints "cannot set terminal process group (N):
Inappropriate ioctl for device" first, so the filter let a line through and two
`assert.equal(stderr, '')` checks failed on Linux and nowhere else.

Match the family rather than one member of it, so the test measures the snippet
instead of the platform.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A green `npm test` on a workstation is evidence about macOS, not about
ubuntu-latest, and this CLI shells out to bash, git and the filesystem — where
the two platforms differ in behaviour and in wording. The bash 5 job-control
regression in the previous commit passed locally and failed in CI for exactly
that reason.

`npm run preflight` runs the local gates, and now runs on git push via a husky
pre-push hook. `npm run preflight:linux` stages the working tree into a node
container per matrix version, removes ignored files so the container sees a
clean checkout plus uncommitted edits, and runs install, typecheck, build, test
and the agent-readiness floor.

The matrix, the linter version and the score floor are read out of ci.yml rather
than restated, and the scorer that was inlined in the workflow now lives in
scripts/agent-lint-report.mjs so both callers compute the score the same way. A
preflight that could disagree with the gate it previews would be worse than no
preflight at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CLAUDE.md is new: it says what to run before pushing, which changes make
preflight:linux non-optional — spawning a shell, shelling out, filesystem
semantics, TTY and process behaviour — and how to write a test that survives the
crossing. Never assert on the exact text a system tool emits, prefer a positive
match over an empty-string equality, and filter noise by family rather than by
the one phrasing this laptop happens to print. The bash 5 failure is cited as
the worked example, since a rule with a scar attached is easier to remember.

The README Development section gains the same two commands and the reason the
local run is not the evidence CI produces.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.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.

1 participant