@keelcore/standards 0.9.7 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -26
- package/governance/ai/conduct.md +17 -26
- package/governance/api-management.md +91 -102
- package/governance/arb.md +55 -62
- package/governance/bash.md +117 -46
- package/governance/ci.md +165 -144
- package/governance/coding.md +89 -78
- package/governance/observability.md +77 -86
- package/governance/platform.md +81 -87
- package/governance/release-policy.md +126 -96
- package/governance/runtime.md +87 -95
- package/governance/security.md +77 -85
- package/governance/testing.md +66 -20
- package/package.json +1 -1
package/governance/bash.md
CHANGED
|
@@ -3,15 +3,18 @@
|
|
|
3
3
|
These rules govern all bash scripts in this project. They are non-negotiable.
|
|
4
4
|
|
|
5
5
|
**Maturity:** Required
|
|
6
|
-
**Version:** 1.
|
|
7
|
-
**Last Reviewed:** 2026-03-
|
|
6
|
+
**Version:** 1.2.0
|
|
7
|
+
**Last Reviewed:** 2026-03-17
|
|
8
|
+
|
|
9
|
+
Each rule has a stable canonical ID (`R1`–`R42`) used when these rules are
|
|
10
|
+
cited elsewhere. Numbers do not renumber across edits.
|
|
8
11
|
|
|
9
12
|
## Portability and Shell Baseline
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
- **R1.** Google Bash style unless overridden by rules below.
|
|
15
|
+
- **R2.** Darwin-compatible; Bash 3.x compatible — no `mapfile`, no `declare -A`.
|
|
16
|
+
- **R3.** Shebang: `#!/usr/bin/env bash` always.
|
|
17
|
+
- **R4.** Use the explicit shell options block (not `set -euo pipefail` shorthand).
|
|
15
18
|
|
|
16
19
|
## Required Shell Options Block
|
|
17
20
|
|
|
@@ -33,66 +36,111 @@ set -o errexit
|
|
|
33
36
|
set -o pipefail
|
|
34
37
|
```
|
|
35
38
|
|
|
39
|
+
## Locale Determinism
|
|
40
|
+
|
|
41
|
+
Bash tooling is locale-sensitive and MUST run under a pinned C locale so output is reproducible
|
|
42
|
+
across machines and CI (the generic policy: see coding.md, "Locale and Reproducibility"):
|
|
43
|
+
|
|
44
|
+
- **L1.** `sort` collates by `LC_COLLATE` — order differs across locales, and `sort -u` can even
|
|
45
|
+
dedup differently. `awk`/`printf` format numbers by `LC_NUMERIC` — a comma-radix locale (e.g.
|
|
46
|
+
`de_DE`) turns `95.00` into `95,00`, breaking numeric comparisons and file compares. `tr [:upper:]`,
|
|
47
|
+
`[[ a < b ]]`, and `grep` character classes are likewise locale-dependent.
|
|
48
|
+
- **L2.** The build entrypoint (the `Makefile`) MUST `export LC_ALL := C`, so every recipe shell and
|
|
49
|
+
its subprocesses — including canonical scripts a consumer does not edit — run under C.
|
|
50
|
+
- **L3.** A script that sorts or formats numbers and may be run OUTSIDE `make` MUST source a locale
|
|
51
|
+
guard (`scripts/lib/locale.sh`) that exports `LC_ALL=C`, so direct `bash scripts/x.sh` runs are
|
|
52
|
+
deterministic too.
|
|
53
|
+
- **L4.** The lock is testable: a BATS case runs a recipe under a hostile `LC_ALL`/`LC_NUMERIC` and
|
|
54
|
+
asserts `LC_ALL=C` wins.
|
|
55
|
+
|
|
36
56
|
## Script Structure
|
|
37
57
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
58
|
+
- **R5.** `main` is the FIRST function defined in the file.
|
|
59
|
+
- **R6.** `main "${@:-}"` is the LAST line of the file.
|
|
60
|
+
- **R7.** Always provide the complete script, never a patch or diff.
|
|
61
|
+
- **R8.** Never omit any referenced function.
|
|
42
62
|
|
|
43
63
|
## Functions
|
|
44
64
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
65
|
+
- **R9.** All function declarations preceded by keyword `function`.
|
|
66
|
+
- **R10.** No `function_` prefix on function names.
|
|
67
|
+
- **R11.** Function names: lowercase, simple, reflect intent.
|
|
68
|
+
- **R12.** One statement per line.
|
|
69
|
+
- **R13.** No function body > 10 lines unless reduction is genuinely impossible.
|
|
70
|
+
- **R14.** Prefer decomposition into small, reusable helpers.
|
|
71
|
+
- **R15.** Load-bearing functions: log intent first, then log result.
|
|
52
72
|
|
|
53
73
|
## Arguments and Validation
|
|
54
74
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
75
|
+
- **R16.** Validate argument count.
|
|
76
|
+
- **R17.** Validate required arguments are non-null.
|
|
77
|
+
- **R18.** Support empty argument lists under `set -u` using `"${@:-}"`.
|
|
78
|
+
- **R19.** `validate_args` must allow an empty first argument when zero arguments are valid:
|
|
59
79
|
|
|
60
|
-
```bash
|
|
61
|
-
function validate_args() {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
```
|
|
80
|
+
```bash
|
|
81
|
+
function validate_args() {
|
|
82
|
+
if [ "${#}" -gt 0 ] && [ -z "${1:-}" ]; then
|
|
83
|
+
log '❌ Error: Unexpected empty argument'
|
|
84
|
+
exit 1
|
|
85
|
+
fi
|
|
86
|
+
}
|
|
87
|
+
```
|
|
68
88
|
|
|
69
89
|
## Variables
|
|
70
90
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
91
|
+
- **R20.** Always `"${var}"` style for variable expansion.
|
|
92
|
+
- **R21.** Quote expansions unless unquoted behavior is explicitly required.
|
|
93
|
+
- **R22.** `local -r` for immutable locals NOT assigned from subshell.
|
|
94
|
+
- **R23.** Mutable locals or subshell-assigned: declare on one line, assign on next:
|
|
75
95
|
|
|
76
|
-
```bash
|
|
77
|
-
local result
|
|
78
|
-
result="$(some_command)"
|
|
79
|
-
```
|
|
96
|
+
```bash
|
|
97
|
+
local result
|
|
98
|
+
result="$(some_command)"
|
|
99
|
+
```
|
|
80
100
|
|
|
81
|
-
|
|
101
|
+
- **R24.** Single quotes for literal strings with no expansion needed.
|
|
82
102
|
|
|
83
103
|
## Logging and Output
|
|
84
104
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
105
|
+
- **R25.** Route all messaging through a `log` function.
|
|
106
|
+
- **R26.** `log` must tee to a well-known log file.
|
|
107
|
+
- **R27.** Capture original stdout in `main` with `exec 5>&1`; `log` writes to FD 5.
|
|
108
|
+
- **R28.** Use emoji for key accomplishments.
|
|
109
|
+
- **R29.** Load-bearing functions log both intent and result.
|
|
90
110
|
|
|
91
111
|
## Printing
|
|
92
112
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
113
|
+
- **R30.** Prefer `printf` over `echo`.
|
|
114
|
+
- **R31.** `printf` format string must be single-quoted with explicit format specifiers.
|
|
115
|
+
- **R32.** Use `echo` only with no parameters and no formatting concerns.
|
|
116
|
+
|
|
117
|
+
## Globals
|
|
118
|
+
|
|
119
|
+
- **R33.** Never use the keyword `global`.
|
|
120
|
+
- **R34.** All globals shall be declared after the header comment block and before `function main`.
|
|
121
|
+
- **R35.** All globals shall be immutable after initialization.
|
|
122
|
+
- **R36.** Immutability shall be enforced with `declare -r`; the one exception is a global whose
|
|
123
|
+
initialization requires multi-statement evaluation — in that case declare on one line,
|
|
124
|
+
assign on the next (the variable remains effectively immutable by convention):
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# simple: single-expression initialization
|
|
128
|
+
declare -r DEPLOY_ENV="${DEPLOY_ENV:-staging}"
|
|
129
|
+
|
|
130
|
+
# multi-statement: declare first, then assign
|
|
131
|
+
declare LOG_FILE
|
|
132
|
+
LOG_FILE="$(mktemp /tmp/script.XXXXXX)"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
- **R37.** Environment variables shall be declared as globals with `declare -rx`, using the
|
|
136
|
+
default-value idiom. This applies to both imported and exported environment variables:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
declare -rx SOME_VAR="${SOME_VAR:-default_value}"
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
- **R38.** Global names SHALL be uppercase.
|
|
143
|
+
- **R39.** Never declare a global inside a function scope.
|
|
96
144
|
|
|
97
145
|
## Standard `log` Function Pattern
|
|
98
146
|
|
|
@@ -145,3 +193,26 @@ function validate_args() {
|
|
|
145
193
|
```
|
|
146
194
|
|
|
147
195
|
Do NOT change the call site from `"${@:-}"` to `"$@"` — fix the body only.
|
|
196
|
+
|
|
197
|
+
## Embedded Code (Heredocs)
|
|
198
|
+
|
|
199
|
+
- **R40.** Never embed code blocks (PHP, TOML, ini, shell, conf, etc.) as heredocs inside a bash
|
|
200
|
+
script. Code in heredocs cannot be linted by its native tooling, is invisible to static
|
|
201
|
+
analysis, and causes bash quoting edge cases that are impossible to debug.
|
|
202
|
+
|
|
203
|
+
- **R41.** Extract every embedded code block to a **sibling file** in the same directory as the
|
|
204
|
+
script, with the correct source extension for linting (`.php`, `.toml`, `.sh`, `.conf`,
|
|
205
|
+
etc.). Load it at runtime with `cat`:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
php_script=$(cat "${SCRIPT_DIR}/myscript-step.php")
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
- **R42.** Templates that require runtime variable substitution use `@PLACEHOLDER@` notation and
|
|
212
|
+
`sed` for substitution. Never use an unquoted heredoc (`<< EOF`) for variable injection:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
php_script=$(sed \
|
|
216
|
+
-e "s|@MY_VAR@|${MY_VAR}|g" \
|
|
217
|
+
"${SCRIPT_DIR}/myscript-template.php")
|
|
218
|
+
```
|