Skip to content

fix: bash 3.2 fractional read timeouts + CI portability lint#14

Merged
crgeee merged 3 commits into
masterfrom
fix/bash32-read-timeout
Apr 26, 2026
Merged

fix: bash 3.2 fractional read timeouts + CI portability lint#14
crgeee merged 3 commits into
masterfrom
fix/bash32-read-timeout

Conversation

@crgeee

@crgeee crgeee commented Apr 26, 2026

Copy link
Copy Markdown
Owner

Summary

You hit read: 0.01: invalid timeout specification when pressing Enter on an empty command-mode prompt.

Root cause: macOS default /bin/bash is 3.2 (Apple froze it pre-GPLv3). Bash 3.2 only accepts integer read -t timeouts; the dashboard had a read -t 0.01 for "refresh-now" semantics that worked on bash 4+ in development but errored on the actual installed bash.

There was also a second fractional read -t 0.05 for distinguishing lone Esc from arrow-key escape sequences — the same bug, I missed it.

Two fixes

  1. Drop the 0.01s read entirely. When the refresh interval has elapsed, just call _dashboard_refresh_cache and continue — no read needed. Eliminates drift too.
  2. _read_short_peek helper for the Esc disambiguation. Branches on a single BASH_HAS_FRACTIONAL_TIMEOUT flag set at script load. 50ms on bash 4+, non-blocking -t 0 on bash 3.2 (arrow-key follow-up bytes are already buffered when we reach the peek, so it works either way).

CI: new portability lint

To catch this class at PR time, new bash32-portability job greps for patterns that break on bash 3.2:

  • read -t <fractional>
  • mapfile / readarray
  • ${var^^} / ${var,,} case conversions
  • declare -n / local -n namerefs
  • read -i (initial buffer)

Lines tagged # bash4-ok (gated uses) and pure comment lines are skipped. The helper that intentionally uses read -t 0.05 carries the bash4-ok annotation.

This is the second time I've shipped a bash-3.2 issue (first was read -e -i in PR #6). The CI lint catches both retroactively and prevents future ones.

Test plan

  • All 78 bats tests pass on bash 5 + macOS bash 3.2
  • shellcheck clean
  • Portability lint passes on this branch
  • Manually verified the read -t 0.01 path is gone
  • CI: new bash32-portability job passes
  • Test the dashboard interactively after merge — the original repro (Enter on empty prompt) should no longer throw

🤖 Generated with Claude Code

User hit `read: 0.01: invalid timeout specification` after pressing
Enter on an empty command-mode prompt. Root cause: macOS default
/bin/bash is 3.2 and only accepts integer `read -t` timeouts; bash 4+
accepts decimals.

Two fixes:

1. Drop the 0.01s read in the dashboard loop entirely — when the
   refresh interval has already elapsed, `_dashboard_refresh_cache`
   immediately and continue, no read at all. Net effect: no drift
   either, and works on every bash.

2. Extract the Esc-vs-arrow-key disambiguation read (the OTHER
   fractional-timeout site I'd missed) into a single helper:
   `_read_short_peek`. Helper checks BASH_HAS_FRACTIONAL_TIMEOUT
   (set once at script load) and uses 50ms on bash 4+ or a
   non-blocking peek (`-t 0`) on bash 3.2. Arrow-key follow-up
   bytes are typically already buffered when we reach the peek,
   so non-blocking still works in practice.

CI: new "Bash 3.2 portability lint" job greps the script for
patterns that break on bash 3.2:
  - `read -t <fractional>`
  - mapfile / readarray
  - ${var^^} / ${var,,} case conversions
  - declare -n / local -n namerefs
  - read -i (initial buffer)

Lines with `# bash4-ok` annotation are skipped (the one helper that
intentionally branches by version uses this). Pure-comment lines are
also skipped so prose mentioning the patterns doesn't trip the lint.

This catches the class of issue at PR time instead of at runtime
on a user's mac mini.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 26, 2026 22:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes interactive dashboard input handling to be compatible with macOS’s default Bash 3.2 (which rejects fractional read -t timeouts) and adds CI enforcement to prevent future Bash-4+ portability regressions.

Changes:

  • Introduce a Bash-version capability flag and _read_short_peek helper to safely disambiguate lone Esc vs escape sequences across Bash 3.2 vs 4+.
  • Remove the fractional read -t 0.01 refresh behavior by refreshing immediately when the interval elapses (no read needed).
  • Add a new GitHub Actions portability-lint job that flags common Bash-4+ constructs (fractional read -t, mapfile, ${var^^}, namerefs, read -i) unless annotated with # bash4-ok.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
gh-runner-status Adds Bash version capability detection + _read_short_peek, and removes fractional read timeout usage in the dashboard loop.
.github/workflows/ci.yml Adds a new bash32-portability CI job to detect Bash-3.2-incompatible patterns early.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread gh-runner-status
Comment thread .github/workflows/ci.yml Outdated
Comment thread gh-runner-status
@crgeee crgeee merged commit c9c8f47 into master Apr 26, 2026
6 checks passed
@crgeee crgeee deleted the fix/bash32-read-timeout branch April 26, 2026 23:10
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