@thebassclef/lite 0.1.0 → 0.1.2

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.
Files changed (163) hide show
  1. package/dist/cli.cjs +2 -0
  2. package/dist/cli.js +2 -0
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.js +1 -1
  6. package/package.json +1 -1
  7. package/substrate/.bassclef/lite-manifest.json +999 -92
  8. package/substrate/.claude/hooks/longrun-prep-compounding-axis-check.sh +492 -0
  9. package/substrate/.claude/hooks/longrun-prep-compounding-sequence-check.sh +492 -0
  10. package/substrate/.claude/hooks/pre-commit-gate.sh +1 -2
  11. package/substrate/.claude/hooks/turn-prose-kiss-check.sh +30 -1
  12. package/substrate/.claude/luminaries/glenford-myers.md +230 -0
  13. package/substrate/.claude/luminaries/hunt-thomas.md +115 -0
  14. package/substrate/.claude/luminaries/hyrum-wright.md +94 -0
  15. package/substrate/.claude/luminaries/michael-feathers.md +2 -2
  16. package/substrate/.claude/luminaries/tony-hoare.md +170 -0
  17. package/substrate/.claude/luminaries/vaughn-vernon.md +50 -0
  18. package/substrate/.claude/luminaries/w-edwards-deming.md +158 -0
  19. package/substrate/.claude/rules/accessor-library-discipline.md +138 -0
  20. package/substrate/.claude/rules/adr-discipline.md +120 -0
  21. package/substrate/.claude/rules/api-conventions.md +125 -0
  22. package/substrate/.claude/rules/bootstrap-pair-discipline.md +141 -0
  23. package/substrate/.claude/rules/cold-adopter-harness-discipline.md +129 -0
  24. package/substrate/.claude/rules/compounding-axis-fresh-analysis.md +188 -0
  25. package/substrate/.claude/rules/compounding-sequence-fresh-analysis.md +188 -0
  26. package/substrate/.claude/rules/defensive-bash.md +68 -0
  27. package/substrate/.claude/rules/deferred-actions.md +233 -0
  28. package/substrate/.claude/rules/github-issue-flash-tweet.md +156 -0
  29. package/substrate/.claude/rules/hook-wire-on-author.md +103 -0
  30. package/substrate/.claude/rules/iteration-bet-brief-completeness.md +54 -0
  31. package/substrate/.claude/rules/lite-manifest-schema-change-discipline.md +3 -3
  32. package/substrate/.claude/rules/longrun-prep-plan-doc-compression.md +89 -0
  33. package/substrate/.claude/rules/loop-discipline.md +81 -0
  34. package/substrate/.claude/rules/manual-prod-approval.md +100 -0
  35. package/substrate/.claude/rules/marker-enrichment-discipline.md +99 -0
  36. package/substrate/.claude/rules/mobile-ephemeral-session.md +109 -0
  37. package/substrate/.claude/rules/new-dependency-check.md +51 -0
  38. package/substrate/.claude/rules/option-label-discipline.md +108 -0
  39. package/substrate/.claude/rules/pattern-annotation.md +100 -0
  40. package/substrate/.claude/rules/plain-english-discipline.md +11 -9
  41. package/substrate/.claude/rules/plan-enumeration-needs-value-props.md +211 -0
  42. package/substrate/.claude/rules/pr-title-shape.md +161 -0
  43. package/substrate/.claude/rules/prototype-workflow.md +65 -0
  44. package/substrate/.claude/rules/reserved-skill-names.md +123 -0
  45. package/substrate/.claude/rules/schema-management.md +49 -0
  46. package/substrate/.claude/rules/security.md +37 -0
  47. package/substrate/.claude/rules/skill-composition-declarations.md +124 -0
  48. package/substrate/.claude/rules/skill-description-clarity.md +247 -0
  49. package/substrate/.claude/rules/skill-procedure-step-list.md +137 -0
  50. package/substrate/.claude/rules/stuck-signal-diagnostic.md +140 -0
  51. package/substrate/.claude/rules/substrate-config-schema.md +98 -0
  52. package/substrate/.claude/rules/test-list-discipline.md +175 -0
  53. package/substrate/.claude/rules/test-sufficiency.md +210 -0
  54. package/substrate/.claude/rules/testing-tier-config.md +145 -0
  55. package/substrate/.claude/rules/testing.md +38 -0
  56. package/substrate/.claude/rules/turn-estimate-grounding.md +134 -0
  57. package/substrate/.claude/rules/visual-hierarchy.md +437 -0
  58. package/substrate/.claude/rules/we-dont-break-adopters.md +126 -0
  59. package/substrate/.claude/rules/wu-sequencing-compounds.md +145 -0
  60. package/substrate/.claude/skills/build/SKILL.md +1 -1
  61. package/substrate/.claude/skills/chronicle/SKILL.md +55 -0
  62. package/substrate/.claude/skills/clean-artifacts/SKILL.md +249 -0
  63. package/substrate/.claude/skills/decompose/SKILL.md +1 -1
  64. package/substrate/.claude/skills/diagnose/SKILL.md +1 -1
  65. package/substrate/.claude/skills/feynman/SKILL.md +90 -0
  66. package/substrate/.claude/skills/howdoi/SKILL.md +1 -1
  67. package/substrate/.claude/skills/ia-model/SKILL.md +1 -1
  68. package/substrate/.claude/skills/interaction-design/SKILL.md +1 -1
  69. package/substrate/.claude/skills/interpret-input/SKILL.md +8 -8
  70. package/substrate/.claude/skills/journal/SKILL.md +209 -0
  71. package/substrate/.claude/skills/kiss/SKILL.md +1 -1
  72. package/substrate/.claude/skills/launch/SKILL.md +14 -23
  73. package/substrate/.claude/skills/lean-canvas/SKILL.md +1 -1
  74. package/substrate/.claude/skills/longrun/SKILL.md +45 -8
  75. package/substrate/.claude/skills/luminary/SKILL.md +1 -1
  76. package/substrate/.claude/skills/ogilvy-writing-audit/SKILL.md +1 -1
  77. package/substrate/.claude/skills/onboard-repo/SKILL.md +143 -709
  78. package/substrate/.claude/skills/pattern-review/SKILL.md +1 -1
  79. package/substrate/.claude/skills/personas/SKILL.md +5 -5
  80. package/substrate/.claude/skills/promote/SKILL.md +1 -1
  81. package/substrate/.claude/skills/requirement/SKILL.md +1 -1
  82. package/substrate/.claude/skills/retro/SKILL.md +1 -1
  83. package/substrate/.claude/skills/riff/SKILL.md +1 -1
  84. package/substrate/.claude/skills/roadmap-reconcile/SKILL.md +1 -1
  85. package/substrate/.claude/skills/session-end/SKILL.md +1 -1
  86. package/substrate/.claude/skills/session-log/SKILL.md +3 -3
  87. package/substrate/.claude/skills/skills/SKILL.md +1 -1
  88. package/substrate/.claude/skills/spec/SKILL.md +1 -1
  89. package/substrate/.claude/skills/sprint/SKILL.md +1 -1
  90. package/substrate/.claude/skills/stage/SKILL.md +1 -1
  91. package/substrate/.claude/skills/state-a-problem/SKILL.md +1 -1
  92. package/substrate/.claude/skills/temperance/SKILL.md +1 -1
  93. package/substrate/.claude/skills/use-case/SKILL.md +1 -1
  94. package/substrate/.claude/skills/user-stories/SKILL.md +1 -1
  95. package/substrate/.claude/skills/value-prop/SKILL.md +1 -1
  96. package/substrate/.claude/skills/verify/SKILL.md +1 -1
  97. package/substrate/.claude/skills/visual-review/SKILL.md +503 -0
  98. package/substrate/.claude/skills/whats-the-plan/SKILL.md +202 -0
  99. package/substrate/.claude/skills/whereami/SKILL.md +2 -2
  100. package/substrate/CONTRIBUTING.md +1 -1
  101. package/substrate/README.md +5 -5
  102. package/substrate/lib/prose-scan-boundary.sh +171 -0
  103. package/substrate/lib/tier-check.sh +50 -1
  104. package/substrate/lib/tier-dependency-audit.sh +159 -4
  105. package/substrate/presence/install/bassclef-sync.template.sh +1 -1
  106. package/substrate/scripts/generate-lite-manifest.sh +21 -5
  107. package/substrate/standards/adr-template.md +86 -0
  108. package/substrate/standards/api-conventions/nextjs.md +84 -0
  109. package/substrate/standards/artifact-composition.md +209 -0
  110. package/substrate/standards/bash-hook-safety.md +246 -0
  111. package/substrate/standards/branch-stacking.md +408 -0
  112. package/substrate/standards/code-safety-principles.md +176 -0
  113. package/substrate/standards/composer-prerequisites.md +155 -0
  114. package/substrate/standards/dependency-discipline/cargo.md +39 -0
  115. package/substrate/standards/dependency-discipline/gem.md +43 -0
  116. package/substrate/standards/dependency-discipline/go-mod.md +41 -0
  117. package/substrate/standards/dependency-discipline/npm.md +42 -0
  118. package/substrate/standards/dependency-discipline/pip.md +42 -0
  119. package/substrate/standards/deployment-topology/ec2-tailscale.md +225 -0
  120. package/substrate/standards/deployment-topology.md +69 -0
  121. package/substrate/standards/docs-sync-allowlist.md +4 -4
  122. package/substrate/standards/domain-and-dns.md +145 -0
  123. package/substrate/standards/frontend-stack.md +67 -0
  124. package/substrate/standards/frontmatter-schema.md +154 -0
  125. package/substrate/standards/hook-injection-discipline.md +202 -0
  126. package/substrate/standards/hook-install-class.md +215 -0
  127. package/substrate/standards/input-handler-interface.md +152 -0
  128. package/substrate/standards/lite-manifest-schema-changes.md +60 -0
  129. package/substrate/standards/luminary-matching.md +105 -0
  130. package/substrate/standards/migration-discipline/active-record.md +50 -0
  131. package/substrate/standards/migration-discipline/alembic.md +43 -0
  132. package/substrate/standards/migration-discipline/gorm.md +50 -0
  133. package/substrate/standards/migration-discipline/prisma.md +53 -0
  134. package/substrate/standards/migration-discipline/sqlalchemy.md +51 -0
  135. package/substrate/standards/mobile-ephemeral-session.md +167 -0
  136. package/substrate/standards/model-routing-discipline.md +160 -0
  137. package/substrate/standards/persona-schema.md +229 -0
  138. package/substrate/standards/pluggable-luminaries.md +323 -0
  139. package/substrate/standards/pr-body-discipline.md +115 -0
  140. package/substrate/standards/preview-state-schema.md +189 -0
  141. package/substrate/standards/reserved-skill-names.md +120 -0
  142. package/substrate/standards/scannable-multi-option-output.md +261 -0
  143. package/substrate/standards/sdlc-gates/typescript.md +57 -0
  144. package/substrate/standards/session-board.md +256 -0
  145. package/substrate/standards/state-spine-contract.md +255 -0
  146. package/substrate/standards/steering-hints/kiss-words.md +11 -0
  147. package/substrate/standards/substrate-config-schema.md +267 -0
  148. package/substrate/standards/tier-dependency-analysis.md +1 -1
  149. package/substrate/standards/tier-tag-schema.md +1 -1
  150. package/substrate/standards/two-layer-config.md +99 -0
  151. package/substrate/standards/use-case-format.md +292 -0
  152. package/substrate/standards/user-story-invest.md +268 -0
  153. package/substrate/standards/velocity-and-appetite.md +229 -0
  154. package/substrate/standards/voice-input-pattern.md +119 -0
  155. package/substrate/standards/worktree-management.md +211 -0
  156. package/substrate/templates/chronicle-template.md +75 -0
  157. package/substrate/templates/memory-proposal-template.md +77 -0
  158. package/substrate/templates/persona-template.md +200 -0
  159. package/substrate/templates/pr-faq.md +45 -0
  160. package/substrate/templates/secret-rotation-template.md +162 -0
  161. package/substrate/templates/spec-template.md +131 -0
  162. package/substrate/templates/use-case-template.md +194 -0
  163. package/substrate/templates/user-story-template.md +107 -0
@@ -0,0 +1,246 @@
1
+ ---
2
+ tier: lite
3
+ description: "This standard codifies the defensive coding discipline for bassclef's bash hooks (.claude/hooks/.sh) + supporting bash scripts (scripts/.sh, lib/*.sh)."
4
+ ---
5
+
6
+ # Bash Hook Safety Standard
7
+
8
+ This standard codifies the defensive coding discipline for bassclef's bash hooks (`.claude/hooks/*.sh`) + supporting bash scripts (`scripts/*.sh`, `lib/*.sh`). It is the bash-specific instantiation of `standards/code-safety-principles.md`.
9
+
10
+ ## Why this standard exists
11
+
12
+ Bassclef's hooks are bash scripts that fire on every tool call. They sit between the operator's intent and every Edit / Write / Bash invocation. A bug in a hook is amplified: every session in every consumer repo runs the hook on every tool call. Three months of substrate-evolution surfaced four same-shape failures (bassclef#1027, #1054, #1058, #1060) — all in bash code, all caught after PR open, all preventable by hooks at the moment of failure.
13
+
14
+ The existing `.claude/rules/security.md` is scoped to web-application source files (`.ts`, `.tsx`, `.json`, `.env`, `Dockerfile`). It does not cover `.sh`. This standard closes that gap with hook-specific discipline anchored to Saltzer & Schroeder's 8 principles per `@luminary saltzer-schroeder`.
15
+
16
+ ## Anchors
17
+
18
+ - `@luminary saltzer-schroeder` — the 8 protection-mechanism principles this standard instantiates
19
+ - `@luminary john-ousterhout` — Deep modules + define-errors-out-of-existence framing for hook interface design
20
+ - `@luminary kent-beck` — TDD discipline for hooks (Tier 0 per `.claude/rules/testing-tier-config.md`)
21
+ - `standards/code-safety-principles.md` — language-agnostic principles this standard instantiates for bash
22
+
23
+ ## The seven disciplines
24
+
25
+ ### 1. Strict mode at the top
26
+
27
+ Every bash hook + script starts with:
28
+
29
+ ```bash
30
+ #!/bin/bash
31
+ set -euo pipefail
32
+ ```
33
+
34
+ - `-e` exits on any command failure
35
+ - `-u` exits on undefined variable use
36
+ - `-o pipefail` makes pipelines fail if any stage fails (default is "last stage's exit code only")
37
+
38
+ INSTEAD of "I'll handle errors explicitly where they matter": strict mode makes errors loud by default. Explicit error handling overrides apply per-command via `command || true` or `command 2>/dev/null` with intent.
39
+
40
+ **Why mandatory**: bash's default behavior is "continue on error, use the empty string for undefined variables." Bassclef's hooks run in environments where silent failure causes incorrect downstream state. PR #1054 demonstrated this: `log()` function's quiet-mode toggle behavior depended on an unset variable being treated as false; strict mode would have surfaced the bug at write-time.
41
+
42
+ ### 2. Quote every variable expansion
43
+
44
+ Every `$var` or `${var}` reference inside a string context gets double-quoted:
45
+
46
+ ```bash
47
+ # Correct
48
+ file_path="$1"
49
+ if [[ -f "$file_path" ]]; then
50
+ cat "$file_path"
51
+ fi
52
+
53
+ # Wrong — word-splits on spaces; glob-expands; breaks on empty
54
+ if [[ -f $1 ]]; then
55
+ cat $1
56
+ fi
57
+ ```
58
+
59
+ INSTEAD of "I know this variable will never contain a space": quote universally. The cost of consistent quoting is zero; the cost of one missed quote in a path with a space is silent corruption.
60
+
61
+ **Exception**: `local` declarations of arrays + variables that intentionally word-split (rare; document with an inline comment).
62
+
63
+ ### 3. `local` in functions
64
+
65
+ Every variable declared inside a function uses `local`:
66
+
67
+ ```bash
68
+ parse_config() {
69
+ local config_path="$1"
70
+ local mode="${2:-default}"
71
+ # ...
72
+ }
73
+ ```
74
+
75
+ INSTEAD of relying on bash's default of global scope: `local` keeps function-internal state inside the function. A function that pollutes parent scope is a defect waiting to surface — the bug appears when a different caller reuses the same variable name.
76
+
77
+ ### 4. Trap EXIT for cleanup
78
+
79
+ Hooks that create temporary files, lock files, or background processes register a trap for cleanup:
80
+
81
+ ```bash
82
+ TMP_DIR=$(mktemp -d)
83
+ trap 'rm -rf "$TMP_DIR"' EXIT
84
+
85
+ # ... use $TMP_DIR ...
86
+ # Cleanup happens automatically on any exit path
87
+ ```
88
+
89
+ INSTEAD of "I'll clean up at the end of the happy path": cleanup at every exit path including error paths and SIGINT. `trap '...' EXIT` runs the cleanup regardless of how the script exits.
90
+
91
+ For hooks specifically: clean up `/tmp` markers, release `flock`s, kill background `tee`s.
92
+
93
+ ### 5. Explicit return codes
94
+
95
+ Hook exit codes carry meaning. Bassclef's hook protocol:
96
+
97
+ - `0` — allow the tool call (default)
98
+ - `1` — generic non-blocking failure (usually a bug in the hook itself; logged but doesn't block)
99
+ - `2` — BLOCK the tool call (the operator-facing surface)
100
+ - `>2` — reserved; treat as `1`
101
+
102
+ Every hook ends with explicit `exit 0` (or `exit 2` on block). Relying on bash's "exit code of last command" default produces silent breakage when refactors reorder statements.
103
+ INSTEAD of implicit "last-command exit code": explicit `exit N` at every termination point. Hook readers should see the exit value next to the BLOCK decision.
104
+
105
+ ### 6. Glob-tolerance via fallthrough
106
+
107
+ Bash's `for f in pattern/*` fails silently when the pattern matches nothing — the loop body runs once with the literal pattern string. The shape that tolerates empty match:
108
+
109
+ ```bash
110
+ shopt -s nullglob # empty match = empty loop, not literal pattern
111
+
112
+ for f in "$dir"/*.json; do
113
+ process "$f"
114
+ done
115
+
116
+ shopt -u nullglob # restore default
117
+ ```
118
+
119
+ INSTEAD of assuming a glob always matches: `nullglob` makes empty-match an empty loop. The hook handles "no files" naturally.
120
+
121
+ For one-off patterns: `[[ ${#files[@]} -gt 0 ]]` after assigning to an array via `files=("$dir"/*.json)`.
122
+
123
+ ### 7. Audit emission at every exit path
124
+
125
+ Every hook emits a trace record before exit:
126
+
127
+ ```bash
128
+ source "$(dirname "$0")/../lib/trace-helper.sh"
129
+
130
+ # ... hook body ...
131
+
132
+ trace_record "hook-name" "decision" "context"
133
+ exit 0
134
+ ```
135
+
136
+ INSTEAD of audit-only-on-block: audit on allow + block + override usage + skip path. Post-incident reconstruction needs the full trace.
137
+
138
+ ## Common idioms
139
+
140
+ ### `find | head` under `set -euo pipefail` — SIGPIPE kills the script
141
+
142
+ Under `set -euo pipefail`, piping `find` into `head -N` can silently kill the script when `find` produces more than N results. `head` closes stdin after N lines, `find` receives SIGPIPE from the closed pipe, `find` exits 141, and `pipefail` promotes 141 to the pipeline exit code. `set -e` then kills the script silently.
143
+
144
+ Anti-pattern:
145
+
146
+ ```bash
147
+ set -euo pipefail
148
+ broken=$(find . -type l \! -exec test -e {} \; -print 2>/dev/null | head -10)
149
+ # ^ script may exit here with no error output when find produces >10 results
150
+ ```
151
+
152
+ INSTEAD — Option 5d (preferred): bash-native array truncation. No pipe means no SIGPIPE.
153
+
154
+ ```bash
155
+ set -euo pipefail
156
+ readarray -t all_broken < <(find . -type l \! -exec test -e {} \; -print 2>/dev/null)
157
+ broken="$(printf '%s\n' "${all_broken[@]:0:10}")"
158
+ ```
159
+
160
+ Three alternatives, each with tradeoffs:
161
+
162
+ - **SIGPIPE catch** — `find ... 2>/dev/null | head -10 || true`. Swallows all pipeline errors including real ones; less specific than the SIGPIPE-only class.
163
+ - **No-pipe restructure** — `find ... -print | while read; do ... done` with an explicit counter break. More lines; keeps structural-code style.
164
+ - **Fallthrough with `|| true`** — cheapest to write; hides every non-zero pipeline exit; least safe under production loads.
165
+
166
+ Option 5d preserves the structural read shape AND removes the SIGPIPE vector at the source. Strictly stronger error propagation than SIGPIPE catch — the pipe never exists, so no pipe error class can fire.
167
+
168
+ Anchors:
169
+
170
+ - `@luminary linus-torvalds` — adopter contract on observable script behavior; silent-exit is worse than loud-fail
171
+ - `@luminary michael-feathers` — characterization test pins the FAIL contract (find-produces-N-plus-1 case) before the fix ships
172
+
173
+ Per sunj-labs/bassclef-upstream#912. Cure 4 shipped inside the #901 install-corrupts-tree epic; PR #892 carries the exemplar. See the ticket body for the second-AI consultation write-up + the linus lens shift from Option 1 (SIGPIPE catch) to Option 5d.
174
+
175
+ ## The Saltzer & Schroeder stack-translation
176
+
177
+ This standard's seven disciplines instantiate Saltzer & Schroeder's 8 principles (per `@luminary saltzer-schroeder` stack-translation table):
178
+
179
+ | Saltzer & Schroeder principle | Bash discipline |
180
+ |---|---|
181
+ | Economy of mechanism | Hook body ≤200 lines; helpers in `lib/*.sh` |
182
+ | Fail-safe defaults | Strict mode `set -euo pipefail`; default `exit 2` on uncertainty |
183
+ | Complete mediation | PreToolUse fires on every matching call |
184
+ | Open design | Triggers documented in `docs/hooks-index.md`; BLOCK banners name the read path |
185
+ | Separation of privilege | Destructive hooks require multiple independent signals |
186
+ | Least privilege | `cd` only when needed; scope git/file ops to specific paths |
187
+ | Least common mechanism | Per-hook trace; per-hook test; no global state between hooks |
188
+ | Psychological acceptability | `SKIP_<HOOK>=1` per-call override; logged via trace-helper |
189
+
190
+ ## Shellcheck
191
+
192
+ Every bash hook + script passes `shellcheck` (current version). Tier 0 strict-TDD discipline per `.claude/rules/testing-tier-config.md` extends to shellcheck: an edit that introduces a shellcheck finding fails CI per Phase X-4's shellcheck workflow.
193
+
194
+ **Local install**:
195
+
196
+ | OS | Command |
197
+ |---|---|
198
+ | macOS | `brew install shellcheck` |
199
+ | Ubuntu/Debian | `apt install shellcheck` |
200
+ | Fedora | `dnf install ShellCheck` |
201
+ | Arch | `pacman -S shellcheck` |
202
+ | Container builds | Add `shellcheck` to the base image |
203
+
204
+ **Local run**:
205
+
206
+ ```bash
207
+ shellcheck .claude/hooks/*.sh scripts/*.sh lib/*.sh
208
+ ```
209
+
210
+ **Phase X-4** ships the CI workflow that runs shellcheck on every PR + a baseline allowlist (`.claude/hooks/shellcheck-allowlist.txt`) with 60-day grace through 2026-08-17 for existing findings.
211
+
212
+ ## What this standard does NOT do
213
+
214
+ - Does not cover language interop (bash calling Python, Python calling bash) — handle at the boundary
215
+ - Does not specify which utilities to use (`gh` vs `curl` vs `jq` vs `yq`) — that is a stack-choice question
216
+ - Does not cover bash 4+ features absent from macOS's default install (`${var^^}` uppercase conversion, associative arrays) — bash 3.2 compatibility is the baseline.
217
+ INSTEAD of using a bash 4+ feature implicitly: opt in via explicit shebang line (`#!/usr/bin/env bash` with version check) or fallback shape.
218
+
219
+ ## When this standard fires
220
+
221
+ | Surface | Check |
222
+ |---|---|
223
+ | New hook in `.claude/hooks/` | All 7 disciplines + shellcheck-clean + sibling test (Tier 0) |
224
+ | New script in `scripts/` | All 7 disciplines + shellcheck-clean (Tier 1) |
225
+ | Edit to existing hook | Same — Phase A's testing-tier hook BLOCKs without test mtime ≤ source mtime |
226
+ | Code review on PR touching `.sh` | Reviewer applies the 7 disciplines |
227
+ | `/security-review` skill invocation | Skill cites this standard for any `.sh` change |
228
+
229
+ ## Refs
230
+
231
+ - `@luminary saltzer-schroeder` — anchor
232
+ - `@luminary john-ousterhout` — Deep modules framing for hook interface design
233
+ - `@luminary kent-beck` — TDD anchor; Tier 0 strict-TDD discipline applies
234
+ - `standards/code-safety-principles.md` — language-agnostic principles this standard instantiates
235
+ - `.claude/rules/destructive-operations.md` — agent self-assessment companion
236
+ - `.claude/rules/security.md` — web-layer rule (`.ts/.tsx/.json/.env/Dockerfile` scope); this standard extends to bash
237
+ - `.claude/rules/testing-tier-config.md` — Tier 0 strict-TDD discipline for hooks
238
+ - `.claude/rules/defensive-bash.md` — companion methodology rule (auto-loaded; points here)
239
+ - `.github/workflows/shellcheck.yml` (Phase X-4) — CI enforcement
240
+ - bassclef#1054 — `log()` quiet-mode bug (strict-mode would have caught at write-time)
241
+ - bassclef#1058 — auto-save scope hole (fail-safe-defaults + least-privilege violation)
242
+ - docs/iteration-bets/2026-06-17a-secure-coding-foundations-longrun.md — goal authoring this standard
243
+
244
+ ## Retirement
245
+
246
+ This standard retires only if bassclef migrates off bash for its hook implementation language (e.g., adopting Python or Go for hooks). The 7 disciplines themselves remain valid; the language-specific instantiation changes.
@@ -0,0 +1,408 @@
1
+ ---
2
+ tier: lite
3
+ description: Stacked atomic branches are the default for any iteration with N≥2 issues.
4
+ ---
5
+
6
+ # Branch Stacking Standard
7
+
8
+ ## Default for multi-issue iterations
9
+
10
+ **Stacked atomic branches are the default** for any iteration with N≥2
11
+ issues. One branch per issue, one PR per branch, each independently
12
+ reviewable and revertable. The `bundle` flag at `/autonomous start`
13
+ overrides this; see `.claude/rules/pr-strategy.md` for when bundling is
14
+ acceptable.
15
+
16
+ This is codified — the boot sequence does not ask "stacked or bundled?"
17
+ per session.
18
+
19
+ ## When to use
20
+
21
+ Autonomous or multi-step sessions where work spans multiple logical units
22
+ that should be independently reviewable and revertable. This extends the
23
+ trunk-based model in `.claude/rules/branching.md` for multi-branch work.
24
+
25
+ Use this standard when:
26
+ - An agent (or agents) will produce more than one PR in a session
27
+ - Work has a natural decomposition into independent or sequenced units
28
+ - You want the ability to accept some branches and reject others
29
+
30
+ ---
31
+
32
+ ## Core principle
33
+
34
+ **Each branch = one intent.** A branch does one thing: "Add the data model"
35
+ or "Build the list view" — not "Add data model and list view and API routes."
36
+
37
+ If you can't describe a branch's intent in one sentence, split it.
38
+
39
+ ---
40
+
41
+ ## Stack manifest
42
+
43
+ Before building, the agent writes a manifest declaring the planned stack.
44
+ This is the iteration's work breakdown and the Orchestrator's routing map.
45
+
46
+ **Location**: `docs/branch-stacks/YYYY-MM-DD-slug.md` in the working repo.
47
+
48
+ ### Format
49
+
50
+ ```markdown
51
+ # Branch Stack: [name]
52
+
53
+ **Issue**: #NNN
54
+ **Iteration goal**: [path to iteration goal]
55
+ **Execution mode**: sequential | parallel | mixed
56
+ **Date**: YYYY-MM-DD
57
+
58
+ ## Branches
59
+
60
+ | # | Branch | Intent | Depends on | Parallel-safe |
61
+ |---|--------|--------|-----------|---------------|
62
+ | 1 | feature/NNN-stack-1-data-model | CRM schema + migration | none | yes |
63
+ | 2 | feature/NNN-stack-2-api-routes | CRUD API for entities | stack-1 | no |
64
+ | 3 | feature/NNN-stack-3-list-view | List page UI | stack-1 | yes |
65
+ | 4 | feature/NNN-stack-4-detail-view | Detail page UI | stack-3 | no |
66
+
67
+ ## Parallel groups
68
+
69
+ - Group A (no dependencies): stack-1
70
+ - Group B (after stack-1 merged): stack-2, stack-3 (parallel)
71
+ - Group C (after stack-3 merged): stack-4
72
+
73
+ ## Acceptance criteria per branch
74
+
75
+ ### stack-1: data-model
76
+ - [ ] Prisma schema with migration file
77
+ - [ ] prisma generate succeeds
78
+ - [ ] Seed script if needed
79
+
80
+ ### stack-2: api-routes
81
+ - [ ] CRUD endpoints with tests
82
+ - [ ] Auth middleware applied
83
+
84
+ [... etc]
85
+ ```
86
+
87
+ ---
88
+
89
+ ## Execution modes
90
+
91
+ ### Sequential (supervised sessions)
92
+
93
+ One agent, one branch at a time. Human checkpoint between branches.
94
+
95
+ ```
96
+ Agent builds stack-1
97
+ → PR opened → human reviews → approved → merge to main
98
+ Agent builds stack-2
99
+ → PR opened → human reviews → approved → merge to main
100
+ [...]
101
+ ```
102
+
103
+ **Branch base**: Each branch is based on `main` (after the previous branch
104
+ is merged). This means each branch includes the accumulated work of prior
105
+ branches but is independently reviewable as a PR.
106
+
107
+ **Human checkpoint**: After each branch, the agent opens a PR and stops.
108
+ Human reviews. Approved → merge → agent proceeds. Rejected → agent adjusts
109
+ or the branch is discarded.
110
+
111
+ ### Parallel (Cherny's worktree model)
112
+
113
+ Multiple agents in git worktrees (`claude -w`), each on its own branch,
114
+ building simultaneously. No dirty tree conflicts — each worktree is an
115
+ isolated copy of the repo.
116
+
117
+ ```
118
+ Orchestrator reads stack manifest
119
+ → Independent branches fan out to parallel worktree agents
120
+ → Dependent branches queue behind prerequisites
121
+
122
+ Agent A (worktree 1): stack-2-api-routes
123
+ Agent B (worktree 2): stack-3-list-view
124
+ [building simultaneously, no conflicts]
125
+
126
+ Each agent opens its own PR when done
127
+ Human reviews PRs after session (or Reviewer agent evaluates)
128
+ ```
129
+
130
+ **Branch base for parallel work**: Independent branches base off `main`.
131
+ When a branch depends on another (stack-4 depends on stack-3), it bases
132
+ off the dependency branch. The manifest makes this explicit.
133
+
134
+ **Worktree lifecycle**:
135
+ - Created by Orchestrator (or human) when fanning out parallel work
136
+ - Each worktree gets one branch, one agent
137
+ - On completion: agent opens PR, worktree is available for cleanup
138
+ - Merged worktrees: deleted automatically after merge
139
+ - Abandoned worktrees (no commits after 2 hours): flagged for cleanup
140
+ - Worktree cleanup command: `git worktree prune`
141
+
142
+ **Conflict detection**: If two parallel agents modify the same file,
143
+ the Orchestrator flags this to the human. Do not attempt auto-resolution —
144
+ file-level conflicts in parallel branches indicate a decomposition problem
145
+ (the branches weren't truly independent). Fix the manifest, not the merge.
146
+
147
+ ### Mixed (common in practice)
148
+
149
+ Some branches are independent (parallelize), some are dependent (sequence).
150
+ The manifest's dependency graph determines which is which.
151
+
152
+ ```
153
+ Group A: stack-1 (alone, must complete first)
154
+ Group B: stack-2 + stack-3 (parallel, both depend on stack-1)
155
+ Group C: stack-4 (sequential, depends on stack-3)
156
+ ```
157
+
158
+ The Orchestrator executes Group A, waits for merge, fans out Group B in
159
+ parallel worktrees, waits for Group B merges, then executes Group C.
160
+
161
+ ---
162
+
163
+ ## Graceful unwind
164
+
165
+ If branch N is rejected:
166
+ - **Branches that depend on N** (per manifest): also discarded
167
+ - **Branches independent of N**: survive untouched
168
+ - **Agent never needs to untangle** — independence is designed in, not retrofitted
169
+
170
+ The dependency graph in the manifest makes this mechanical:
171
+ 1. Look up what depends on N (transitively)
172
+ 2. Discard N and all its dependents
173
+ 3. Everything else is unaffected
174
+
175
+ This is why independence is the default and dependencies are the exception.
176
+
177
+ ---
178
+
179
+ ## Squash merge — two protocols
180
+
181
+ When merging stacked PRs whose bases are other feature branches
182
+ (Graphite-style stack — common when the agent declares "depends on"
183
+ in the manifest), GitHub auto-closes dependent PRs the moment the
184
+ base branch is deleted. ("Cannot change the base branch of a closed
185
+ pull request.") Recovery requires re-creating each PR. Costs ~3-8
186
+ turns per stack. Observed 2026-04-18c (#178/#179/#180 cascade).
187
+
188
+ Two protocols handle this. Pick by **when** you can act:
189
+
190
+ | Protocol | When to use | Trade-off |
191
+ |----------|-------------|-----------|
192
+ | **Retarget-before-merge** (preferred) | You have access to the dependent PRs before merging stack-N | Cleanest — no auto-close, no recovery cost |
193
+ | **Rebase-after-merge** (fallback) | Auto-close already happened, OR you forgot to retarget | Recoverable but expensive — per-PR rebase + re-create |
194
+
195
+ If the iteration uses **base = main** for every branch (independence
196
+ designed in via manifest, no Graphite-style chaining), neither cascade
197
+ fires — but stacked PRs with declared dependencies almost always use
198
+ chained bases.
199
+
200
+ ### Retarget-before-merge (preferred)
201
+
202
+ **Before** squash-merging PR N in a stack:
203
+
204
+ 1. **Identify dependent PRs** — those whose `baseRefName` equals
205
+ PR N's `headRefName`. Use the helper script:
206
+
207
+ ```bash
208
+ scripts/check-stack-dependents.sh <PR-N-number>
209
+ ```
210
+
211
+ This lists open PRs that will auto-close if PR N merges and its
212
+ branch is deleted.
213
+
214
+ 2. **Retarget each dependent to main** (or the stack root):
215
+
216
+ ```bash
217
+ gh pr edit <dependent-PR-number> --base main
218
+ ```
219
+
220
+ The dependent PR survives the merge. Its diff may now show extra
221
+ commits (the work from PR N) until the dependent is rebased — but
222
+ it stays open.
223
+
224
+ 3. **Merge PR N**:
225
+
226
+ ```bash
227
+ gh pr merge <PR-N-number> --squash --delete-branch
228
+ ```
229
+
230
+ Auto-close cascade does not fire because no other PR has PR N's
231
+ branch as base anymore.
232
+
233
+ 4. **(Optional) Rebase dependent on top of merged main** if you want
234
+ the diff to show only the dependent's own work:
235
+
236
+ ```bash
237
+ git checkout <dependent-branch>
238
+ git pull origin main --rebase
239
+ git push --force-with-lease
240
+ ```
241
+
242
+ 5. **Repeat** for the next PR in stack order.
243
+
244
+ #### Why this works
245
+
246
+ The auto-close trigger is "base branch deleted on a closed/merged PR
247
+ where another PR depends on it." Retarget breaks the dependency
248
+ *before* the delete fires. The dependents continue tracking main; their
249
+ content is unchanged.
250
+
251
+ ### Rebase-after-merge (fallback)
252
+
253
+ Use only if the cascade has already fired (dependent PRs show
254
+ "Closed — base branch was deleted") OR you forgot the retarget step.
255
+
256
+ After squash-merging PR N in a stack (without prior retarget):
257
+
258
+ 1. **Pull updated main**: `git checkout main && git pull`
259
+ 2. **Rebase the next branch**: `git checkout feature/next && git rebase main`
260
+ 3. **Force push with lease**: `git push --force-with-lease`
261
+ (safe force push — fails if someone else pushed to the branch)
262
+ 4. **If GitHub auto-closed the downstream PR**: re-create it pointing
263
+ at main, since the closed PR cannot be reopened with a new base:
264
+ ```bash
265
+ gh pr create --base main --head feature/next \
266
+ --title "[same as closed PR]" \
267
+ --body "[same as closed PR, plus link to closed PR for context]"
268
+ ```
269
+ Or merge locally:
270
+ ```bash
271
+ git checkout main
272
+ git merge --squash feature/next
273
+ git commit -m "feat: description"
274
+ ```
275
+ 5. **Repeat** for remaining branches in stack order
276
+
277
+ #### Why this happens
278
+
279
+ Squash merge creates a single new commit on main that replaces all
280
+ commits from the branch. The next branch's base commits no longer exist
281
+ on main — they were squashed into one. GitHub sees a conflict because
282
+ the branch history diverged from main. Combined with branch deletion,
283
+ the auto-close cascade fires for any dependent PR.
284
+
285
+ ### Auto-save checkpoint conflicts
286
+
287
+ If the auto-save launchd timer committed checkpoint files (e.g.,
288
+ `.test-baseline`) on feature branches during construction, these
289
+ checkpoints will conflict during rebase. Use `git rebase --skip` to
290
+ drop the checkpoint commits — they're not part of the real work.
291
+
292
+ The auto-save timer should be paused during autonomous sessions
293
+ (SESSION_LOCK check, see #62).
294
+
295
+ ### Alternative: regular merge commits
296
+
297
+ Using regular merge commits (no squash) preserves branch relationships
298
+ and avoids the rebase cascade. Trade-off: messier main history with
299
+ merge commits. Squash is still preferred for a clean main, but the
300
+ rebase step is the cost.
301
+
302
+ ---
303
+
304
+ ## Naming convention
305
+
306
+ ```
307
+ feature/ISSUE-NNN-stack-N-short-description
308
+ ```
309
+
310
+ Examples:
311
+ - `feature/47-stack-1-crm-data-model`
312
+ - `feature/47-stack-2-crm-api-routes`
313
+ - `feature/47-stack-3-crm-list-view`
314
+
315
+ The `stack-N` segment makes merge ordering obvious in `git branch` output
316
+ and GitHub's PR list.
317
+
318
+ ---
319
+
320
+ ## Per-branch verification
321
+
322
+ Each branch passes `/verify` independently before the PR is opened.
323
+ CI must pass on each branch in isolation. A branch that only works when
324
+ combined with another branch is not atomic — fix the decomposition.
325
+
326
+ Exception: Dependent branches (stack-4 depends on stack-3) are verified
327
+ against their base branch, not against main alone.
328
+
329
+ ---
330
+
331
+ ## Automatic stack creation (via /autonomous)
332
+
333
+ When `/autonomous start` enters Construction, the Orchestrator runs a
334
+ five-step automation sequence (defined in strategy/autonomous-boot-sequence.md Step 9).
335
+ This section documents what the automation produces so agents know what
336
+ to expect when picking up a stack mid-session.
337
+
338
+ ### What gets created automatically
339
+
340
+ **1. GitHub issues** — one per task, created via `gh issue create`.
341
+ Each issue includes: what, acceptance criteria, and a ref to the
342
+ iteration goal. Issue numbers are assigned before any branch is created.
343
+
344
+ **2. Stack manifest** — written to `docs/branch-stacks/YYYY-MM-DD-slug.md`
345
+ before any branch is created. The manifest is committed first so all
346
+ agents can find it.
347
+
348
+ **3. Branches** — created from main in dependency order, named
349
+ `feature/ISSUE-NNN-stack-N-short-description`.
350
+
351
+ ### Automation invariants
352
+
353
+ - The manifest is always committed before branches are created.
354
+ If you see a branch without a manifest, the automation was interrupted.
355
+ - Issue numbers in the manifest are real GitHub issue numbers.
356
+ A placeholder NNN means the automation did not complete — finish it.
357
+ - Every branch bases from main, not from the previous branch.
358
+ Stacked rebases follow the squash-merge protocol in this document.
359
+ - The manifest is updated after each PR opens (status: pr-open) and
360
+ after each merge (status: merged). Pending = not yet started.
361
+
362
+ ### Resuming an interrupted stack
363
+
364
+ If the automation was interrupted (crash, time budget exhausted, pause),
365
+ the next session reads:
366
+
367
+ 1. `docs/branch-stacks/YYYY-MM-DD-slug.md` — what's done, what's pending
368
+ 2. `docs/whereami.md` — branch_stack field points at the manifest
369
+ 3. GitHub issues — open issues with no linked PR are pending branches
370
+
371
+ Resume from the first branch with status "pending" in the manifest.
372
+ Do not re-create issues or rewrite the manifest unless it is corrupted.
373
+
374
+ ### Manual stack creation (non-autonomous sessions)
375
+
376
+ The standard still applies for manually created stacks — write the
377
+ manifest first, create issues, then branches. The automation does this
378
+ programmatically. Format and invariants are identical.
379
+
380
+ ---
381
+
382
+ ## Integration with process framework
383
+
384
+ | Concept | Maps to |
385
+ |---------|---------|
386
+ | Branch stack | Iteration |
387
+ | Stack manifest | Iteration work breakdown |
388
+ | Individual branch | Task within iteration |
389
+ | Iteration goal | Governs the whole stack |
390
+ | Parallel groups | Orchestrator routing |
391
+
392
+ Individual branches do not need their own iteration goals. The stack-level
393
+ bet covers time budget, risk, and value for the entire body of work.
394
+
395
+ ---
396
+
397
+ ## Relation to existing branching rules
398
+
399
+ This standard extends `.claude/rules/branching.md`. All existing rules
400
+ still apply:
401
+ - `main` is always deployable
402
+ - Every branch ties to a GitHub Issue (stack branches share the parent issue)
403
+ - Squash merge to main
404
+ - No branches older than 5 days
405
+ - Force push on feature branches is fine, never on main
406
+
407
+ The addition: branches can now be explicitly sequenced and parallelized
408
+ via a stack manifest, with graceful unwind as a first-class property.