fix(fm-brief): remove heredoc-in-command-substitution that breaks bash 3.2#989
Open
sandersjdd3 wants to merge 1 commit into
Open
fix(fm-brief): remove heredoc-in-command-substitution that breaks bash 3.2#989sandersjdd3 wants to merge 1 commit into
sandersjdd3 wants to merge 1 commit into
Conversation
…h 3.2 (kunchenguid#958) fm-brief.sh failed to parse under stock macOS bash 3.2.57, so no brief could be scaffolded on a default macOS firstmate. Bash 3.2 misreads a lone apostrophe inside a heredoc body nested in $(...) as an unterminated single-quoted string and aborts with "unexpected EOF while looking for matching `)'". kunchenguid#945 added a line containing "firstmate's" to a DOD=$(cat <<EOF ...) block, triggering it. Bash 4+ parses the body literally, so CI and modern-bash dev machines passed. Durable fix, not just a reword: eliminate the VAR=$(cat <<EOF ... EOF) pattern. The mode-specific Definition of done is appended with a plain cat >> redirection heredoc (not wrapped in $(...)), and the unguarded Herdr section is built with printf like its --herdr-lab counterpart. Both quoted and unquoted heredoc delimiters trip the 3.2 bug, so the quoted Herdr <<'EOF' was a latent trap too. Brief content is byte-for-byte unchanged across all modes; only the shell construction changed. Regression test parses fm-brief.sh under every available bash (the 3.2 variant is invisible to a modern-bash bash -n) and adds a version-independent structural guard that the command-substitution-around-heredoc pattern does not return.
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Fix a bash 3.2 parse regression in bin/fm-brief.sh (issue #958) that broke ALL crewmate/scout brief scaffolding on stock macOS. Symptom: /bin/bash -n bin/fm-brief.sh (macOS /bin/bash is 3.2.57) fails with 'unexpected EOF while looking for matching )'. Root cause: bash 3.2 misreads a lone apostrophe inside a heredoc body nested in$(...) as an unterminated single-quoted string, scans to EOF, and aborts; bash 4+ parses it literally so CI and modern dev machines passed. #945 added a line containing 'firstmate's' to a DOD=$ (cat <<EOF ...) block, triggering it.
Deliberate design decisions in this fix: (1) The fix is DURABLE/structural, not a reword. I eliminated the VAR=$(cat <<EOF ... EOF) command-substitution-around-heredoc pattern entirely for the affected assignments rather than just rewording the one apostrophe, because a reword leaves the same trap for the next apostrophe. The mode case now sets only SETUP2/RULE1 scalars; the mode-specific Definition-of-done text is appended after the main brief with a plain 'cat >> "$BRIEF" <<EOF' redirection heredoc (NOT wrapped in$(...)), which parses fine on 3.2. (2) I also converted the unguarded Herdr section from HERDR_SECTION=$ (cat <<'EOF') to printf, matching its --herdr-lab counterpart which already used printf. This was intentional even though that block has no apostrophe today: I tested the counterfactual and confirmed BOTH quoted (<<'EOF') and unquoted heredoc-in-$() delimiters trip the bash 3.2 bug, so the quoted delimiter was a latent trap, not safe. (3) Brief CONTENT is byte-for-byte unchanged across all delivery modes (no-mistakes, direct-PR, local-only, scout) and the Herdr sections; #945's wording is preserved verbatim. I am fixing parsing only, not reverting or changing content. Verified by generating real briefs in a throwaway home and confirming identical output and boundaries.
Regression test: extended the existing tests/fm-brief.test.sh (colocated *.test.sh pattern). Strengthened test_script_parses to run 'bash -n' under EVERY available bash on the box (dedup by resolved path), because the #958 variant only surfaces on stock bash 3.2 and a modern-bash bash -n passes the exact input that breaks a stock-macOS firstmate. Added test_no_heredoc_in_command_substitution: a version-independent structural guard grepping for the '=$(cat <<' pattern in code lines (comment lines excluded) so the hazard cannot return even on modern-bash CI where the parse check would pass. Both tests fail on pre-fix code and pass on the fix; verified. bin/fm-lint.sh passes clean (shellcheck 0.11.0 pinned).
What Changed
VAR=$(cat <<EOF ... EOF)command-substitution-around-heredoc pattern inbin/fm-brief.shthat bash 3.2 (stock macOS/bin/bash3.2.57) misparses as an unterminated single-quoted string on a lone apostrophe, aborting brief scaffolding withunexpected EOF while looking for matching ). The mode case now sets onlySETUP2/RULE1scalars, and the mode-specific Definition-of-done text is appended via a plaincat >> "$BRIEF" <<EOFredirection heredoc; the unguarded Herdr section was converted from a quoted-delimiter heredoc-in-$()toprintf, matching its--herdr-labcounterpart. Generated brief content is byte-for-byte unchanged across all delivery modes and Herdr sections.tests/fm-brief.test.sh:test_script_parsesnow runsbash -nunder every distinct bash on the box (deduped by resolved path) so the 3.2-only variant is caught, and a newtest_no_heredoc_in_command_substitutiongrep guard fails on the=$(cat <<hazard pattern in code lines regardless of the running bash version.Risk Assessment
✅ Low: Parse-only bash 3.2 fix: verified OLD fails and NEW passes bash -n on stock /bin/bash, all Definition-of-done and Herdr content is byte-for-byte identical to the prior version, and the added regression tests correctly fail pre-fix and pass post-fix.
Testing
Reproduced #958 on the box's stock macOS bash 3.2.57 (the only bash present, and the exact broken environment): base commit's bin/fm-brief.sh fails
bash -nwith 'unexpected EOF while looking for matching )', while the target commit parses cleanly. Ran the full tests/fm-brief.test.sh suite (16/16 pass) and independently confirmed both regression guards catch the bug on pre-fix code (structural grep finds 4 heredoc-in-command-substitution hazard lines on base, 0 on fix). Captured the real end-user artifact — a generated no-mistakes ship brief produced on bash 3.2.57 — showing clean output with no heredoc corruption and the #945 apostrophe wording preserved verbatim. Not a UI change, so no visual artifact; the reviewer-visible artifact is the generated brief markdown.Evidence: Generated no-mistakes ship brief (produced on stock macOS bash 3.2.57 — the operation that was fully broken pre-fix)
Evidence: Bug reproduction + fix confirmation on bash 3.2.57
Evidence: fm-brief test suite result
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.
/bin/bash -n bin/fm-brief.shon base commit 10ee779 → rc=2, reproduced 'unexpected EOF while looking for matching )' on bash 3.2.57/bin/bash -n bin/fm-brief.shon target commit → rc=0 (parses clean on stock macOS bash 3.2.57)bash tests/fm-brief.test.sh→ all 16 tests pass (EXIT=0)Confirmedtest_no_heredoc_in_command_substitutiongrep finds 4 hazard lines on base (220/288/300/314) and 0 on fixGenerated a real no-mistakes ship brief viaFM_HOME=<tmp> /bin/bash bin/fm-brief.sh demo-task-001 my-projecton bash 3.2.57 → exit 0, 68 lines, 0 leaked EOF markers, #945 'firstmate's' apostrophe wording preserved verbatim✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.