Doctrine
macOS shell snippets must be portable and bounded
macOS shell snippets must be portable and bounded
The rule
Any shell snippet intended for a live fleet seat, feed post, handoff, or copy-paste diagnostic MUST assume macOS + zsh unless it explicitly executes a checked-in script with a bash shebang.
Two concrete consequences follow:
1. Do not rely on GNU timeout. macOS seats do not ship it by default. Use a tool-native timeout (curl --max-time, curl --connect-timeout, python3 subprocess.run(timeout=...), etc.) or a checked-in helper script. 2. Do not use status as a shell variable name in portable snippets. zsh reserves status as a read-only special parameter. Use names like http_code, exit_code, tool_state, or result_state.
Why
Fleet work often happens under time pressure on seats that default to zsh. A snippet that works in bash but fails on the first paste into zsh creates shell noise right when the operator is trying to gather evidence. That converts a simple live check into a context switch: the seat has to debug the tool instead of proving the underlying question.
The failure pattern is predictable:
- A seat pastes a snippet with
status=$(...)and zsh rejects it because - A seat pastes a snippet that shells out to GNU
timeout, but the binary is - The operator gets error text instead of the evidence table they were actually
status is read-only.
not installed on macOS, so the diagnostic never runs.
trying to collect.
This doctrine treats shell portability as evidence quality, not just style.
Canonical guidance
For one-off network checks
- Prefer
curl --max-time <seconds> --connect-timeout <seconds>over wrapping - Prefer explicit variable names:
http_code,json_body,row_count,
curl in an external timeout binary.
probe_state.
For repeated audit patterns
- Do not keep re-typing ad hoc snippets in feed replies or handoff docs.
- Promote the pattern into a checked-in helper script, then point future seats
- Current example in this repo:
at the helper.
./scripts/postgrest-count-compare.sh camber heartwood <table...>
For repo scripts
- Standalone scripts with
#!/bin/bashare allowed to be bash-specific, but
if their internals are likely to be copied into docs or terminal snippets, prefer portable naming there too.
Scope
In scope:
- Feed posts containing runnable shell
- Diagnostic packets and handoff docs
- README / AGENTS command examples
- One-liners pasted into Codex / Claude / Gemini seats
- New fleet helper scripts
Out of scope:
- Purely internal bash implementation details that are never surfaced as
- Non-shell programs using their own native timeout APIs
copy-paste snippets
Sourcing
Sourced by a 2026-04-17 Codex fleet audit pass where a live table-count comparison had to be re-run after two environment wrinkles surfaced on-seat:
timeoutwas not installed on the macOS seat- zsh rejected
status=...becausestatusis read-only
The work itself was fine; the shell surface was noisy. This doctrine exists so future seats get evidence first and shell debugging second.