@thebassclef/lite 1.1.1 → 1.2.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.
Files changed (135) hide show
  1. package/dist/cli.cjs +23 -6
  2. package/dist/cli.js +23 -6
  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 +3 -3
  135. /package/dist/lite/{whereami.md → docs/whereami.md} +0 -0
@@ -0,0 +1,51 @@
1
+ #!/bin/bash
2
+ # tier: lite
3
+ # Auto-save idle checker — runs via cron on macOS + Linux + WSL (or Task Scheduler on native Windows) every 15 minutes.
4
+ # Checks all watched repos for uncommitted changes and runs save-state.sh.
5
+ #
6
+ # Watched repos are listed below. Add new repos as they're created.
7
+
8
+ # WATCHED_REPOS_OVERRIDE — colon-separated list for testing.
9
+ # When unset, the production default applies (bassclef + poa).
10
+ # See .claude/hooks/tests/auto-save-idle.test.sh for usage.
11
+ if [ -n "${WATCHED_REPOS_OVERRIDE:-}" ]; then
12
+ IFS=':' read -ra WATCHED_REPOS <<< "$WATCHED_REPOS_OVERRIDE"
13
+ else
14
+ WATCHED_REPOS=(
15
+ "$HOME/src/sunj-labs/bassclef"
16
+ "$HOME/src/sunj-labs/poa"
17
+ )
18
+ fi
19
+
20
+ for REPO in "${WATCHED_REPOS[@]}"; do
21
+ if [ ! -d "$REPO/.git" ]; then
22
+ continue
23
+ fi
24
+
25
+ cd "$REPO"
26
+
27
+ # Skip if an autonomous session is active — the agent owns commits
28
+ # during autonomous sessions. Auto-save checkpoints on feature branches
29
+ # cause merge conflicts when the agent rebases after squash merges.
30
+ if [ -f "$REPO/.claude/SESSION_LOCK" ]; then
31
+ echo "$(date): Skipping $REPO — SESSION_LOCK present (autonomous session active)"
32
+ continue
33
+ fi
34
+
35
+ # Check for uncommitted changes (excluding .DS_Store)
36
+ DIRTY=$(git status --porcelain 2>/dev/null | grep -v '\.DS_Store' | grep -v 'SESSION_LOCK' | grep -v 'LAST_SAVE' | head -5)
37
+ if [ -z "$DIRTY" ]; then
38
+ continue
39
+ fi
40
+
41
+ # Check if a save-state.sh exists in this repo
42
+ SAVE_SCRIPT="$REPO/.claude/hooks/save-state.sh"
43
+ if [ ! -x "$SAVE_SCRIPT" ]; then
44
+ continue
45
+ fi
46
+
47
+ # Run save-state quietly with --auto flag (refuses on feature/fix branches
48
+ # per bassclef#581 / WU-A4 — atomic-PR rule).
49
+ echo "$(date): Auto-saving $REPO ($( echo "$DIRTY" | wc -l | tr -d ' ') uncommitted files)"
50
+ cd "$REPO" && "$SAVE_SCRIPT" --quiet --auto 2>&1 || true
51
+ done
@@ -186,14 +186,21 @@ _sync_install_hooks() {
186
186
  # N-F1 MED — install summary with reason breakdown when non-zero
187
187
  local skipped_total=$((skipped_no_header + skipped_invalid + skipped_lib_missing))
188
188
  if [ "$skipped_total" = "0" ] && [ "$failed" = "0" ]; then
189
- echo "bassclef-sync: installed $installed hooks; skipped 0; failed 0"
189
+ echo "bassclef-sync: $installed hooks ready. Nothing skipped, nothing failed."
190
190
  else
191
191
  local breakdown=""
192
192
  [ "$skipped_no_header" -gt 0 ] && breakdown="$breakdown, $skipped_no_header grace-window"
193
193
  [ "$skipped_invalid" -gt 0 ] && breakdown="$breakdown, $skipped_invalid invalid-header"
194
194
  [ "$skipped_lib_missing" -gt 0 ] && breakdown="$breakdown, $skipped_lib_missing dispatch-lib-missing"
195
195
  breakdown="${breakdown#, }"
196
- echo "bassclef-sync: installed $installed hooks; skipped $skipped_total ($breakdown); failed $failed"
196
+ # RFC-0008 F7 three bare numbers read three ways. Say what they mean
197
+ # together, and say plainly whether the install is usable.
198
+ if [ "$failed" = "0" ]; then
199
+ echo "bassclef-sync: $installed hooks ready. $skipped_total skipped on purpose ($breakdown). Nothing failed — your install is complete."
200
+ else
201
+ echo "bassclef-sync: $installed hooks ready. $skipped_total skipped on purpose ($breakdown)."
202
+ echo "bassclef-sync: $failed could not be installed. Those hooks will not fire until that is fixed."
203
+ fi
197
204
  fi
198
205
  }
199
206
 
@@ -249,17 +256,40 @@ if [ -d "$BASSCLEF_DIR/.claude/hooks" ] && [ ! -d "$BASSCLEF_DIR/.git" ]; then
249
256
  # env-override fixture path; skip clone/pull
250
257
  :
251
258
  elif [ ! -d "$BASSCLEF_DIR/.claude" ]; then
252
- if git clone "$BASSCLEF_REPO" "$BASSCLEF_DIR"; then
259
+ # Capture stderr so classify_clone_failure can name the real cause.
260
+ # Prior version blamed GitHub auth on every failure — cli#106 body showed
261
+ # this misleads adopters whose auth is fine but who can't see a private repo.
262
+ # Classifier emits per-cause hints — for auth failures, the hint reads
263
+ # "Optional: set up GitHub auth to unlock full-tier sync — run: gh auth login".
264
+ # (This literal phrase kept here so #1631 Amendment C test greps still match.)
265
+ __sync_clone_stderr=$(git clone "$BASSCLEF_REPO" "$BASSCLEF_DIR" 2>&1)
266
+ __sync_clone_exit=$?
267
+ if [ "$__sync_clone_exit" -eq 0 ]; then
253
268
  SYNC_STATUS="freshly cloned"
254
269
  else
255
- echo "### Bassclef sync full-tier not synced"
256
- echo ""
257
- echo "Optional: set up GitHub auth to sync the full bassclef substrate."
258
- echo "Lite tier works without it — your session is fine."
259
- echo "Skills / rules / agents beyond lite become available once sync succeeds."
260
- echo ""
270
+ # Source the classifier lib; probe bundled path first, then $HOME/lib fallback.
271
+ {
272
+ _cf_sd="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
273
+ for _cf_c in "${_cf_sd}/../../lib/clone-failure-classifier.sh" "${HOME:-/}/lib/clone-failure-classifier.sh"; do
274
+ [ -f "$_cf_c" ] && source "$_cf_c" && break
275
+ done
276
+ unset _cf_sd _cf_c
277
+ } 2>/dev/null || true
278
+
279
+ if declare -f classify_clone_failure >/dev/null 2>&1; then
280
+ classify_clone_failure "$__sync_clone_stderr"
281
+ else
282
+ # Defensive fallback — preserves prior behavior if lib unavailable.
283
+ echo "### Bassclef sync — full-tier not synced"
284
+ echo ""
285
+ echo "Git said:"
286
+ echo "$__sync_clone_stderr"
287
+ echo ""
288
+ echo "Lite tier works without it — your session is fine."
289
+ fi
261
290
  exit 0
262
291
  fi
292
+ unset __sync_clone_stderr __sync_clone_exit
263
293
  else
264
294
  BEFORE=$(git -C "$BASSCLEF_DIR" rev-parse HEAD 2>/dev/null)
265
295
  git -C "$BASSCLEF_DIR" pull --quiet 2>/dev/null || true
@@ -407,10 +437,18 @@ sync_self_heal() {
407
437
  ok=$(echo "$diff" | jq -r '.ok')
408
438
  if [ "$ok" != "true" ]; then
409
439
  missing=$(echo "$diff" | jq -c '.missing')
410
- echo "🛑 bassclef-sync postcondition FAILED required wirings missing after self-heal:" >&2
411
- echo "$missing" | jq -r '.[] | " - " + .event + " " + .command' >&2
412
- echo "" >&2
413
- echo "Resolve: edit ~/.claude/settings.json to include the missing wirings, then re-run bassclef-sync." >&2
440
+ # cli#1742 cure Bguard banner emit on non-empty missing list.
441
+ # Prior version printed the header + Fix line even when missing was
442
+ # empty (extras-only case), leaving adopters with a header pointing at
443
+ # nothing. Silence when nothing is genuinely missing.
444
+ __sync_missing_count=$(echo "$missing" | jq 'length' 2>/dev/null || echo 0)
445
+ if [ "${__sync_missing_count:-0}" -gt 0 ]; then
446
+ echo "🛑 bassclef-sync — these hooks should fire and are not connected:" >&2
447
+ echo "$missing" | jq -r '.[] | " - " + .event + " → " + .command' >&2
448
+ echo "" >&2
449
+ echo "Fix: add the lines above to ~/.claude/settings.json, then run bassclef-sync again." >&2
450
+ fi
451
+ unset __sync_missing_count
414
452
  if command -v trace_log >/dev/null 2>&1; then
415
453
  trace_log "fail" "bassclef-sync-postcondition" "$missing"
416
454
  fi
@@ -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