@thebassclef/lite 1.1.0 → 1.2.0-alpha.1

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 (135) hide show
  1. package/dist/cli.cjs +258 -63
  2. package/dist/cli.js +258 -63
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/lite/.claude/bassclef-configs.jsonc +56 -0
  7. package/dist/lite/.claude/bassclef-orientation.md +120 -0
  8. package/dist/lite/.claude/hooks/adr-discipline-check.sh +132 -0
  9. package/dist/lite/.claude/hooks/atomic-pr-check.sh +169 -0
  10. package/dist/lite/.claude/hooks/auto-save-idle.sh +51 -0
  11. package/dist/lite/.claude/hooks/bassclef-sync.sh +51 -13
  12. package/dist/lite/.claude/hooks/bet-doc-gate.sh +283 -0
  13. package/dist/lite/.claude/hooks/bug-diagnosis.sh +64 -0
  14. package/dist/lite/.claude/hooks/destructive-command-guard.sh +136 -0
  15. package/dist/lite/.claude/hooks/file-rename-discipline-check.sh +147 -0
  16. package/dist/lite/.claude/hooks/hook-classifier-canary.sh +158 -0
  17. package/dist/lite/.claude/hooks/kiss-default-check.sh +129 -0
  18. package/dist/lite/.claude/hooks/multi-tenancy-check.sh +83 -0
  19. package/dist/lite/.claude/hooks/oo-ad-entry-point-check.sh +216 -0
  20. package/dist/lite/.claude/hooks/operator-private-path-check.sh +203 -0
  21. package/dist/lite/.claude/hooks/pattern-annotation-validate.sh +142 -0
  22. package/dist/lite/.claude/hooks/plain-english-steering.sh +15 -1
  23. package/dist/lite/.claude/hooks/post-write-trace.sh +66 -0
  24. package/dist/lite/.claude/hooks/pr-body-scrub-check.sh +446 -0
  25. package/dist/lite/.claude/hooks/pre-commit-identifier-leak-scrub.sh +137 -0
  26. package/dist/lite/.claude/hooks/pre-gh-pr-body-scrub.sh +192 -0
  27. package/dist/lite/.claude/hooks/pre-gh-pr-title-scrub.sh +172 -0
  28. package/dist/lite/.claude/hooks/role-entry-gate.sh +115 -0
  29. package/dist/lite/.claude/hooks/save-state.sh +268 -0
  30. package/dist/lite/.claude/hooks/session-reflection.d/08-settings-drift.sh +8 -1
  31. package/dist/lite/.claude/hooks/session-reflection.d/10-abrupt-stop-recovery.sh +52 -0
  32. package/dist/lite/.claude/hooks/session-reflection.d/55-orientation-gate.sh +40 -4
  33. package/dist/lite/.claude/hooks/session-reflection.d/80-hook-heartbeat-check.sh +12 -5
  34. package/dist/lite/.claude/hooks/session-reflection.d/81-hook-manifest-staleness.sh +18 -2
  35. package/dist/lite/.claude/hooks/session-reflection.d/91-npm-version-drift.sh +54 -0
  36. package/dist/lite/.claude/hooks/session-reflection.d/tests/10-abrupt-stop-recovery.test.sh +95 -0
  37. package/dist/lite/.claude/hooks/session-reflection.sh +62 -15
  38. package/dist/lite/.claude/hooks/tool-failure-diagnosis.sh +50 -0
  39. package/dist/lite/.claude/luminaries/andrej-karpathy.md +165 -0
  40. package/dist/lite/.claude/luminaries/dave-gray.md +155 -0
  41. package/dist/lite/.claude/luminaries/gary-klein.md +166 -0
  42. package/dist/lite/.claude/luminaries/kaoru-ishikawa.md +162 -0
  43. package/dist/lite/.claude/luminaries/lean-product-canvas.md +33 -0
  44. package/dist/lite/.claude/luminaries/luke-wroblewski.md +224 -0
  45. package/dist/lite/.claude/luminaries/ryan-singer.md +113 -0
  46. package/dist/lite/.claude/luminaries/steve-krug.md +168 -0
  47. package/dist/lite/.claude/luminaries/tony-ulwick.md +134 -0
  48. package/dist/lite/.claude/rules/arc-level-orientation-before-leaf.md +60 -0
  49. package/dist/lite/.claude/rules/architect-review-discipline.md +71 -0
  50. package/dist/lite/.claude/rules/auto-fix-criteria.md +126 -0
  51. package/dist/lite/.claude/rules/bassclef-hook-drift-blocker.md +144 -0
  52. package/dist/lite/.claude/rules/bassclef-models-discovery.md +107 -0
  53. package/dist/lite/.claude/rules/bassclef-workflow-parity.md +104 -0
  54. package/dist/lite/.claude/rules/cost-governance.md +154 -0
  55. package/dist/lite/.claude/rules/file-rename-discipline.md +95 -0
  56. package/dist/lite/.claude/rules/mechanism-fidelity.md +88 -0
  57. package/dist/lite/.claude/rules/prototype-production-coupling.md +125 -0
  58. package/dist/lite/.claude/rules/quick-fix-criteria.md +90 -0
  59. package/dist/lite/.claude/rules/session-artifacts.md +30 -0
  60. package/dist/lite/.claude/rules/sibling-smoke-after-substrate-change.md +107 -0
  61. package/dist/lite/.claude/rules/substrate-as-system.md +67 -0
  62. package/dist/lite/.claude/rules/usability.md +22 -0
  63. package/dist/lite/.claude/settings.json +20 -0
  64. package/dist/lite/.claude/skills/agent-research-spawn/SKILL.md +100 -0
  65. package/dist/lite/.claude/skills/architect-review/SKILL.md +201 -0
  66. package/dist/lite/.claude/skills/auto-fix/SKILL.md +297 -0
  67. package/dist/lite/.claude/skills/autonomous/SKILL.md +300 -0
  68. package/dist/lite/.claude/skills/bind-subdomain/SKILL.md +232 -0
  69. package/dist/lite/.claude/skills/canvas/SKILL.md +41 -0
  70. package/dist/lite/.claude/skills/deploy-prod/SKILL.md +106 -0
  71. package/dist/lite/.claude/skills/docs-sync/SKILL.md +188 -0
  72. package/dist/lite/.claude/skills/eisenhower/SKILL.md +273 -0
  73. package/dist/lite/.claude/skills/empathy-map/SKILL.md +168 -0
  74. package/dist/lite/.claude/skills/frontend-design/SKILL.md +53 -0
  75. package/dist/lite/.claude/skills/interview-me/SKILL.md +171 -0
  76. package/dist/lite/.claude/skills/journal-export/SKILL.md +293 -0
  77. package/dist/lite/.claude/skills/jtbd-tasks/SKILL.md +203 -0
  78. package/dist/lite/.claude/skills/objectory-decompose/SKILL.md +256 -0
  79. package/dist/lite/.claude/skills/pick-luminaries/SKILL.md +100 -0
  80. package/dist/lite/.claude/skills/pre-mortem/SKILL.md +180 -0
  81. package/dist/lite/.claude/skills/prototype-gallery/SKILL.md +264 -0
  82. package/dist/lite/.claude/skills/provision-deploy-host/SKILL.md +257 -0
  83. package/dist/lite/.claude/skills/quick-fix/SKILL.md +273 -0
  84. package/dist/lite/.claude/skills/release/SKILL.md +311 -0
  85. package/dist/lite/.claude/skills/release-notes/SKILL.md +202 -0
  86. package/dist/lite/.claude/skills/rfc/SKILL.md +222 -0
  87. package/dist/lite/.claude/skills/riff-prototypes/SKILL.md +637 -0
  88. package/dist/lite/.claude/skills/secrets-bootstrap/SKILL.md +209 -0
  89. package/dist/lite/.claude/skills/skill/SKILL.md +304 -0
  90. package/dist/lite/.claude/skills/smoke-test/SKILL.md +100 -0
  91. package/dist/lite/.claude/skills/state/SKILL.md +204 -0
  92. package/dist/lite/.claude/skills/substrate-check/SKILL.md +218 -0
  93. package/dist/lite/.claude/skills/synthetic-user/SKILL.md +381 -0
  94. package/dist/lite/.claude/skills/task-scenarios/SKILL.md +105 -0
  95. package/dist/lite/.claude/skills/tier-dependency-audit/SKILL.md +139 -0
  96. package/dist/lite/.claude/skills/ux-migration/SKILL.md +424 -0
  97. package/dist/lite/.claude/skills/value-prop-canvas/SKILL.md +297 -0
  98. package/dist/lite/README.md +5 -1
  99. package/dist/lite/gitignore +6 -0
  100. package/dist/lite/lib/bassclef-dir-resolver.sh +85 -0
  101. package/dist/lite/lib/clone-failure-classifier.sh +59 -0
  102. package/dist/lite/lib/fixture-builder.sh +190 -0
  103. package/dist/lite/lib/fresh-install-check.sh +49 -0
  104. package/dist/lite/lib/hook-heartbeat.sh +0 -1
  105. package/dist/lite/lib/mechanism-fidelity.sh +569 -0
  106. package/dist/lite/lib/npm-version-check.sh +112 -0
  107. package/dist/lite/lib/preview-state.sh +229 -0
  108. package/dist/lite/lib/session-rescue.sh +0 -1
  109. package/dist/lite/scripts/generate-lite-manifest.sh +145 -4
  110. package/dist/lite/scripts/lite-manifest-drift-check.sh +5 -1
  111. package/dist/lite/standards/architect-review-discipline.md +222 -0
  112. package/dist/lite/standards/bassclef-platform.md +249 -0
  113. package/dist/lite/standards/bassclef-test-repos.md +97 -0
  114. package/dist/lite/standards/bassclef-wiring-manifest.json +25 -1
  115. package/dist/lite/standards/cold-adopter-harness.md +205 -0
  116. package/dist/lite/standards/data-management.md +135 -0
  117. package/dist/lite/standards/frontend-stack/nextjs-prisma.md +124 -0
  118. package/dist/lite/standards/lite-manifest-schema-changes.md +27 -0
  119. package/dist/lite/standards/lite-manifest.json +1456 -14
  120. package/dist/lite/standards/mechanism-fidelity.md +205 -0
  121. package/dist/lite/standards/operator-private-discipline.md +200 -0
  122. package/dist/lite/standards/operator-to-public-flow.md +195 -0
  123. package/dist/lite/standards/prototype-gallery.md +251 -0
  124. package/dist/lite/standards/pseudonym-key-schema.md +109 -0
  125. package/dist/lite/standards/sibling-validation-checklist.md +181 -0
  126. package/dist/lite/standards/slack-notifications.md +161 -0
  127. package/dist/lite/standards/synthetic-user-testing.md +173 -0
  128. package/dist/lite/standards/vendor-bindings.md +165 -0
  129. package/dist/lite/standards/vendor-plugins/frontend-design.md +66 -0
  130. package/dist/lite/standards/vpc-fit-validation.md +167 -0
  131. package/dist/lite/templates/gallery-yml.md +113 -0
  132. package/dist/lite/templates/product-canvas.md +169 -0
  133. package/dist/lite/templates/value-prop-canvas.md +180 -0
  134. package/package.json +12 -5
  135. /package/dist/lite/{whereami.md → docs/whereami.md} +0 -0
@@ -0,0 +1,283 @@
1
+ #!/usr/bin/env bash
2
+ # tier: lite
3
+ # bet-doc-gate.sh — PreToolUse Edit|Write|MultiEdit on docs/iteration-bets/*.md
4
+ #
5
+ # Composes two substrate disciplines at the bet-doc artifact-edit surface:
6
+ #
7
+ # 1. Thread-walk gate (per .claude/rules/arc-level-orientation-before-leaf.md)
8
+ # — marker required at state/markers/arc-walk/<goal-slug>.marker
9
+ # — closes bassclef-upstream#300
10
+ #
11
+ # 2. Produces/consumes substantive check (per .claude/rules/wu-sequencing-compounds.md)
12
+ # — Consumes cells in steps table must name concrete artifacts
13
+ # (file paths, marker paths, decision names, backtick-quoted refs)
14
+ # OR explicitly carry "parallel-safe (no consumer)"
15
+ # OR contain "session-start" / "union of" (prep / closeout shapes)
16
+ # — WARN on vague-prose ("WU-N's stable enforcement", "wired hook", etc.)
17
+ # — closes bassclef-upstream#21 (V2 mechanical layer of wu-sequencing-compounds)
18
+ #
19
+ # Closes the V1-methodology-only debt for both rules. Per memory
20
+ # feedback_hooks_beat_methodology_for_substrate_quality.
21
+ #
22
+ # Behavior:
23
+ # - Fires on Edit|Write|MultiEdit PreToolUse when target matches docs/iteration-bets/*.md
24
+ # - Reads tool_name + tool_input.file_path from stdin JSON (PreToolUse contract)
25
+ # OR from BASSCLEF_BET_DOC_GATE_TEST_TOOL / BASSCLEF_BET_DOC_GATE_TEST_FILE env vars (testability)
26
+ # - Silent on non-matching paths or non-Edit tool calls
27
+ # - BLOCK (exit 2) when thread-walk marker missing or stale
28
+ # - WARN (exit 0 with stderr) when produces/consumes table has vague-prose cells (advisory V1; V3 escalates if drift persists)
29
+ # - Allowlist baseline at .claude/hooks/bet-doc-gate-allowlist.txt grandfathers existing un-walked goal docs
30
+ #
31
+ # Overrides (all logged via trace-helper):
32
+ # SKIP_BET_DOC_GATE=1 — bypass both checks
33
+ # SKIP_ARC_WALK_GATE=1 — bypass thread-walk only (P/C still runs)
34
+ # SKIP_PRODUCES_CONSUMES_CHECK=1 — bypass P/C only (thread-walk still runs)
35
+
36
+ set -euo pipefail
37
+ # Saltzer-Schroeder Discipline 1 (strict mode at the top) per .claude/rules/defensive-bash.md.
38
+ # Non-fatal commands carry `|| true` per-line where their non-zero exit is the expected path.
39
+
40
+ # === Override (logged) ===
41
+ if [ "${SKIP_BET_DOC_GATE:-0}" = "1" ]; then
42
+ if [ -x ".claude/hooks/trace-helper.sh" ]; then
43
+ bash .claude/hooks/trace-helper.sh "bet-doc-gate" "skipped via SKIP_BET_DOC_GATE=1" 2>/dev/null || true
44
+ fi
45
+ exit 0
46
+ fi
47
+
48
+ # === Parse input: test env vars first, else stdin JSON ===
49
+ TOOL_NAME="${BASSCLEF_BET_DOC_GATE_TEST_TOOL:-}"
50
+ FILE_PATH="${BASSCLEF_BET_DOC_GATE_TEST_FILE:-}"
51
+
52
+ if [ -z "$TOOL_NAME" ] && [ -z "$FILE_PATH" ]; then
53
+ if [ ! -t 0 ]; then
54
+ INPUT=$(cat)
55
+ if [ -n "$INPUT" ] && command -v jq >/dev/null 2>&1; then
56
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null || echo "")
57
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null || echo "")
58
+ fi
59
+ fi
60
+ fi
61
+
62
+ # === Tool matcher ===
63
+ case "$TOOL_NAME" in
64
+ Edit|Write|MultiEdit) ;;
65
+ *) exit 0 ;;
66
+ esac
67
+
68
+ # === Path matcher (absolute or relative; both shapes accepted) ===
69
+ case "$FILE_PATH" in
70
+ */docs/iteration-bets/*.md) ;;
71
+ docs/iteration-bets/*.md) ;;
72
+ *) exit 0 ;;
73
+ esac
74
+
75
+ # === Extract goal slug (basename without extension) ===
76
+ BET_BASE=$(basename "$FILE_PATH" .md)
77
+
78
+ # === Allowlist check ===
79
+ ALLOWLIST=".claude/hooks/bet-doc-gate-allowlist.txt"
80
+ if [ -f "$ALLOWLIST" ]; then
81
+ if grep -Fxq "$BET_BASE" "$ALLOWLIST" 2>/dev/null; then
82
+ exit 0
83
+ fi
84
+ fi
85
+
86
+ # === Arc-walk check (unless SKIP_ARC_WALK_GATE=1) ===
87
+ ARC_MARKER="state/markers/arc-walk/${BET_BASE}.marker"
88
+ if [ "${SKIP_ARC_WALK_GATE:-0}" != "1" ]; then
89
+ if [ ! -f "$ARC_MARKER" ]; then
90
+ cat >&2 <<EOF
91
+
92
+ ============================================
93
+ 🛑 BLOCKED: bet-doc-gate (thread-walk)
94
+ ============================================
95
+
96
+ You are editing a goal doc without prior parent-goal walk evidence in this session.
97
+
98
+ Goal: ${BET_BASE}
99
+ Missing marker: state/markers/thread-walk/${BET_BASE}.marker
100
+ (writes resolve to state/markers/arc-walk/ per compat
101
+ window through 2026-10-31; both paths accepted)
102
+
103
+ Per .claude/rules/arc-level-orientation-before-leaf.md — walk the parent_bet
104
+ chain up to the root before editing. Surface drivers plus open acceptance plus
105
+ out-of-scope from each ancestor.
106
+
107
+ To resolve:
108
+ 1. Walk the chain: read docs/iteration-bets/${BET_BASE}.md frontmatter,
109
+ then follow parent_bet up to the root
110
+ 2. Surface drivers plus open acceptance plus out_of_scope per ancestor
111
+ 3. Touch the marker:
112
+ mkdir -p state/markers/thread-walk/
113
+ touch state/markers/thread-walk/${BET_BASE}.marker
114
+
115
+ Overrides (logged via trace-helper):
116
+ SKIP_ARC_WALK_GATE=1 — skip the parent-goal walk check only
117
+ SKIP_BET_DOC_GATE=1 — skip both checks
118
+
119
+ Closes bassclef-upstream#300 + #21 (composed).
120
+ ============================================
121
+ EOF
122
+ if [ -x ".claude/hooks/trace-helper.sh" ]; then
123
+ bash .claude/hooks/trace-helper.sh "bet-doc-gate" "BLOCKED thread-walk on ${BET_BASE}" 2>/dev/null || true
124
+ fi
125
+ exit 2
126
+ fi
127
+ fi
128
+
129
+ # === Produces/consumes substantive check (unless SKIP_PRODUCES_CONSUMES_CHECK=1) ===
130
+ if [ "${SKIP_PRODUCES_CONSUMES_CHECK:-0}" = "1" ]; then
131
+ exit 0
132
+ fi
133
+
134
+ if [ ! -f "$FILE_PATH" ]; then
135
+ # New file (Write before content lands); nothing to scan
136
+ exit 0
137
+ fi
138
+
139
+ # Find the steps table header line.
140
+ # Accepts two shapes (per bassclef-upstream#688):
141
+ # 3-col: | WU | Produces | Consumes (from prior WU) |
142
+ # 6-col: | WU | Problem + value prop | Produces | Consumes (from prior WU) | How this step builds on the prior | Risk |
143
+ # The optional group before "Produces" swallows the "Problem + value prop"
144
+ # column when the six-column shape is used.
145
+ HEADER_INFO=$(grep -nE '^\| WU \|( [^|]* \|)? Produces \| Consumes \(from prior WU\) \|' "$FILE_PATH" 2>/dev/null | head -1 || true)
146
+ if [ -z "$HEADER_INFO" ]; then
147
+ # No steps table; nothing to scan
148
+ exit 0
149
+ fi
150
+
151
+ HEADER_LNUM=$(echo "$HEADER_INFO" | cut -d: -f1)
152
+ # Detect column shape by counting pipes in the header line.
153
+ # 3-col header has 4 pipes; 6-col header has 7 pipes.
154
+ # Consumes cell is field 4 in 3-col; field 5 in 6-col (awk splits on '|'
155
+ # and prepends an empty field before the first pipe).
156
+ HEADER_LINE=$(sed -n "${HEADER_LNUM}p" "$FILE_PATH")
157
+ PIPE_COUNT=$(echo "$HEADER_LINE" | tr -cd '|' | wc -c | tr -d ' ')
158
+ if [ "$PIPE_COUNT" -ge 7 ]; then
159
+ CONSUMES_FIELD=5
160
+ else
161
+ CONSUMES_FIELD=4
162
+ fi
163
+ # Skip header + separator (lines header_lnum and header_lnum+1)
164
+ START=$((HEADER_LNUM + 2))
165
+
166
+ # Scan rows until empty line or non-pipe line
167
+ VAGUE_HITS=""
168
+ LNUM=$START
169
+ while IFS= read -r line; do
170
+ case "$line" in
171
+ "|"*)
172
+ # Pipe-separated row. Extract Consumes cell at CONSUMES_FIELD
173
+ # (field 4 in 3-col shape, field 5 in 6-col shape).
174
+ CONSUMES=$(echo "$line" | awk -F'|' -v f="$CONSUMES_FIELD" '{print $f}' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
175
+ if [ -z "$CONSUMES" ]; then
176
+ LNUM=$((LNUM+1)); continue
177
+ fi
178
+
179
+ # PASS patterns: explicit parallel-safe, session-start, union-of-closeout,
180
+ # backtick-quoted reference, slash-path, .sh/.md/.json/.yml extension
181
+ if echo "$CONSUMES" | grep -qE 'parallel-safe \(no consumer\)|session-start|union of|`[^`]+`|/[a-zA-Z]|\.sh\b|\.md\b|\.json\b|\.yml\b'; then
182
+ LNUM=$((LNUM+1)); continue
183
+ fi
184
+
185
+ # WARN pattern: starts with WU-N or WU-N's followed by vague prose without
186
+ # any concrete artifact reference
187
+ if echo "$CONSUMES" | grep -qE "^WU-[0-9]+'?s?\b"; then
188
+ VAGUE_HITS="${VAGUE_HITS} line ${LNUM}: ${CONSUMES}
189
+ "
190
+ fi
191
+ ;;
192
+ "") break ;;
193
+ *) break ;;
194
+ esac
195
+ LNUM=$((LNUM+1))
196
+ done < <(tail -n +"$START" "$FILE_PATH")
197
+
198
+ if [ -n "$VAGUE_HITS" ]; then
199
+ cat >&2 <<EOF
200
+
201
+ ============================================
202
+ ⚠️ WARN: bet-doc-gate (produces/consumes)
203
+ ============================================
204
+
205
+ Bet: ${BET_BASE}
206
+
207
+ The following Consumes cells contain vague-prose references that may not
208
+ name concrete artifacts the next WU's source actually reads:
209
+
210
+ ${VAGUE_HITS}
211
+ Per .claude/rules/wu-sequencing-compounds.md — each Consumes cell should
212
+ name a concrete artifact (file path, marker path, decision name) OR
213
+ explicitly carry "parallel-safe (no consumer)" when the step is independent.
214
+
215
+ This is advisory (V1). Flips to BLOCK if observable drift continues
216
+ (V3 follow-on per feedback_hooks_beat_methodology_for_substrate_quality).
217
+
218
+ Overrides (logged):
219
+ SKIP_PRODUCES_CONSUMES_CHECK=1 — skip P/C only
220
+ SKIP_BET_DOC_GATE=1 — skip both
221
+
222
+ Closes bassclef-upstream#21.
223
+ ============================================
224
+ EOF
225
+ if [ -x ".claude/hooks/trace-helper.sh" ]; then
226
+ bash .claude/hooks/trace-helper.sh "bet-doc-gate" "WARN produces/consumes on ${BET_BASE}" 2>/dev/null || true
227
+ fi
228
+ fi
229
+
230
+ # === Closeout enforcement check (unless SKIP_CLOSEOUT_ENFORCE=1) ===
231
+ # Fires when goal frontmatter marks the goal as complete AND no ## Closeout
232
+ # section exists. Prevents the 103-orphan class from recurring — per
233
+ # whereami operator_recap_2026-08-04e Phase 1b, 103 of 107 goal docs closed
234
+ # without a Closeout section; metrics undercounted by 96% until backfilled.
235
+ # V1 ADVISORY (WARN via stderr; exit 0 continues). V2 flips to BLOCK after
236
+ # allowlist baseline clears. Per bassclef-upstream#1081 + .claude/rules/session-artifacts.md.
237
+ if [ "${SKIP_CLOSEOUT_ENFORCE:-0}" != "1" ]; then
238
+ # Read status field from YAML frontmatter (first `status: X` line inside first --- block)
239
+ BET_STATUS=$(awk '/^---$/{c++; next} c==1 && /^status:/{sub(/^status: */, ""); sub(/[[:space:]]*$/, ""); print; exit}' "$FILE_PATH" 2>/dev/null || echo "")
240
+
241
+ case "$BET_STATUS" in
242
+ complete|shipped|closed|done)
243
+ # Bet marks itself complete; check for closeout section
244
+ if ! grep -qiE "^## Closeout" "$FILE_PATH" 2>/dev/null; then
245
+ cat >&2 <<EOF
246
+
247
+ ============================================
248
+ ⚠️ WARN: bet-doc-gate (closeout section)
249
+ ============================================
250
+
251
+ Bet: ${BET_BASE}
252
+ Status: ${BET_STATUS}
253
+
254
+ The goal frontmatter marks this as complete but no "## Closeout" section
255
+ exists in the body. Metrics velocity view undercounts closed work when the
256
+ section is missing.
257
+
258
+ Per .claude/rules/session-artifacts.md — completed goals ship a Closeout
259
+ section documenting what landed + what deferred + retro links.
260
+
261
+ This is advisory (V1). Flips to BLOCK after allowlist baseline clears
262
+ (V2 per feedback_hooks_beat_methodology_for_substrate_quality).
263
+
264
+ Overrides (logged):
265
+ SKIP_CLOSEOUT_ENFORCE=1 — skip closeout check only
266
+ SKIP_BET_DOC_GATE=1 — skip all bet-doc gate checks
267
+
268
+ Closes bassclef-upstream#1081.
269
+ ============================================
270
+ EOF
271
+ if [ -x ".claude/hooks/trace-helper.sh" ]; then
272
+ bash .claude/hooks/trace-helper.sh "bet-doc-gate" "WARN closeout missing on ${BET_BASE}" 2>/dev/null || true
273
+ fi
274
+ fi
275
+ ;;
276
+ *)
277
+ # Bet not marked complete (or status unset) — skip; only fires on complete bets
278
+ :
279
+ ;;
280
+ esac
281
+ fi
282
+
283
+ exit 0
@@ -0,0 +1,64 @@
1
+ #!/bin/bash
2
+ # tier: lite
3
+ set -e
4
+
5
+ # Bug Diagnosis hook — fires on UserPromptSubmit.
6
+ # Safety net: keyword detection injects diagnosis reminder.
7
+ #
8
+ # IMPORTANT: This hook is a SAFETY NET, not the process.
9
+ # The agent is responsible for self-triggering diagnosis when
10
+ # it recognizes a failure in any form — including screenshots,
11
+ # implicit phrasing, and logs that this keyword matcher cannot detect.
12
+
13
+ INPUT=$(cat)
14
+ # Per bassclef#1125: fail-soft on jq parse failure (malformed stdin JSON
15
+ # returns jq exit 5; set -e would crash the hook).
16
+ PROMPT=$(echo "$INPUT" | jq -r '.prompt // ""' 2>/dev/null | tr '[:upper:]' '[:lower:]' || echo "")
17
+
18
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
19
+
20
+ # Strong match: explicit defect/failure keywords → full diagnosis block
21
+ if echo "$PROMPT" | grep -qiE '\b(bug|defect|regress(ion)?|flaky|intermittent)\b|\b(crash|panic|abort|sigkill|oom|segfault)\b|\b(broken|corrupt|stale|stuck|hung|hanging|frozen|deadlock|infinite.loop)\b|\b(404|500|502|503|504|redirect.loop|cors.error)\b|\b(timeout|timed.out|memory.leak|high.cpu)\b|\b(unreachable|not.responding|connection.refused)\b|\b(not.working|doesn.t.work|won.t.start|stopped.working|blank.page|white.screen)\b'; then
22
+ source "$SCRIPT_DIR/trace-helper.sh"
23
+ trace_log "bug-diagnosis" "defect keywords detected"
24
+ cat <<'DIAG'
25
+ ============================================
26
+ BUG/FAILURE DETECTED — RUN /diagnose FIRST
27
+ ============================================
28
+
29
+ The user's message describes a bug, error, or failure.
30
+ Run the /diagnose skill BEFORE making ANY code changes.
31
+
32
+ Do not Edit, Write, or run fix commands until you have written:
33
+ 1. Is / Is Not
34
+ 2. Five Whys
35
+ 3. Hypothesis + Test
36
+
37
+ If this is actually a design discussion (not a bug), state that
38
+ and proceed normally.
39
+ ============================================
40
+ DIAG
41
+ exit 0
42
+ fi
43
+
44
+ # Weak match: phrases that often accompany failures — lighter reminder
45
+ if echo "$PROMPT" | grep -qiE '(fix|re-run|re.run|how do we|throws|error|issue|wrong|problem|diagnose|screenshot|logs show|FAIL)'; then
46
+ cat <<'REMIND'
47
+ ============================================
48
+ POSSIBLE FAILURE — CHECK BEFORE FIXING
49
+ ============================================
50
+
51
+ This message may describe a failure (screenshot, log, or implicit).
52
+ The hook cannot always detect failures — YOU must judge:
53
+
54
+ - Is the user reporting something that isn't working as expected?
55
+ - Are they showing a screenshot with errors or unexpected output?
56
+ - Are they asking "how do we fix/re-run X"?
57
+
58
+ If YES → run /diagnose BEFORE writing any fix.
59
+ If NO → proceed normally.
60
+ ============================================
61
+ REMIND
62
+ fi
63
+
64
+ exit 0
@@ -0,0 +1,136 @@
1
+ #!/bin/bash
2
+ # tier: lite
3
+ # Destructive Command Guard — fires on Bash PreToolUse.
4
+ # Detects destructive SIGNALS in any command, not specific tools.
5
+ # Forces a temperance pause before proceeding.
6
+ #
7
+ # Design: signal-based, not command-specific. Instead of hardcoding
8
+ # "prisma migrate reset", we detect the PATTERN of destruction:
9
+ # destructive verbs + force flags + amplifiers. This catches tools
10
+ # we haven't seen yet.
11
+ #
12
+ # Origin: operator manually invoked /temperance to prevent a prisma
13
+ # migrate reset that would have destroyed dev data. A single DELETE
14
+ # on a stale migration row was the actual fix.
15
+
16
+ INPUT=$(cat)
17
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
18
+
19
+ # Skip empty commands
20
+ if [ -z "$COMMAND" ]; then
21
+ exit 0
22
+ fi
23
+
24
+ # === Safe command allowlist (skip quickly for common operations) ===
25
+ # These are high-frequency, never destructive — skip to avoid latency.
26
+ if echo "$COMMAND" | grep -qE '^(git (status|diff|log|show|branch --show)|ls |cat |head |tail |wc |echo |which |test |pwd|cd )'; then
27
+ exit 0
28
+ fi
29
+
30
+ # === Signal-based destructive detection ===
31
+ #
32
+ # Three signal types, scored by severity:
33
+ # VERB: destructive action word (reset, drop, delete, destroy, purge, etc.)
34
+ # FORCE: flag that bypasses confirmation (--force, --hard, -f, -rf, --yes)
35
+ # AMPLIFIER: scope expansion (CASCADE, --all, --volumes, recursive, -r)
36
+ #
37
+ # Scoring:
38
+ # VERB alone = MEDIUM (warn)
39
+ # VERB + FORCE = HIGH (strong warn)
40
+ # VERB + AMPLIFIER = HIGH
41
+ # VERB + FORCE + AMPLIFIER = CRITICAL
42
+
43
+ VERB=""
44
+ FORCE=""
45
+ AMPLIFIER=""
46
+
47
+ # --- Detect destructive verbs ---
48
+ # These words signal intent to destroy state
49
+ if echo "$COMMAND" | grep -qiE '\b(reset|drop|delete|destroy|purge|prune|wipe|clean|truncate|obliterate|nuke|flush|drain|evict|overwrite|revert|rollback|format|erase|clear|demolish|discard|abort|terminate|revoke|invalidate|expire|uninstall|deregister|detach)\b'; then
50
+ VERB=$(echo "$COMMAND" | grep -oiE '\b(reset|drop|delete|destroy|purge|prune|wipe|clean|truncate|obliterate|nuke|flush|drain|evict|overwrite|revert|rollback|format|erase|clear|demolish|discard|abort|terminate|revoke|invalidate|expire|uninstall|deregister|detach)\b' | head -1)
51
+ fi
52
+ # "remove" only counts with a force/recursive flag (otherwise it's just rm file.txt)
53
+ if [ -z "$VERB" ] && echo "$COMMAND" | grep -qiE '\bremove\b' && echo "$COMMAND" | grep -qE '\-(r|f|rf|fr)\b|--force|--recursive'; then
54
+ VERB="remove"
55
+ fi
56
+ # "rm" only counts with a force/recursive flag (otherwise it's just rm file.txt)
57
+ # Per bassclef goal 2026-06-17a Phase Z-2: hardening discovered the rm gap.
58
+ if [ -z "$VERB" ] && echo "$COMMAND" | grep -qE '\brm\b' && echo "$COMMAND" | grep -qE '\-(r|f|rf|fr)\b|--force|--recursive'; then
59
+ VERB="rm"
60
+ fi
61
+ # "push" only counts with --force (otherwise it's just git push)
62
+ if [ -z "$VERB" ] && echo "$COMMAND" | grep -qE 'push.*--force|push.*-f\b'; then
63
+ VERB="force-push"
64
+ fi
65
+
66
+ # No destructive verb → exit early
67
+ if [ -z "$VERB" ]; then
68
+ exit 0
69
+ fi
70
+
71
+ # --- Detect force flags ---
72
+ if echo "$COMMAND" | grep -qE '\-\-(force|hard|yes|no-verify|skip-seed|no-prompt)\b|\s-f\b|\s-rf\b|\s-fr\b'; then
73
+ FORCE=$(echo "$COMMAND" | grep -oE '\-\-(force|hard|yes|no-verify|skip-seed|no-prompt)\b|\s-f\b|\s-rf\b' | head -1 | tr -d ' ')
74
+ fi
75
+
76
+ # --- Detect amplifiers ---
77
+ if echo "$COMMAND" | grep -qiE 'CASCADE|--all\b|--volumes\b|--recursive\b|\s-r\b|--include-untracked|system\b'; then
78
+ AMPLIFIER=$(echo "$COMMAND" | grep -oiE 'CASCADE|--all|--volumes|--recursive|-r\b|--include-untracked|system' | head -1)
79
+ fi
80
+
81
+ # === Score severity ===
82
+ if [ -n "$VERB" ] && [ -n "$FORCE" ] && [ -n "$AMPLIFIER" ]; then
83
+ SEVERITY="CRITICAL — forced + amplified destruction"
84
+ elif [ -n "$VERB" ] && ([ -n "$FORCE" ] || [ -n "$AMPLIFIER" ]); then
85
+ SEVERITY="HIGH — forced or amplified destruction"
86
+ else
87
+ SEVERITY="MEDIUM — destructive verb detected"
88
+ fi
89
+
90
+ # === Classify the domain ===
91
+ CATEGORY="DESTRUCTIVE COMMAND"
92
+ if echo "$COMMAND" | grep -qiE 'prisma|psql|mysql|sqlite|mongo|DROP|TRUNCATE|migrate'; then
93
+ CATEGORY="DATABASE DESTRUCTION"
94
+ elif echo "$COMMAND" | grep -qiE 'docker|compose|container|volume|image'; then
95
+ CATEGORY="CONTAINER DESTRUCTION"
96
+ elif echo "$COMMAND" | grep -qiE '^git |^gh '; then
97
+ CATEGORY="GIT DESTRUCTION"
98
+ elif echo "$COMMAND" | grep -qiE '^rm |^find.*-delete|xargs.*rm'; then
99
+ CATEGORY="FILE DESTRUCTION"
100
+ elif echo "$COMMAND" | grep -qiE 'node_modules|\.next|dist/|build/|cache'; then
101
+ CATEGORY="BUILD CACHE WIPE"
102
+ fi
103
+
104
+ # === Output temperance prompt ===
105
+ echo ""
106
+ echo "============================================"
107
+ echo "⚠️ DESTRUCTIVE COMMAND — TEMPERANCE REQUIRED"
108
+ echo "============================================"
109
+ echo ""
110
+ echo "Category: $CATEGORY"
111
+ echo "Severity: $SEVERITY"
112
+ echo "Signals: verb=$VERB${FORCE:+ force=$FORCE}${AMPLIFIER:+ amplifier=$AMPLIFIER}"
113
+ echo "Command: $COMMAND"
114
+ echo ""
115
+ echo "BEFORE PROCEEDING, answer:"
116
+ echo ""
117
+ echo "1. ROOT CAUSE — What specific problem requires destruction?"
118
+ echo " (Not 'it's broken' — what exactly is wrong?)"
119
+ echo ""
120
+ echo "2. SIMPLER FIX — Is there a targeted alternative?"
121
+ echo " Common traps:"
122
+ echo " • DB checksum mismatch → fix the row, don't reset the DB"
123
+ echo " • Schema drift → create a migration, don't db push"
124
+ echo " • Dirty git state → commit or stash, don't reset --hard"
125
+ echo " • Failing tests → diagnose the test, don't wipe state"
126
+ echo " • Container issues → restart the service, don't prune all"
127
+ echo " • Build errors → clear the specific cache, don't rm -rf"
128
+ echo ""
129
+ echo "3. RECOVERY — If you proceed, can you undo it?"
130
+ echo " (Backup? Seed data? Git reflog? Docker image cache?)"
131
+ echo ""
132
+ echo "The simplest fix is almost never the most destructive one."
133
+ echo "============================================"
134
+ echo ""
135
+
136
+ exit 0
@@ -0,0 +1,147 @@
1
+ #!/bin/bash
2
+ # tier: lite
3
+ # install-class: project
4
+ # file-rename-discipline-check — PreToolUse hook that blocks `git commit`
5
+ # Bash invocations when the staged changes delete a file under the
6
+ # rule's scope without a matching entry in standards/file-renames.json.
7
+ #
8
+ # Per .claude/rules/file-rename-discipline.md — every file rename in
9
+ # bassclef registers a map entry AND ships a fixture test. The rule's
10
+ # methodology layer says do it; this hook is the mechanical layer that
11
+ # fires on every commit.
12
+ #
13
+ # Why this exists: the 2026-06-21 rename of canonical-source-config-validate.sh
14
+ # shipped without registering itself anywhere. POA's session on 2026-06-29
15
+ # surfaced the gap eight days later. Bet 29e WU-9 shipped the cure; this
16
+ # hook prevents the next rename from repeating the failure.
17
+ #
18
+ # Exit codes:
19
+ # 0 — allow (non-matching Bash, no scoped deletions, all deletions registered, override)
20
+ # 2 — block (one or more scoped deletions missing from the rename map)
21
+ #
22
+ # Override: SKIP_FILE_RENAME_DISCIPLINE=1 (logged via trace-helper)
23
+
24
+ set +e
25
+
26
+ INPUT=$(cat 2>/dev/null)
27
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""' 2>/dev/null)
28
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""' 2>/dev/null)
29
+
30
+ # Only fire on Bash tool calls
31
+ [ "$TOOL_NAME" = "Bash" ] || exit 0
32
+
33
+ # Only fire on `git commit` invocations (not git status, not git diff, etc)
34
+ case "$COMMAND" in
35
+ *"git commit"*) : ;;
36
+ *) exit 0 ;;
37
+ esac
38
+
39
+ # Skip via override (logged)
40
+ if [ "${SKIP_FILE_RENAME_DISCIPLINE:-0}" = "1" ]; then
41
+ if [ -x ".claude/hooks/trace-helper.sh" ]; then
42
+ bash .claude/hooks/trace-helper.sh "file-rename-discipline" "skipped via SKIP_FILE_RENAME_DISCIPLINE=1" 2>/dev/null || true
43
+ fi
44
+ exit 0
45
+ fi
46
+
47
+ # Resolve the rename map (env-var override for tests + curl-pipe scenarios)
48
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
49
+ RENAME_MAP="${BASSCLEF_RENAME_MAP:-$REPO_ROOT/standards/file-renames.json}"
50
+
51
+ # If the rename map does not exist on this repo, the hook is dormant.
52
+ # (Adopter repos don't ship the map; only bassclef-upstream does.)
53
+ [ -f "$RENAME_MAP" ] || exit 0
54
+
55
+ # Read staged deletions. `git diff --cached --diff-filter=D --name-only`
56
+ # lists files deleted in the staged change.
57
+ DELETIONS=$(git diff --cached --diff-filter=D --name-only 2>/dev/null)
58
+ [ -n "$DELETIONS" ] || exit 0
59
+
60
+ # Scope filter — only check deletions under the rule's path list.
61
+ SCOPED_PATHS=(
62
+ ".claude/hooks/"
63
+ ".claude/skills/"
64
+ ".claude/rules/"
65
+ ".claude/luminaries/"
66
+ ".claude/agents/"
67
+ "scripts/"
68
+ "lib/"
69
+ "standards/"
70
+ "presence/"
71
+ "architecture/decisions/"
72
+ )
73
+
74
+ # Build the list of registered old_names from the rename map.
75
+ # Both the renames array AND the compat_shims_preserved list are
76
+ # considered "registered" — the rule allows both shapes.
77
+ REGISTERED_NAMES=$(jq -r '
78
+ ((.renames // [])[]?.old_name),
79
+ ((.compat_shims_preserved // [])[]?.old_name)
80
+ ' "$RENAME_MAP" 2>/dev/null | sort -u)
81
+
82
+ # Walk the deletions; flag any scoped deletion whose basename is missing
83
+ # from the registered list.
84
+ UNREGISTERED=""
85
+ while IFS= read -r path; do
86
+ [ -n "$path" ] || continue
87
+ # Scope filter: skip deletions outside the rule's paths
88
+ in_scope=0
89
+ for scope in "${SCOPED_PATHS[@]}"; do
90
+ case "$path" in
91
+ "$scope"*) in_scope=1; break ;;
92
+ esac
93
+ done
94
+ [ "$in_scope" = "1" ] || continue
95
+
96
+ base=$(basename "$path")
97
+ if ! echo "$REGISTERED_NAMES" | grep -qFx "$base"; then
98
+ UNREGISTERED="$UNREGISTERED - $path (basename: $base)"$'\n'
99
+ fi
100
+ done <<< "$DELETIONS"
101
+
102
+ # No unregistered scoped deletions → allow
103
+ [ -z "$UNREGISTERED" ] && exit 0
104
+
105
+ # Block with a structured remediation message
106
+ cat <<MSG >&2
107
+ ============================================
108
+ 🛑 FILE-RENAME DISCIPLINE — BLOCKED 🛑
109
+ ============================================
110
+
111
+ The staged commit deletes one or more files under the rule's scope without
112
+ a matching entry in standards/file-renames.json. Per .claude/rules/file-rename-discipline.md
113
+ every file rename registers a map entry plus a fixture test.
114
+
115
+ Unregistered deletions:
116
+ $UNREGISTERED
117
+
118
+ Three required moves:
119
+
120
+ 1. Add an entry under \`.renames\` in standards/file-renames.json:
121
+
122
+ {
123
+ "old_name": "<basename-being-deleted>",
124
+ "new_name": "<basename-of-the-successor-file>",
125
+ "renamed_at": "$(date +%Y-%m-%d)",
126
+ "category": "<hook|skill|rule|agent|luminary|script|lib|standard|adr>"
127
+ }
128
+
129
+ 2. Ship a fixture under .claude/hooks/tests/fixtures/adopter-$(date +%Y-%m-%d)-rename/
130
+ that mirrors the adopter state after the rename ships. The migrate
131
+ script's test suite reads the fixture; assertions pin the cure.
132
+
133
+ 3. Cite the rename in the goal doc or chronicle for this commit.
134
+
135
+ Compat-shim case — when the old name keeps working via a different
136
+ mechanism (e.g., dispatcher's canonical-sync.sh via ADR-032 two-stage rocket):
137
+ add the entry under \`.compat_shims_preserved\` instead, with a reason field.
138
+
139
+ Override (logged via trace-helper): SKIP_FILE_RENAME_DISCIPLINE=1 git commit ...
140
+
141
+ Use only for migrating bulk content or emergency rescue; routine renames
142
+ register the entry.
143
+
144
+ ============================================
145
+ MSG
146
+
147
+ exit 2