Skip to content

fix(bin): parse cleanly under macOS system bash 3.2 - #1125

Closed
ammar00sheikh wants to merge 3 commits into
kunchenguid:mainfrom
ammar00sheikh:fm/fmbrief-bash32
Closed

ammar00sheikh wants to merge 3 commits into
kunchenguid:mainfrom
ammar00sheikh:fm/fmbrief-bash32

Conversation

@ammar00sheikh

Copy link
Copy Markdown

Problem

On a stock macOS host the system shell is bash 3.2.57, and /usr/bin/env bash resolves to it when no newer bash is installed.
Running any ship scaffold (bin/fm-brief.sh <id> <repo>) failed:

bin/fm-brief.sh: line 314: unexpected EOF while looking for matching `)'
bin/fm-brief.sh: line 388: syntax error: unexpected end of file

Root cause (verified by bisection): bash 3.2's naive $(...) scanner tracks quote state through a heredoc body nested in a command substitution, so the apostrophe in the no-mistakes DOD wording ("firstmate's authority check", introduced in ec09871 / #945) inside DOD=$(cat <<EOF ... EOF) broke the parse of the entire rest of the script.
Every ship-task scaffold on a stock-bash mac failed; scout scaffolds escaped only because they exit 0 before the parser reaches the ship-mode case.
The issue #166 regression test did not catch this because its bash -n runs under the dev bash, whose scanner accepts the wording.

Fix

Structural, not a rewording: every VAR=$(cat <<EOF ... EOF) in bin/ now uses the IFS= read -r -d '' VAR <<EOF ... EOF || true idiom plus a single trailing-newline strip, so no heredoc lives inside $(...) and future copy edits adding apostrophes, backticks, or parens cannot re-break parsing.
The emitted brief text is byte-identical: the diff touches only assignment wrappers (zero body lines), and the idiom was empirically verified equivalent to $(cat <<EOF) under bash 3.2, including expansion and backtick handling.

Converted sites:

  • bin/fm-brief.sh - the three ship-mode DOD heredocs and the unguarded Herdr section (the reproduced failure).
  • bin/fm-fleet-snapshot.sh (4 sites) and bin/fm-bootstrap.sh (1 site) - the audit's remaining $(cat << instances; they parse today only because their apostrophes happen to balance, so they were converted rather than listed.

Regression coverage

tests/fm-brief.test.sh (extended in place because CI test lanes are explicit hand-maintained lists):

  • bash -n now also runs under the system /bin/bash - the real 3.2 gate on macOS.
  • A sweep syntax-checks every bin/*.sh and bin/backends/*.sh under /bin/bash, guarding the whole bug class.
  • The per-mode scaffold test asserts non-empty output containing each delivery mode's definition-of-done marker.

Pipeline follow-ups on this branch: documentation of the heredoc rule, and the macOS CI lane now runs the fm-brief regression gate so the sweep executes under a real bash 3.2 in CI, not only on developer macs.

Verification (on the affected stock-bash mac)

  • Reproduced first on the default branch: /bin/bash -n bin/fm-brief.sh exit 2 with the exact error above.
  • After the fix: /bin/bash -n clean on all bin scripts; real scaffolds for no-mistakes, direct-PR, local-only, --scout, and --secondmate all generate correctly under bash 3.2.
  • tests/fm-brief.test.sh green; bin/fm-test-run.sh --changed (~40 suites) exit 0; bin/fm-lint.sh green under pinned ShellCheck 0.11.0.
  • Pre-existing, unrelated: tests/fm-bearings-snapshot.test.sh fails identically on the clean default branch on this host.

🤖 Generated with Claude Code

Amsh added 3 commits July 27, 2026 17:20
bash 3.2's naive $(...) scanner tracks quote state through a heredoc body
nested in a command substitution, so the apostrophe added to fm-brief.sh's
no-mistakes DOD wording (ec09871) broke the parse of the entire script on a
stock mac, where /usr/bin/env bash resolves to /bin/bash 3.2.57. Every ship
scaffold failed with "unexpected EOF while looking for matching `)'".

Replace every VAR=$(cat <<EOF ...) in bin/ with the IFS= read -r -d ''
heredoc idiom plus a single trailing-newline strip, which is byte-equivalent
to the command-substitution form and immune to future copy edits adding
apostrophes, backticks, or parens: fm-brief.sh (the reproduced failure),
and the same latent pattern in fm-fleet-snapshot.sh and fm-bootstrap.sh.
The emitted brief text is unchanged.

Extend tests/fm-brief.test.sh to also syntax-check fm-brief.sh and every
bin script under the system /bin/bash (the real 3.2 gate on macOS runners),
and to assert each delivery mode's definition-of-done marker in a scaffolded
ship brief.
@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate: closing this as stale. It has been waiting on a contributor update for 14+ days with no author push or comment. Reopen if you want to pick it back up.

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