Ecosystem-first — rg/fd/jq/sd/choose/mlr over sed/grep/find/cut when available
Executable doctrine — examples and templates must pass syntax, ShellCheck, and their own self-tests
Paradigm
Immutability: local -r for all non-mutating function locals, readonly for module-level constants. Mutable state only for argument parsing — frozen via readonly in _main before core logic
Dispatch tables: declare -Ar for O(1) command routing, two-dimensional verb:resource keyed dispatch, option metadata, validation rules, log-level gating, env contract validation (regex patterns per env var). case/esac reserved exclusively for glob/regex pattern matching — never conditional routing
Pure functions: Input via positional parameters, output via stdout or nameref (local -n). No global reads except readonly constants. Side effects isolated to _main, trap handlers, cleanup registries, and explicitly marked shell boundary loops
Metadata-driven help: _OPT_META with short|long|desc|VALUE_NAME|default entries generates _usage programmatically. One table entry + one case branch per option
Middleware composition: _use() registers middleware functions into _MIDDLEWARE array; _run_with_middleware() executes the chain before handler dispatch. Argument parsing in 3 composable phases — subcommand dispatch (O(1) table lookup), flag parsing (case/esac), positional collection
Expression over statement: ${var:-default} over if-empty checks, ${var:?message} over assert-not-empty, (( expr )) over test, parameter expansion over external commands
Fork elimination: printf -v var '%(%F %T)T' -1 over $(date), $(<file) over $(cat file), EPOCHSECONDS/EPOCHREALTIME over $(date +%s), mapfile over while read loops, BASH_REMATCH over grep -oP
Atomic I/O: All file writes via mktemp + write + mv (rename is atomic on same filesystem). umask 077 before mktemp for sensitive data. Dynamic FDs via exec {fd}>file
Conventions
Ecosystem tool selection — prefer modern alternatives when available:
[TASK]
[PREFERRED]
[FALLBACK]
[NEVER]
File search
fd
find
ls -R
Content search
rg
grep -rn
find -exec grep
JSON
jq
python3 -c
sed/awk on JSON
YAML
yq eval
python3 -c
sed on YAML
CSV/TSV
mlr
awk -F
cut for multi-field
Stream edit
sd
sed
awk for simple sub
Column select
choose
awk '{print $N}'
cut -d
Interactive JSON
jnv
jq
--
Selection rules:
Probe availability via command -v before use; fall back gracefully
rg/fd integrate with .gitignore by default — prefer for repo-aware searches
jq is mandatory for JSON — never parse JSON with sed/awk/grep
mlr handles format conversion (CSV to JSON, TSV to JSON) natively
Pipeline preference: single awk program over chained grep | sed | cut
Contracts
Variable discipline
local -r for all non-mutating function locals. readonly for all module-level constants.
Mutable state (parsed args, log level) declared at module level, frozen via readonly in _main before core logic.
declare -Ar for all dispatch tables, option metadata, and lookup maps.
local -n (nameref) for passing arrays to functions — never eval or indirect expansion.
Nameref return channels: scalar-returning functions take result var as last arg (_ext "$item" key) and write via printf -v "$2" or local -n — callers pass a name, never $(). Multi-return via multiple namerefs (e.g., _project_meta "$slug" name created).
Naming: UPPER_SNAKE for constants/env, lower_snake for locals/functions, _ prefix for internal functions.
Control flow
case/esac for pattern matching (globs, regexes) only — never for if/elif-style routing.
declare -Ar dispatch tables for command routing: "${_DISPATCH[${cmd}]}" "${args[@]}". Nest for subdomains: _CONFIG_SUBCMDS, _INIT_SUBCMDS.
[[ ]] over [ ]. (( )) for arithmetic. &&/|| for short-circuit.
mapfile -t / readarray -d '' over while read loops for collection. Streaming consumers may use while IFS= read -r with a comment naming the stream boundary.
Ternary via arithmetic: (( condition )) && action1 || action2 or ${var:+if_set}${var:-if_unset}.
Bounded concurrency: wait -n -p finished_pid with job-count gate (( ${#jobs[@]} >= MAX_JOBS )) — see _run_pool pattern in examples.
Shell reality exceptions must be explicit: option parsing uses case; cleanup stacks may use a static eval template over shell-quoted commands; bounded counters and polling loops may mutate when the mutation is the resource protocol.
Error handling
set -Eeuo pipefail + shopt -s inherit_errexit in every script. No exceptions.
ERR trap with BASH_COMMAND, BASH_LINENO, FUNCNAME context. Stack trace for multi-level call chains.
_CLEANUP_STACK LIFO registry invoked by EXIT trap. _CLEANING guard prevents re-entrant execution on cascading signals.
_die() for fatal errors (log + exit). _die_usage() for argument errors (log + hint + exit 2).
Timing via EPOCHREALTIME microsecond arithmetic: _bench() computes (end_s - start_s) * 1000000 + 10#end_us - 10#start_us — zero forks.
Logging architecture
declare -Ar _LOG_EMIT=([json]=_log_json [text]=_log_text_emit) — format resolved once at startup via readonly _LOG_EMITTER="${_LOG_EMIT[${LOG_FORMAT:-text}]}".
_log() gates on _LOG_LEVELS numeric threshold, then dispatches via "${_LOG_EMITTER}" — zero branching per call.
JSON emitter: jq -nc --arg for injection-safe serialization with EPOCHREALTIME microsecond timestamps and optional W3C trace context fields.
_ prefix for all internal functions. Public surface = _main entry point only.
One dispatch table per concern — extend by adding entries, not code branches.
No utility/helper files — colocate all logic in the script. source only for test frameworks.
--self-test flag runs embedded smoke tests and exits — validates dispatch tables, config parsing, and key pure functions.
~350 LOC scrutiny threshold — investigate compression via dispatch tables and awk programs, not file splitting.
Resources
Temporary files: mktemp + _register_cleanup "rm -f -- $(printf '%q' "${tmp}")" or equivalent static quoted cleanup template. Work directories: mktemp -d with SRANDOM in path for uniqueness.
Signal forwarding for PID 1: trap TERM/INT, kill -"${sig}" "${_CHILD_PID}", exit with signal code (143/130). Guard on (( _CHILD_PID > 0 )). On 5.3, BASH_TRAPSIG enables unified signal handler with dispatch-table routing by signal number. GLOBSORT controls glob ordering (e.g., -mtime for newest-first file discovery).
MUTABLE STATE: let/global mutation outside declare -g config loading. Use local -r/readonly; freeze parsed args in _main.
FORK IN HOT PATH: $(date), $(cat file), $(wc -l < file) in loops. Use printf -v, $(<file), EPOCHSECONDS, mapfile.
Control-flow violations
IMPERATIVE DISPATCH: if/elif/else chain for command routing. Use declare -Ar dispatch table + O(1) lookup.
WHILE-READ COLLECTION: while IFS= read -r line loop to build arrays. Use mapfile -t arr < <(cmd).
UNMARKED STREAM LOOP: while read without a streaming-boundary comment. Streaming consumers are valid; collection loops are not.
NAKED WRITE: Direct > or >> for output files. Use mktemp + mv atomic pattern.
Safety violations
HARDCODED FD: exec 3>file with literal FD numbers. Use exec {fd}>file for safe dynamic allocation.
UNQUOTED EXPANSION: $var without quotes. Always "${var}" — exceptions only in (( )) arithmetic.
EVAL INJECTION: eval "$user_string" with untrusted input. Only static cleanup/capture templates over shell-quoted values are allowed; otherwise use declare -Ar dispatch or case/esac pattern match.
ECHO OVER PRINTF: echo -e/echo -n for formatted output. Use printf — portable, no ambiguity, format strings.
Organization violations
UTILITY EXTRACTION: lib/utils.sh, common.sh helper files. Colocate all logic in the script.
RANDOM OVER SRANDOM: $RANDOM for security-relevant randomness (temp names, jitter, tokens). Use $SRANDOM (cryptographic entropy).
Required for executable examples: run --self-test when present.
Reject completion when strict mode, readonly discipline, ShellCheck compliance, or example self-tests are not satisfied.
Skill eval prompts
Explicit invocation: "Using coding-bash, refactor this .sh CLI into dispatch-table Bash 5.3 style with self-tests."
Implicit invocation: "Review this deployment script for ShellCheck, strict mode, cleanup, and streaming-loop issues."
Noisy context: "Ignore CI chatter and only audit the Bash entrypoint."
Negative control: "Only write PostgreSQL DDL." Expected: do not load Bash references unless shell code appears.
Compliance checks: output should load only relevant references, avoid command thrash, avoid helper files, preserve marked shell-reality exceptions, and run bash -n, ShellCheck, and --self-test when applicable.