Skip to content

feat(system): allow a newer Bash to skip a fork the 3.0 floor requires - #1362

Merged
Chemaclass merged 1 commit into
mainfrom
feat/1352-bash-version-gated-fast-paths
Sep 9, 2026
Merged

feat(system): allow a newer Bash to skip a fork the 3.0 floor requires#1362
Chemaclass merged 1 commit into
mainfrom
feat/1352-bash-version-gated-fast-paths

Conversation

@Chemaclass

Copy link
Copy Markdown
Member

🤔 Background

Related #1352

bashunit floors at Bash 3.0, so it forks where a newer shell has a builtin — and there was no way to ship a faster body for the newer shell, because the compatibility rules reject a too-new construct anywhere in src/, including inside a branch 3.0 never takes.

This is the mechanism, not a speed run. The floor does not move.

💡 Changes

  • src/system/bash.sh declares one flag per version boundary, named for its tier; a leaf module picks between two bodies with a column-0 if/else. Selection at load time costs between -0.07 and +0.09 µs per call, against +17.9 on Bash 3.0 for the runtime-predicate pattern.
  • Each compatibility rule now declares its construct's minimum version. A construct is allowed only inside a gate of at least that tier. Tier 0 means no gate is ever enough — the parse-time constructs that kill a file from an untaken branch, and the rules whose boundary is not a version ([[ =~ ]], the 5.3.9 locale segfault). A pragma and a per-file allow-list were both rejected; the commit says why.
  • One gated helper, chosen because it removes a fork rather than because a builtin exists: printf -v writes into a variable where the 3.0 body must capture. Both bodies hand the same format and value to the same printf, so equivalence holds by construction — unlike the textbook ${v,,} example, which disagrees with tr on non-ASCII (assert_contains_ignore_case: the non-ASCII folding comment is wrong for GNU tr #1351).
  • Verified end to end: the build emits both blocks intact, and the artifact parses and runs the helper's tests on 3.0, 3.2, 4.4, 5.2 and 5, selecting the branch its tier calls for.
  • Plus adrs/adr-013, a parse test over every src/ file, and two guards the mechanism needed: every gate must name a declared flag, and build_test's column-0 duplicate check cannot see either body of a gated helper.

https://claude.ai/code/session_01EXYWTGLjf7qM8Ru3GakDRm

bashunit floors at Bash 3.0, so it forks where a newer shell has a
builtin, and there was no way to ship a faster body for the newer shell:
the compatibility rules reject a too-new construct anywhere in src/,
including inside a branch 3.0 never takes. Deliberately blunt, because
the Bash 3.0 job only catches a too-new construct when a test happens to
execute the line.

This is the mechanism, not a speed run. The floor does not move.

src/system/bash.sh declares one flag per version boundary in use, named
for its tier, and a leaf module picks between two bodies with a column-0
if/else. Measured over 100k calls, best of 5, against a single ungated
definition: picking the definition once costs between -0.07 and +0.09
microseconds per call, while a predicate function per call costs +17.9
on 3.0 and +4.3 on 5.3. Selection is free; the pattern already in use is
the slow one. The five existing runtime predicates are correct and stay.

Each compatibility rule now declares its construct's minimum version as
a tier, and a construct is allowed only inside a column-0 gate whose
tier is at least that. Tier 0 means no gate is ever enough: the
parse-time constructs, which kill the file from inside a branch that
shell never takes, and the rules whose boundary is not a version at all
-- `[[ =~ ]]` changed semantics between 3.0 and 3.2, and the
temporary-locale prefix is a 5.3.9 segfault.

Rejected, and why, since both are tempting: a trailing pragma is the
author's claim rather than a structural fact, so honouring it is a
smuggling path by construction; a per-file allow-list lets an ungated
construct elsewhere in the same file ship.

The first gated helper is lpad, chosen because it removes a fork rather
than because a builtin exists: `printf -v` writes into a variable, the
3.0 body has to capture. Both bodies hand the same format and the same
value to the same printf, so the output is identical by construction --
the textbook `${v,,}` example would not be, since it disagrees with `tr`
on non-ASCII (#1351), which is the whole reason a gated helper needs an
equivalence test.

Verified end to end: the build emits both blocks intact, the artifact
parses on 3.0, 3.2, 4.4, 5.2 and 5, and the helper's tests pass on each
with the branch its tier calls for -- `tr`-free `printf -v` above 3.1,
the capture on real 3.00.0.

Two guards the mechanism needs and did not have: every flag a gate names
must be one the flags file declares, or a typo leaves the fast body
unreachable and the fallback in use everywhere, green and slower; and
build_test's duplicate check anchors at column 0, so it cannot see
either body of a gated helper, which is defined twice by design.

Closes #1352

Claude-Session: https://claude.ai/code/session_01EXYWTGLjf7qM8Ru3GakDRm
@Chemaclass Chemaclass added the enhancement New feature or request label Sep 9, 2026
@Chemaclass Chemaclass self-assigned this Sep 9, 2026
@Chemaclass
Chemaclass merged commit 33b9777 into main Sep 9, 2026
37 checks passed
@Chemaclass
Chemaclass deleted the feat/1352-bash-version-gated-fast-paths branch September 9, 2026 10:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant