🤔 Background
src/console/colors.sh builds the palette with one $(bashunit::sgr N) per colour. Each of those is a subshell fork, and there are sixteen. Every invocation pays for them, including bashunit --version and bashunit --help, which never print a colour.
They survived the #801–#851 fork campaign because that campaign's census method is a PATH shim: a subshell around a shell function execs no binary, so no shim and none of the ^\++ +/path/to/binary trace patterns can see it. .claude/rules/perf-fork-budget.md still records cold start as "3 forks" on that basis.
💡 Measurements
Per-source-file profile (macOS, timestamps injected at the # src/<path> markers the build emits):
| source file | cost |
|---|
src/config/env.sh | 12.6 ms |
src/console/colors.sh | 10.0 ms |
src/state/payload.sh | 2.9 ms |
src/system/check_os.sh | 2.4 ms |
| total top-level execution | 41.6 ms |
For contrast, parsing the whole 600KB artifact and defining all 915 functions costs 10 ms — parsing is not the bottleneck, source-time execution is.
💡 Fix
The sixteen values are constants. Build them through the return-slot pattern (_BASHUNIT_SGR_OUT) that .claude/rules/bash-style.md already prescribes, keeping the public echoing bashunit::sgr as a thin wrapper so its contract is unchanged.
Interleaved A/B, 3 rounds × 200 invocations in one tree: 60.0/60.9 ms → 51.6/50.7 ms, about 9 ms and 15% off every cold start.
🤔 Background
src/console/colors.shbuilds the palette with one$(bashunit::sgr N)per colour. Each of those is a subshell fork, and there are sixteen. Every invocation pays for them, includingbashunit --versionandbashunit --help, which never print a colour.They survived the #801–#851 fork campaign because that campaign's census method is a PATH shim: a subshell around a shell function execs no binary, so no shim and none of the
^\++ +/path/to/binarytrace patterns can see it..claude/rules/perf-fork-budget.mdstill records cold start as "3 forks" on that basis.💡 Measurements
Per-source-file profile (macOS, timestamps injected at the
# src/<path>markers the build emits):src/config/env.shsrc/console/colors.shsrc/state/payload.shsrc/system/check_os.shFor contrast, parsing the whole 600KB artifact and defining all 915 functions costs 10 ms — parsing is not the bottleneck, source-time execution is.
💡 Fix
The sixteen values are constants. Build them through the return-slot pattern (
_BASHUNIT_SGR_OUT) that.claude/rules/bash-style.mdalready prescribes, keeping the public echoingbashunit::sgras a thin wrapper so its contract is unchanged.Interleaved A/B, 3 rounds × 200 invocations in one tree: 60.0/60.9 ms → 51.6/50.7 ms, about 9 ms and 15% off every cold start.