Skip to content

fix(doctor): §25 crashed under macOS system bash 3.2 — backtick in $()-nested quoted heredoc (BRO-1718)#93

Merged
broomva merged 1 commit into
mainfrom
bro-1718-bash32-doctor-parse
Jul 7, 2026
Merged

fix(doctor): §25 crashed under macOS system bash 3.2 — backtick in $()-nested quoted heredoc (BRO-1718)#93
broomva merged 1 commit into
mainfrom
bro-1718-bash32-doctor-parse

Conversation

@broomva

@broomva broomva commented Jul 7, 2026

Copy link
Copy Markdown
Owner

What

v0.34.1 — patch release. doctor.sh §25 (shipped hours ago in v0.34.0) dies at parse time under stock macOS /bin/bash 3.2 (the script's own shebang):

doctor.sh: line 1354: unexpected EOF while looking for matching ``'
doctor.sh: line 1426: syntax error: unexpected end of file

The §25 script_path() python heredoc carried a literal backtick in the operator-blacklist tuple (+4 more in comments). The delimiter is quoted (<<'PYEOF') but the block sits inside $( ... ), and bash 3.2's command-substitution pre-parser scans the raw body text and dies on backticks there — and (surfaced during the fix round) on unbalanced quotes and dollar-paren openers too.

Why every gate missed it

Dev wrapper (exec bash → PATH bash 5.3), ubuntu CI (bash 5), local suite (bash "$t" → PATH bash), hermetic fresh-install test (bash "$REPO/scripts/doctor.sh") — four green layers, one shared premise: bash ≥ 4. The delivery interface (direct shebang execution — exactly how a stock-macOS fresh-install user runs it) was the one path nobody exercised. Infrastructure-layer instance of pattern/shared-assumption-drift, sibling of 0.34.0's pattern/self-hosting-vacuous-pass.

Fix

  • doctor.sh — all 5 backticks purged from the §25 heredoc body: tuple element → chr(96) (behaviorally identical membership test), comment backticks → plain quotes, NB comment documents the constraint. Zero bash-4-isms in the file → fully 3.2-clean. Verified: direct shebang execution under 3.2.57 runs all 25 sections to a verdict against the self-hosting workspace; §-by-§ output identical to bash 5.3.
  • tests/bash32-parse-safety.test.sh (new) — the class guard, proven RED on 0.34.0 (flags exactly the 5 lines + the parse failure) → GREEN here:
    • A1 (deterministic incl. ubuntu CI): no literal backtick inside quoted-heredoc bodies opened inside unclosed $(/<(, scanning scripts/+tests/+bin/. Standalone heredocs deliberately unflagged (parse-safe in 3.2; compute-arc-status.sh carries them today). Documented residual: multi-line $( spans — backstopped by A2 on macOS.
    • A2: /bin/bash -n parses every script — under the system bash: a real 3.2 on macOS dev machines, i.e. the local suite now exercises the delivery interface ubuntu CI cannot.

P20 cross-review (Strata B, adversarial)

Round 1: 6/10 — reviewer proved the first version of the guard itself carried a literal backtick inside its own $()-nested heredoc (the scan's comparison line): the guard embodied the bug it polices and couldn't parse on the machines it protects. Fix round applied: scanner builds every parser-hostile char via chr(), tests/ added to its own scan scope, scanner exit-status checked (L1), bin/ shebang-filtered (L2), <(+double-quoted-delimiter forms covered and residuals documented honestly (M1). Negative control re-proven after the round.

Validation

  • New test: RED on 0.34.0 → GREEN here, under real bash 3.2.57 and 5.3.9
  • Full suite: 34/34
  • shellcheck (CI flags): clean
  • validate-release.yml gates: VERSION 0.34.0→0.34.1 semver-monotonic, CHANGELOG section matches

Linear: BRO-1718

🤖 Generated with Claude Code

…)-nested quoted heredoc (BRO-1718)

v0.34.0's §25 script_path() heredoc carried a literal backtick in the
operator-blacklist tuple (+4 in comments). The delimiter is quoted, but the
block lives inside $( ... ) and bash 3.2's pre-parser dies on backticks there
— and, as the fix round surfaced, on unbalanced quotes and dollar-paren
openers in raw body text too. Shebang is #!/bin/bash → stock macOS = 3.2 →
doctor died at §25 with no verdict for exactly the fresh-install users
BRO-1715 targeted.

Four green verification layers (dev wrapper, ubuntu CI, local suite, hermetic
fresh-install test) all resolve bash from a modern PATH — one shared premise,
bash >= 4; the delivery interface (direct shebang exec) was the one path
nobody exercised. Infrastructure-layer instance of shared-assumption-drift.

- doctor.sh: all 5 backticks purged from the §25 body (tuple → chr(96),
  comments → plain quotes, NB documents the constraint). Zero bash-4-isms →
  fully 3.2-clean: direct shebang exec now runs all 25 sections to a verdict.
- tests/bash32-parse-safety.test.sh: class guard, RED on 0.34.0 → GREEN.
  A1: no backtick in $()/<( -nested quoted-heredoc bodies (scripts/tests/bin;
  scanner builds parser-hostile chars via chr() — it scans itself).
  A2: /bin/bash -n parses everything — real 3.2 on macOS dev machines.

P20 cross-review: 6/10 round 1 (the guard itself carried the bug it polices
— caught adversarially), fix round applied; verified vs validate-release.yml.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@broomva, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 13 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 74eeb7bf-835b-4586-9682-a04c0595cbc2

📥 Commits

Reviewing files that changed from the base of the PR and between 1c769e8 and d4dc982.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • VERSION
  • scripts/doctor.sh
  • tests/bash32-parse-safety.test.sh
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bro-1718-bash32-doctor-parse

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@broomva broomva merged commit 93a2698 into main Jul 7, 2026
6 checks passed
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