@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,120 @@
1
+ ---
2
+ tier: lite
3
+ purpose: adopter orientation — auto-imported into adopter CLAUDE.md via @.claude/bassclef-orientation.md per ADR-032 + Anthropic @import convention
4
+ last_updated: 2026-06-22
5
+ bet: 2026-06-22b-dispatcher-hardening-rename-orientation (WU-5)
6
+ ---
7
+
8
+ # Bassclef orientation
9
+
10
+ You've inherited the bassclef substrate. This file loads into every session via `@import` from your CLAUDE.md.
11
+
12
+ The substrate is a stable set of skills, rules, hooks, luminaries, agents, and standards that pin discipline at decision boundaries. It evolves upstream in `sunj-labs/bassclef-upstream`; you receive releases from public `sunj-labs/bassclef` via `bassclef-sync` at every session start.
13
+
14
+ ## What you have
15
+
16
+ | Layer | Where | Discover |
17
+ |---|---|---|
18
+ | Skills (invocable workflows) | `.claude/skills/<name>/SKILL.md` | `/skills` |
19
+ | Rules (auto-loaded context) | `.claude/rules/*.md` | All loaded every session via `additionalDirectories` |
20
+ | Luminaries (design + authoring lenses) | `.claude/luminaries/*.md` | `/luminary` |
21
+ | Agents (specialist roles) | `.claude/agents/*.md` | Used by `/autonomous`, `/build`, `/longrun` |
22
+ | Hooks (mechanical enforcement) | `.claude/hooks/*.sh` | Fire automatically; see `/hooks` |
23
+ | Standards (reference docs) | `standards/*.md` | Cited by rules + skills |
24
+ | ADRs (architectural decisions) | `architecture/decisions/ADR-*.md` | Read before changing what they govern |
25
+
26
+ ## Cross-cutting gates — fire across every phase
27
+
28
+ | Gate | When to run |
29
+ |---|---|
30
+ | `/temperance` | Before first edit on a branch + at scope-decision boundaries |
31
+ | `/diagnose` | Before writing any fix code |
32
+ | `/quick-fix` | Localized, obvious, ≤10 lines, no blast radius |
33
+ | `/auto-fix` | Narrow non-trivial bugs within Builder's safe zone |
34
+ | `/verify` | After each task before committing |
35
+ | `/whats-the-plan` | Before any ≥3-skill chain |
36
+ | `/kiss` | When scope bloats or prose drifts toward jargon |
37
+ | `/luminary` | Before ADR authoring + at any design-decision boundary |
38
+
39
+ ## SDLC flow
40
+
41
+ ```
42
+ Canvas → Spec → Design → Issue → Branch → PR → CI → Deploy → Observe
43
+ ```
44
+
45
+ For UI work, run the UX translation chain during Design:
46
+
47
+ ```
48
+ JTBD → HTA → Entity Model → State Diagrams → Sequence Diagrams → /decompose
49
+ ```
50
+
51
+ ## Conventions
52
+
53
+ - **Commits**: `type: description` per `.claude/rules/commit-conventions.md`
54
+ - **PR titles**: ≤72 chars per `.claude/rules/pr-title-shape.md`
55
+ - **PR bodies**: open with `**Problem (≤500 chars):**` per `.claude/rules/pr-body-shape.md`
56
+ - **Specs** reference their source canvas or issue
57
+ - **New standards** go in `standards/`, not inline in other docs
58
+
59
+ ## What NOT to do
60
+
61
+ - **Don't break adopters** (ADR-031). Every change to a downstream-observable surface ships with a compat shim, migration manifest, and changelog entry.
62
+ - **Don't make architectural or design decisions without consulting luminaries.** Design without a named lens drifts toward generic; the lens is what gives you a position to defend in review.
63
+ - **Don't release without checking prior open PRs first.** Cumulative stacks create cascade failures.
64
+ - **Don't write source before tests** on Tier 0 paths (per `.claude/rules/testing-tier-config.md`).
65
+ - **Don't auto-apply discipline changes without `/temperance`** at scope boundaries.
66
+ - **Don't conflate "propose" with "execute".** When operator says "propose," "options," "path forward" — wait for explicit go signal.
67
+ - **Don't use bassclef-internal jargon** in operator-facing prose. Plain English at grade-10 reading level.
68
+ - **Don't push to main without PR review** even on solo workflows.
69
+
70
+ ## Luminaries at design boundaries
71
+
72
+ The substrate ships ~38 luminaries (Linus, Beck, Brooks, Norman, Cooper, Tufte, Nygard, Vernon, Ousterhout, Hyrum, Myers, Hoare, Saltzer-Schroeder, Parnas, Fowler, and more). They pin specific disciplines — they aren't decoration.
73
+
74
+ When to consult:
75
+
76
+ - **Architectural decisions (ADRs)** — pin 1-3 luminaries; the ADR cites their lens
77
+ - **Iteration bets** — declare a luminary map (primary pair + extended pairs per surface in scope) per `.claude/rules/iteration-bet-brief-completeness.md`
78
+ - **UI surfaces** — Norman / Cooper / Tufte / Morville per the surface's job
79
+ - **Cross-cutting design choices** — Bulkhead (Nygard) + Anticorruption Layer (Vernon) + Deep Modules (Ousterhout) converge often
80
+
81
+ `/luminary <slug>` previews one; `/luminary` browses by discipline. Skills with UI / architecture surfaces pin their luminary map in frontmatter — read the skill before invoking.
82
+
83
+ ## Luminaries at the keyboard — not just the whiteboard
84
+
85
+ Beyond ADRs and iteration bets, luminaries inform code authoring directly. **Pick ONE dominant lens per scope** (bet / WU / surface) and check your work against it. Background discipline fires automatically — don't manually verify against each.
86
+
87
+ | Scope | Default dominant authoring luminary |
88
+ |---|---|
89
+ | Most code | @luminary kent-beck (TDD rhythm + test list + simple design) |
90
+ | Bash hooks + scripts | @luminary saltzer-schroeder (7 defensive disciplines) |
91
+ | State-spine writes | @luminary tony-hoare (pre/postconditions; contracts) |
92
+ | UI components | Whichever pair the prototype pinned |
93
+ | Architecture refactoring | @luminary john-ousterhout (deep modules) + @luminary martin-fowler (refactoring patterns) |
94
+
95
+ **Background discipline** (fires automatically — don't manually verify):
96
+
97
+ - **Rules cite anchor luminaries** explaining why each rule exists. Read the rule; the anchor tells you when the rule generalizes vs when an edge case applies
98
+ - **Hooks enforce specific luminary-anchored disciplines mechanically** — `testing-tier-enforce.sh` (Beck), `state-validate.sh` (Hoare), `substrate-clarity-gate.sh` (context engineering)
99
+ - **`@pattern <catalog-path>` annotation** in source code when you CONSCIOUSLY instantiate a named pattern (GoF, Fowler, Vernon DDD). Not every class — only the ones that EMBODY a pattern as a design choice. Per `.claude/rules/pattern-annotation.md`
100
+
101
+ ## Luminary matching — tier ladder
102
+
103
+ `/pick-luminaries` picks luminary fits for an intent paragraph. Adopter capability scales with what keys you provide:
104
+
105
+ - **lite** — stub / deterministic mode; no keys required; for cold-start adopters + CI + tests
106
+ - **standard** — single LLM call picks from the full catalog; semantic matching; needs `ANTHROPIC_API_KEY` (default in bassclef-core)
107
+ - **ultra** — embedding shortlist (Voyage `voyage-3-lite`) + LLM judge re-rank; faster + per-candidate confidence; needs `ANTHROPIC + VOYAGE` keys; load `bassclef-upstream` via `additionalDirectories` to activate
108
+
109
+ Pre-computed catalog embeddings (~230KB) ship with bassclef-upstream so ultra-tier Voyage cost is per-query intent embedding only.
110
+
111
+ Pick the tier that matches your trust + budget. Upgrade path is additive — no migration required to move lite → standard → ultra. See bassclef-upstream#262 for ultra-tier downstream-readiness status; bassclef-upstream#263 for the future "plus" tier (local embedding model, no Voyage key).
112
+
113
+ ## Discovery commands
114
+
115
+ - `/sprint` — what's in flight + what's next
116
+ - `/whereami` — current project state
117
+ - `/substrate-check` — validate substrate wiring
118
+ - `/skills` — phase-grouped skill catalog
119
+ - `/luminary` — design-lens catalog
120
+ - `/hooks` — mechanical-enforcement layer
@@ -0,0 +1,132 @@
1
+ #!/bin/bash
2
+ # tier: lite
3
+ # adr-discipline-check.sh — PreToolUse hook on architectural decision boundaries
4
+ #
5
+ # Detects "load-bearing" architectural decisions via path-shape heuristics and
6
+ # refuses unless an ADR marker at state/markers/adr/<decision-slug>.md exists
7
+ # linking to a real ADR file under docs/adrs/ or architecture/decisions/.
8
+ #
9
+ # Per bassclef#1149 + WU-11 of bet 2026-06-20e.
10
+ # Override: SKIP_ADR_CHECK=1 (logged via trace-helper).
11
+
12
+ set -euo pipefail
13
+
14
+ # ============================================================================
15
+ # Read stdin JSON from Claude Code harness
16
+ # ============================================================================
17
+ PAYLOAD=$(cat 2>/dev/null || echo '{}')
18
+
19
+ TOOL_NAME=$(echo "$PAYLOAD" | jq -r '.tool_name // empty' 2>/dev/null)
20
+ FILE_PATH=$(echo "$PAYLOAD" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
21
+
22
+ # ============================================================================
23
+ # Filters: only fire on Write/Edit/MultiEdit
24
+ # ============================================================================
25
+ case "$TOOL_NAME" in
26
+ Write|Edit|MultiEdit) ;;
27
+ *) exit 0 ;;
28
+ esac
29
+
30
+ if [ -z "$FILE_PATH" ]; then
31
+ exit 0
32
+ fi
33
+
34
+ # ============================================================================
35
+ # Override check
36
+ # ============================================================================
37
+ if [ "${SKIP_ADR_CHECK:-0}" = "1" ]; then
38
+ TRACE_HELPER="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/trace-helper.sh"
39
+ if [ -f "$TRACE_HELPER" ]; then
40
+ bash "$TRACE_HELPER" override "adr-discipline-check" "SKIP_ADR_CHECK=1 on $FILE_PATH" 2>/dev/null || true
41
+ fi
42
+ exit 0
43
+ fi
44
+
45
+ # ============================================================================
46
+ # Architectural-decision detection via path-shape heuristics
47
+ # ============================================================================
48
+ IS_ARCHITECTURAL=0
49
+ DECISION_SLUG=""
50
+
51
+ case "$FILE_PATH" in
52
+ */prisma/schema.prisma|prisma/schema.prisma)
53
+ IS_ARCHITECTURAL=1
54
+ DECISION_SLUG="schema-change"
55
+ ;;
56
+ */docker-compose*.yml|docker-compose*.yml|*/docker-compose*.yaml|docker-compose*.yaml)
57
+ IS_ARCHITECTURAL=1
58
+ DECISION_SLUG="deployment-topology-change"
59
+ ;;
60
+ */middleware.ts|src/middleware.ts|*/middleware.py|*/middleware.rb)
61
+ IS_ARCHITECTURAL=1
62
+ DECISION_SLUG="middleware-change"
63
+ ;;
64
+ */alembic/versions/*.py|*/db/migrate/*.rb|*/migrations/*.go)
65
+ IS_ARCHITECTURAL=1
66
+ DECISION_SLUG="schema-migration"
67
+ ;;
68
+ esac
69
+
70
+ # package.json / pyproject.toml / Gemfile / go.mod / Cargo.toml — only fire on Write (new file or rewrite)
71
+ # Detecting "dependency-add" inside Edit would require diff parsing; deferred to V2
72
+ if [ "$TOOL_NAME" = "Write" ]; then
73
+ case "$FILE_PATH" in
74
+ */package.json|package.json|*/pyproject.toml|pyproject.toml|*/Gemfile|Gemfile|*/go.mod|go.mod|*/Cargo.toml|Cargo.toml)
75
+ IS_ARCHITECTURAL=1
76
+ DECISION_SLUG="dependency-manifest-change"
77
+ ;;
78
+ esac
79
+ fi
80
+
81
+ if [ "$IS_ARCHITECTURAL" -eq 0 ]; then
82
+ exit 0
83
+ fi
84
+
85
+ # ============================================================================
86
+ # Marker check: state/markers/adr/<decision-slug>.md must exist
87
+ # ============================================================================
88
+ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
89
+ MARKER_DIR="$REPO_ROOT/state/markers/adr"
90
+
91
+ # Explicit marker required — generic "recent ADR in dir" is too permissive
92
+ # (other ADRs unrelated to this decision class would silently satisfy)
93
+ if find "$MARKER_DIR" -maxdepth 1 -name "${DECISION_SLUG}*.md" 2>/dev/null | grep -q .; then
94
+ exit 0
95
+ fi
96
+
97
+ # ============================================================================
98
+ # Block: no marker + no recent ADR
99
+ # ============================================================================
100
+ cat >&2 <<EOF
101
+ 🛑 adr-discipline-check — BLOCK (bassclef#1149)
102
+
103
+ You're about to make a load-bearing architectural decision: ${DECISION_SLUG}
104
+
105
+ Path: ${FILE_PATH}
106
+
107
+ Architectural decisions require an ADR (Architecture Decision Record)
108
+ per Michael Nygard's lifecycle: proposed → accepted → deprecated → superseded.
109
+
110
+ Write one before proceeding:
111
+
112
+ 1. Copy the template:
113
+ cp standards/adr-template.md docs/adrs/ADR-NNN-<slug>.md
114
+ (or architecture/decisions/ADR-NNN-<slug>.md per your project layout)
115
+
116
+ 2. Fill Context + Decision + Status + Consequences sections.
117
+
118
+ 3. Touch the marker:
119
+ mkdir -p ${MARKER_DIR}
120
+ touch ${MARKER_DIR}/${DECISION_SLUG}-<your-slug>.md
121
+
122
+ 4. Retry the original change.
123
+
124
+ Override (logged): SKIP_ADR_CHECK=1 <command>
125
+
126
+ When to use override:
127
+ - Trivial revert of a previously-decided architectural change
128
+ - Emergency rescue when the hook itself misclassifies
129
+ - One-shot migration script that touches many architectural files
130
+ EOF
131
+
132
+ exit 2
@@ -0,0 +1,169 @@
1
+ #!/bin/bash
2
+ # tier: lite
3
+ # Atomic-PR Check — fires on Bash tool calls matching "git push" patterns.
4
+ # Counts distinct WU-N tags in commit messages on the branch. BLOCKs the
5
+ # push if multiple step tags exist unless an iteration goal declares
6
+ # `bundle: true` in frontmatter.
7
+ #
8
+ # Phase II of 8-phase staircase: Trim the Fat (bassclef#382).
9
+ # The methodology rule at .claude/rules/pr-strategy.md is the reasoning;
10
+ # this hook is the mechanical enforcement.
11
+ #
12
+ # Exit codes:
13
+ # 0 — allow (no WU tags, single WU tag, bundle:true in matching bet,
14
+ # or override)
15
+ # 2 — block (multi-step branch without bundle:true)
16
+ #
17
+ # Override: SKIP_ATOMIC_PR_CHECK=1 to bypass (logged via trace-helper)
18
+ #
19
+ # Rule: .claude/rules/pr-strategy.md
20
+ # Issue: bassclef#382
21
+
22
+ set +e # never crash the tool call; fall through to allow on any unexpected error
23
+
24
+ INPUT=$(cat)
25
+ TOOL_NAME=$(jq <<< "$INPUT" -r '.tool_name // ""' 2>/dev/null)
26
+ COMMAND=$(jq <<< "$INPUT" -r '.tool_input.command // ""' 2>/dev/null)
27
+
28
+ # Only fire on Bash with a git push command
29
+ if [ "$TOOL_NAME" != "Bash" ]; then
30
+ exit 0
31
+ fi
32
+
33
+ # Match git push patterns (allow flags like -u, --force, etc.)
34
+ case "$COMMAND" in
35
+ *"git push"*)
36
+ : # match — continue
37
+ ;;
38
+ *)
39
+ exit 0
40
+ ;;
41
+ esac
42
+
43
+ # Override hatch — log and allow
44
+ if [ "${SKIP_ATOMIC_PR_CHECK:-0}" = "1" ]; then
45
+ SCRIPT_DIR="$(cd "$(dirname "$0")" 2>/dev/null && pwd)"
46
+ if [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/trace-helper.sh" ]; then
47
+ source "$SCRIPT_DIR/trace-helper.sh" 2>/dev/null || true
48
+ trace_log "atomic-pr-check" "OVERRIDE git push" 2>/dev/null || true
49
+ fi
50
+ exit 0
51
+ fi
52
+
53
+ # Find the repo root
54
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
55
+ if [ -z "$REPO_ROOT" ]; then
56
+ exit 0 # not in a git repo — allow
57
+ fi
58
+ cd "$REPO_ROOT" || exit 0
59
+
60
+ # Get current branch name
61
+ BRANCH=$(git branch --show-current 2>/dev/null)
62
+ if [ -z "$BRANCH" ]; then
63
+ exit 0 # detached HEAD or no branch — allow
64
+ fi
65
+
66
+ # Skip the check on main branch and bare branch names
67
+ case "$BRANCH" in
68
+ main|master|HEAD)
69
+ exit 0
70
+ ;;
71
+ esac
72
+
73
+ # Find commits on this branch not yet on main
74
+ # If main is not available locally, fall back to origin/main, then to all commits
75
+ BASE_REF=""
76
+ if git rev-parse --verify main >/dev/null 2>&1; then
77
+ BASE_REF="main"
78
+ elif git rev-parse --verify origin/main >/dev/null 2>&1; then
79
+ BASE_REF="origin/main"
80
+ fi
81
+
82
+ if [ -n "$BASE_REF" ]; then
83
+ COMMITS=$(git log --format="%s%n%b" "${BASE_REF}..HEAD" 2>/dev/null)
84
+ else
85
+ # No base ref available — check the last 10 commits as a heuristic
86
+ COMMITS=$(git log -10 --format="%s%n%b" HEAD 2>/dev/null)
87
+ fi
88
+
89
+ # Extract distinct WU-N tags from commit messages
90
+ # Match patterns: "WU-1", "WU-2a", "WU-3", etc. (case-sensitive; expects WU-)
91
+ WU_TAGS=$(echo "$COMMITS" | grep -oE '\bWU-[0-9]+[a-z]?\b' | sort -u)
92
+
93
+ # Count distinct WU tags. If WU_TAGS is empty, count is 0.
94
+ if [ -z "$WU_TAGS" ]; then
95
+ WU_COUNT=0
96
+ else
97
+ WU_COUNT=$(echo "$WU_TAGS" | wc -l | tr -d ' ')
98
+ fi
99
+
100
+ # 0 or 1 WU tags — allow
101
+ if [ "$WU_COUNT" -le 1 ]; then
102
+ exit 0
103
+ fi
104
+
105
+ # Multi-WU branch — check for bundle:true in any matching goal doc
106
+ ALLOWED=0
107
+ ITERATION_BETS_DIR="docs/iteration-bets"
108
+ if [ -d "$ITERATION_BETS_DIR" ]; then
109
+ # Look for goal docs that mention any of the step tags AND have bundle: true
110
+ for bet in "$ITERATION_BETS_DIR"/*.md; do
111
+ [ -f "$bet" ] || continue
112
+ # Check if bet has bundle: true in frontmatter
113
+ if awk '/^---$/{c++; next} c==1 && /^bundle:[[:space:]]*true/{print "yes"; exit}' "$bet" | grep -q "yes"; then
114
+ # Verify this goal covers at least one of the step tags
115
+ for wu in $WU_TAGS; do
116
+ if grep -qE "\\b${wu}\\b" "$bet"; then
117
+ ALLOWED=1
118
+ break 2
119
+ fi
120
+ done
121
+ fi
122
+ done
123
+ fi
124
+
125
+ if [ "$ALLOWED" = "1" ]; then
126
+ exit 0
127
+ fi
128
+
129
+ # BLOCK — multi-step branch without bundle:true
130
+ WU_LIST=$(echo "$WU_TAGS" | tr '\n' ' ' | sed 's/ $//')
131
+
132
+ cat >&2 <<EOF
133
+
134
+ ============================================
135
+ 🛑 ATOMIC-PR GATE — BLOCKED 🛑
136
+ ============================================
137
+
138
+ Branch: $BRANCH
139
+ Commits found tagged with multiple steps: $WU_LIST
140
+
141
+ Per .claude/rules/pr-strategy.md, the default is one step per branch
142
+ per PR (atomic). Multiple WUs in one branch defeats the rollback
143
+ discipline — reverting one step also reverts the others.
144
+
145
+ Three resolution paths:
146
+
147
+ 1. Split — create a separate branch per step and a PR for each.
148
+ Operator merges in dependency order.
149
+
150
+ 2. Bundle — if these WUs genuinely belong together (single concern,
151
+ tight coupling, emergency hotfix), declare it explicitly:
152
+ add 'bundle: true' to the iteration goal's frontmatter, then push.
153
+
154
+ 3. Override (rare) — SKIP_ATOMIC_PR_CHECK=1 git push ...
155
+ Logged to trace. Use only for genuine bypass cases.
156
+
157
+ Per pr-strategy.md the bundle option requires one of:
158
+ - Single logical concern across files (changes don't compile if split)
159
+ - Tight coupling that breaks atomic /verify
160
+ - Emergency hotfix
161
+ - Explicit operator request
162
+
163
+ Rule: .claude/rules/pr-strategy.md
164
+ Issue: bassclef#382
165
+ ============================================
166
+
167
+ EOF
168
+
169
+ exit 2
@@ -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