@thebassclef/lite 0.1.0 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/dist/cli.cjs +40 -6
  2. package/dist/cli.js +40 -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/package.json +1 -1
  7. package/substrate/.bassclef/lite-manifest.json +999 -92
  8. package/substrate/.claude/hooks/longrun-prep-compounding-axis-check.sh +492 -0
  9. package/substrate/.claude/hooks/longrun-prep-compounding-sequence-check.sh +492 -0
  10. package/substrate/.claude/hooks/pre-commit-gate.sh +1 -2
  11. package/substrate/.claude/hooks/turn-prose-kiss-check.sh +30 -1
  12. package/substrate/.claude/luminaries/glenford-myers.md +230 -0
  13. package/substrate/.claude/luminaries/hunt-thomas.md +115 -0
  14. package/substrate/.claude/luminaries/hyrum-wright.md +94 -0
  15. package/substrate/.claude/luminaries/michael-feathers.md +2 -2
  16. package/substrate/.claude/luminaries/tony-hoare.md +170 -0
  17. package/substrate/.claude/luminaries/vaughn-vernon.md +50 -0
  18. package/substrate/.claude/luminaries/w-edwards-deming.md +158 -0
  19. package/substrate/.claude/rules/accessor-library-discipline.md +138 -0
  20. package/substrate/.claude/rules/adr-discipline.md +120 -0
  21. package/substrate/.claude/rules/api-conventions.md +125 -0
  22. package/substrate/.claude/rules/bootstrap-pair-discipline.md +141 -0
  23. package/substrate/.claude/rules/cold-adopter-harness-discipline.md +129 -0
  24. package/substrate/.claude/rules/compounding-axis-fresh-analysis.md +188 -0
  25. package/substrate/.claude/rules/compounding-sequence-fresh-analysis.md +188 -0
  26. package/substrate/.claude/rules/defensive-bash.md +68 -0
  27. package/substrate/.claude/rules/deferred-actions.md +233 -0
  28. package/substrate/.claude/rules/github-issue-flash-tweet.md +156 -0
  29. package/substrate/.claude/rules/hook-wire-on-author.md +103 -0
  30. package/substrate/.claude/rules/iteration-bet-brief-completeness.md +54 -0
  31. package/substrate/.claude/rules/lite-manifest-schema-change-discipline.md +3 -3
  32. package/substrate/.claude/rules/longrun-prep-plan-doc-compression.md +89 -0
  33. package/substrate/.claude/rules/loop-discipline.md +81 -0
  34. package/substrate/.claude/rules/manual-prod-approval.md +100 -0
  35. package/substrate/.claude/rules/marker-enrichment-discipline.md +99 -0
  36. package/substrate/.claude/rules/mobile-ephemeral-session.md +109 -0
  37. package/substrate/.claude/rules/new-dependency-check.md +51 -0
  38. package/substrate/.claude/rules/option-label-discipline.md +108 -0
  39. package/substrate/.claude/rules/pattern-annotation.md +100 -0
  40. package/substrate/.claude/rules/plain-english-discipline.md +11 -9
  41. package/substrate/.claude/rules/plan-enumeration-needs-value-props.md +211 -0
  42. package/substrate/.claude/rules/pr-title-shape.md +161 -0
  43. package/substrate/.claude/rules/prototype-workflow.md +65 -0
  44. package/substrate/.claude/rules/reserved-skill-names.md +123 -0
  45. package/substrate/.claude/rules/schema-management.md +49 -0
  46. package/substrate/.claude/rules/security.md +37 -0
  47. package/substrate/.claude/rules/skill-composition-declarations.md +124 -0
  48. package/substrate/.claude/rules/skill-description-clarity.md +247 -0
  49. package/substrate/.claude/rules/skill-procedure-step-list.md +137 -0
  50. package/substrate/.claude/rules/stuck-signal-diagnostic.md +140 -0
  51. package/substrate/.claude/rules/substrate-config-schema.md +98 -0
  52. package/substrate/.claude/rules/test-list-discipline.md +175 -0
  53. package/substrate/.claude/rules/test-sufficiency.md +210 -0
  54. package/substrate/.claude/rules/testing-tier-config.md +145 -0
  55. package/substrate/.claude/rules/testing.md +38 -0
  56. package/substrate/.claude/rules/turn-estimate-grounding.md +134 -0
  57. package/substrate/.claude/rules/visual-hierarchy.md +437 -0
  58. package/substrate/.claude/rules/we-dont-break-adopters.md +126 -0
  59. package/substrate/.claude/rules/wu-sequencing-compounds.md +145 -0
  60. package/substrate/.claude/skills/build/SKILL.md +1 -1
  61. package/substrate/.claude/skills/chronicle/SKILL.md +55 -0
  62. package/substrate/.claude/skills/clean-artifacts/SKILL.md +249 -0
  63. package/substrate/.claude/skills/decompose/SKILL.md +1 -1
  64. package/substrate/.claude/skills/diagnose/SKILL.md +1 -1
  65. package/substrate/.claude/skills/feynman/SKILL.md +90 -0
  66. package/substrate/.claude/skills/howdoi/SKILL.md +1 -1
  67. package/substrate/.claude/skills/ia-model/SKILL.md +1 -1
  68. package/substrate/.claude/skills/interaction-design/SKILL.md +1 -1
  69. package/substrate/.claude/skills/interpret-input/SKILL.md +8 -8
  70. package/substrate/.claude/skills/journal/SKILL.md +209 -0
  71. package/substrate/.claude/skills/kiss/SKILL.md +1 -1
  72. package/substrate/.claude/skills/launch/SKILL.md +14 -23
  73. package/substrate/.claude/skills/lean-canvas/SKILL.md +1 -1
  74. package/substrate/.claude/skills/longrun/SKILL.md +45 -8
  75. package/substrate/.claude/skills/luminary/SKILL.md +1 -1
  76. package/substrate/.claude/skills/ogilvy-writing-audit/SKILL.md +1 -1
  77. package/substrate/.claude/skills/onboard-repo/SKILL.md +143 -709
  78. package/substrate/.claude/skills/pattern-review/SKILL.md +1 -1
  79. package/substrate/.claude/skills/personas/SKILL.md +5 -5
  80. package/substrate/.claude/skills/promote/SKILL.md +1 -1
  81. package/substrate/.claude/skills/requirement/SKILL.md +1 -1
  82. package/substrate/.claude/skills/retro/SKILL.md +1 -1
  83. package/substrate/.claude/skills/riff/SKILL.md +1 -1
  84. package/substrate/.claude/skills/roadmap-reconcile/SKILL.md +1 -1
  85. package/substrate/.claude/skills/session-end/SKILL.md +1 -1
  86. package/substrate/.claude/skills/session-log/SKILL.md +3 -3
  87. package/substrate/.claude/skills/skills/SKILL.md +1 -1
  88. package/substrate/.claude/skills/spec/SKILL.md +1 -1
  89. package/substrate/.claude/skills/sprint/SKILL.md +1 -1
  90. package/substrate/.claude/skills/stage/SKILL.md +1 -1
  91. package/substrate/.claude/skills/state-a-problem/SKILL.md +1 -1
  92. package/substrate/.claude/skills/temperance/SKILL.md +1 -1
  93. package/substrate/.claude/skills/use-case/SKILL.md +1 -1
  94. package/substrate/.claude/skills/user-stories/SKILL.md +1 -1
  95. package/substrate/.claude/skills/value-prop/SKILL.md +1 -1
  96. package/substrate/.claude/skills/verify/SKILL.md +1 -1
  97. package/substrate/.claude/skills/visual-review/SKILL.md +503 -0
  98. package/substrate/.claude/skills/whats-the-plan/SKILL.md +202 -0
  99. package/substrate/.claude/skills/whereami/SKILL.md +2 -2
  100. package/substrate/CONTRIBUTING.md +1 -1
  101. package/substrate/README.md +5 -5
  102. package/substrate/lib/prose-scan-boundary.sh +171 -0
  103. package/substrate/lib/tier-check.sh +50 -1
  104. package/substrate/lib/tier-dependency-audit.sh +159 -4
  105. package/substrate/presence/install/bassclef-sync.template.sh +1 -1
  106. package/substrate/scripts/generate-lite-manifest.sh +21 -5
  107. package/substrate/standards/adr-template.md +86 -0
  108. package/substrate/standards/api-conventions/nextjs.md +84 -0
  109. package/substrate/standards/artifact-composition.md +209 -0
  110. package/substrate/standards/bash-hook-safety.md +246 -0
  111. package/substrate/standards/branch-stacking.md +408 -0
  112. package/substrate/standards/code-safety-principles.md +176 -0
  113. package/substrate/standards/composer-prerequisites.md +155 -0
  114. package/substrate/standards/dependency-discipline/cargo.md +39 -0
  115. package/substrate/standards/dependency-discipline/gem.md +43 -0
  116. package/substrate/standards/dependency-discipline/go-mod.md +41 -0
  117. package/substrate/standards/dependency-discipline/npm.md +42 -0
  118. package/substrate/standards/dependency-discipline/pip.md +42 -0
  119. package/substrate/standards/deployment-topology/ec2-tailscale.md +225 -0
  120. package/substrate/standards/deployment-topology.md +69 -0
  121. package/substrate/standards/docs-sync-allowlist.md +4 -4
  122. package/substrate/standards/domain-and-dns.md +145 -0
  123. package/substrate/standards/frontend-stack.md +67 -0
  124. package/substrate/standards/frontmatter-schema.md +154 -0
  125. package/substrate/standards/hook-injection-discipline.md +202 -0
  126. package/substrate/standards/hook-install-class.md +215 -0
  127. package/substrate/standards/input-handler-interface.md +152 -0
  128. package/substrate/standards/lite-manifest-schema-changes.md +60 -0
  129. package/substrate/standards/luminary-matching.md +105 -0
  130. package/substrate/standards/migration-discipline/active-record.md +50 -0
  131. package/substrate/standards/migration-discipline/alembic.md +43 -0
  132. package/substrate/standards/migration-discipline/gorm.md +50 -0
  133. package/substrate/standards/migration-discipline/prisma.md +53 -0
  134. package/substrate/standards/migration-discipline/sqlalchemy.md +51 -0
  135. package/substrate/standards/mobile-ephemeral-session.md +167 -0
  136. package/substrate/standards/model-routing-discipline.md +160 -0
  137. package/substrate/standards/persona-schema.md +229 -0
  138. package/substrate/standards/pluggable-luminaries.md +323 -0
  139. package/substrate/standards/pr-body-discipline.md +115 -0
  140. package/substrate/standards/preview-state-schema.md +189 -0
  141. package/substrate/standards/reserved-skill-names.md +120 -0
  142. package/substrate/standards/scannable-multi-option-output.md +261 -0
  143. package/substrate/standards/sdlc-gates/typescript.md +57 -0
  144. package/substrate/standards/session-board.md +256 -0
  145. package/substrate/standards/state-spine-contract.md +255 -0
  146. package/substrate/standards/steering-hints/kiss-words.md +11 -0
  147. package/substrate/standards/substrate-config-schema.md +267 -0
  148. package/substrate/standards/tier-dependency-analysis.md +1 -1
  149. package/substrate/standards/tier-tag-schema.md +1 -1
  150. package/substrate/standards/two-layer-config.md +99 -0
  151. package/substrate/standards/use-case-format.md +292 -0
  152. package/substrate/standards/user-story-invest.md +268 -0
  153. package/substrate/standards/velocity-and-appetite.md +229 -0
  154. package/substrate/standards/voice-input-pattern.md +119 -0
  155. package/substrate/standards/worktree-management.md +211 -0
  156. package/substrate/templates/chronicle-template.md +75 -0
  157. package/substrate/templates/memory-proposal-template.md +77 -0
  158. package/substrate/templates/persona-template.md +200 -0
  159. package/substrate/templates/pr-faq.md +45 -0
  160. package/substrate/templates/secret-rotation-template.md +162 -0
  161. package/substrate/templates/spec-template.md +131 -0
  162. package/substrate/templates/use-case-template.md +194 -0
  163. package/substrate/templates/user-story-template.md +107 -0
@@ -0,0 +1,175 @@
1
+ ---
2
+ tier: lite
3
+ description: "Every test file opens with a # test-list: commented block enumerating the assertions the step owes."
4
+ ---
5
+
6
+ # Test-List Discipline
7
+
8
+ Every test file opens with a `# test-list:` commented block enumerating
9
+ the assertions the step owes. Each line is one intended assertion,
10
+ written before any source is edited. Implemented assertions get `[x]`;
11
+ pending stay `[ ]`. Remaining `[ ]` lines visible at commit time are
12
+ honest in-flight signal — truth about what is still owed.
13
+
14
+ This rule is the **planning layer** of TDD discipline. The Tier 0 hook
15
+ (bassclef#1037) enforces test presence + recency: test file exists, test
16
+ mtime ≤ source mtime. That catches tests written after source. It does
17
+ not catch tests written before source that miss intended assertions.
18
+
19
+ INSTEAD: Beck's test list surfaces intent as a tangible artifact at the
20
+ top of the test file, where reviewer and future-reader can see what was
21
+ considered AND deferred vs what was missed entirely.
22
+
23
+ ## Why this rule exists
24
+
25
+ Bassclef adopts Kent Beck as the TDD anchor — Tier 0 enforces his
26
+ red-green-refactor cycle mechanically. The Tier 0 framing came from
27
+ *Test-Driven Development By Example* chapter on cycle discipline.
28
+ Beck's same book opens (chapters 1-3) with the test list as a separate
29
+ discipline: write the list of tests you intend to write BEFORE you
30
+ start; cross off as you go; add new tests to the list as they surface.
31
+
32
+ Bassclef imported the cycle without importing the planning artifact.
33
+ Result: agents (and operators) write a test, see it pass, ship — and
34
+ the next reader has no record of which assertions were considered AND
35
+ deferred vs which were missed entirely. The two collapse into silence.
36
+
37
+ INSTEAD: the test-list block at the top of every test file makes the
38
+ record visible. Considered-and-deferred assertions live as `[~]` lines
39
+ with one-line reasons; missed assertions get added as `[ ]` lines as
40
+ soon as a reviewer or later reader notices them.
41
+
42
+ Surfaced 2026-06-17 during /longrun prep when operator pushed twice on
43
+ "feels incomplete" sufficiency framing. The mechanical sufficiency
44
+ criteria (sister test-sufficiency rule) IS the answer to what to assert;
45
+ the test list IS the answer to whether you covered the intent. Both are
46
+ required.
47
+
48
+ ## Format contract
49
+
50
+ Every test file (any file matching `*test*.sh`, `*.test.*`, `test_*.py`,
51
+ `*_test.go`, etc. depending on stack) opens with this block before any
52
+ test code:
53
+
54
+ ```bash
55
+ # test-list:
56
+ # [x] Skip case: empty stdin returns exit 0
57
+ # [x] Skip case: non-matching tool_name returns exit 0
58
+ # [ ] Behavior: matched tool with valid input writes marker
59
+ # [ ] Behavior: matched tool with invalid input returns exit 3 with stderr
60
+ # [ ] Override: SKIP_FOO=1 bypasses and logs to trace
61
+ # [ ] Edge: missing external binary (jq) returns exit 0 with warning
62
+ ```
63
+
64
+ Rules:
65
+
66
+ - `# test-list:` is the literal marker (greppable; future hook reads it)
67
+ - One assertion per line; `[ ]` pending, `[x]` implemented
68
+ - Each line is one sentence in plain English describing the assertion
69
+ - Lines added as new assertions surface during implementation (the list grows)
70
+ - Lines stay in place when an assertion turns out not to apply — mark `[~]` with one-line reason inline.
71
+
72
+ INSTEAD of removing the line: keep the `[~]` record so future-reader sees the assertion was considered and judged not-applicable, with the reason. Removal loses the consideration history.
73
+
74
+ - The block lives at the top of the test file, before any source code
75
+
76
+ ## What this rule requires
77
+
78
+ When authoring or extending a test file:
79
+
80
+ 1. **Before writing any test code**, write the `# test-list:` block enumerating intended assertions. Use the test-sufficiency rubric (sister rule) as the source for what categories of assertion a hook test needs.
81
+ 2. **As each assertion is implemented**, change its `[ ]` to `[x]` in the same commit that ships the test.
82
+ 3. **If new assertions surface during implementation**, add them to the list as `[ ]` lines. The list grows; that is honest signal.
83
+ 4. **Before commit**, scan remaining `[ ]` lines. Each is either:
84
+ - Implemented in this commit (mark `[x]`)
85
+ - Genuinely deferred (mark `[~]` with one-line reason; commit that reason as the file's record)
86
+ - Required but missed — write the assertion now and mark `[x]`.
87
+
88
+ INSTEAD of committing with the line as `[ ]` when the assertion is owed by THIS commit: write the assertion, mark `[x]`, then commit. Pending lines are for genuinely-next-commit work.
89
+
90
+ ## What satisfies the rule
91
+
92
+ A test file is conformant when:
93
+
94
+ - Top of file has `# test-list:` block
95
+ - Every line in the block has `[x]`, `[ ]`, or `[~]` prefix
96
+ - Every `[x]` line maps to at least one test function in the file
97
+ - Every `[~]` line has a one-line reason inline
98
+ - Pending `[ ]` lines at commit time are intentional (operator-visible signal that work continues)
99
+
100
+ ## Anti-shapes (what fails the rule)
101
+
102
+ The hook-future-extension will BLOCK at these shapes. For now, reviewer + agent self-check.
103
+
104
+ - A test file with no `# test-list:` block at all.
105
+
106
+ INSTEAD: add the block before committing; even retrofitting a list from an existing test file's contents is better than missing one, because the act of writing the list surfaces assertions the existing tests missed.
107
+
108
+ - A test file with a block that contains only `[x]` lines while obvious assertions are missing.
109
+
110
+ INSTEAD: surface the gap by adding `[ ]` lines for the missing assertions; the visible gap is the call-to-action for the next commit.
111
+
112
+ - A `[~]` line without inline reason.
113
+
114
+ INSTEAD: add the one-line reason in the same commit that marks the line `[~]`; the reason IS the audit trail.
115
+
116
+ - A `[x]` line with no matching test function.
117
+
118
+ INSTEAD: either implement the test function (if the assertion was meant to ship) OR change `[x]` to `[~]` with reason (if the assertion turned out not to apply).
119
+
120
+ ## When this rule fires
121
+
122
+ Auto-loaded into every session via `additionalDirectories`. Agents
123
+ self-check before committing any test file. The Tier 0 hook
124
+ (`testing-tier-enforce.sh`) does not currently enforce this block.
125
+
126
+ INSTEAD: methodology-level enforcement via agent + reviewer attention.
127
+ Mechanical extension deferred to follow-on ticket; see V2 section below.
128
+
129
+ ## V2 — mechanical extension (deferred)
130
+
131
+ When operator observes recurring drift from manual self-check, file a
132
+ follow-on ticket extending Tier 0 hook with a grep for `# test-list:`
133
+ block in any test file being committed. BLOCK when missing. Allowlist
134
+ existing test files in a sister `.claude/hooks/test-list-allowlist.txt`
135
+ with grace window (same pattern as Phase A shellcheck-allowlist).
136
+
137
+ For now: methodology layer. Reviewer + agent self-check.
138
+
139
+ ## Override
140
+
141
+ `SKIP_TEST_LIST=1` — logged via trace-helper per bassclef hook
142
+ override idiom. Use only for migration commits backfilling old test
143
+ files OR for emergency rescue when the rule itself appears wrong.
144
+
145
+ INSTEAD of overriding for routine work: write the test-list block. The
146
+ block costs ~30 seconds of typing; the override cost compounds across
147
+ every consumer repo's reviewer attention.
148
+
149
+ ## Composes with
150
+
151
+ - `.claude/rules/testing-tier-config.md` — Tier 0 enforces presence + recency; this adds intent transparency
152
+ - `.claude/rules/test-sufficiency.md` — 10-criterion mechanical sufficiency rubric; supplies WHAT to assert; this supplies HOW to track
153
+ - `.claude/rules/testing.md` — what makes a good test (test-content guidance); this adds the planning artifact
154
+ - `@luminary kent-beck` — TDD anchor; chapters 1-3 of TDD By Example are the bassclef source
155
+
156
+ ## Distinct from
157
+
158
+ - `testing-tier-config.md` — about WHICH paths require which tier of enforcement
159
+ - `testing.md` — about what makes a test good once you write it
160
+ - `test-sufficiency.md` — about what categories of assertion to consider
161
+ - `iteration-bet-brief-completeness.md` — about handoff brief completeness; different scope
162
+
163
+ ## Refs
164
+
165
+ - bassclef#1090 — this rule's filing ticket
166
+ - bassclef#1037 — Tier 0 strict-TDD discipline (Phase A); this extends with intent transparency
167
+ - bassclef#867 — anchor-luminary verification pattern (Beck as TDD anchor)
168
+ - Beck — Test-Driven Development By Example (Addison-Wesley, 2002), chapters 1-3
169
+ - `docs/iteration-bets/2026-06-17b-sufficiency-rubric-plus-hardening-longrun.md` — goal authoring this rule
170
+
171
+ ## Retirement condition
172
+
173
+ This rule retires only if bassclef migrates off TDD as a primary
174
+ discipline. The test list as a planning artifact survives any specific
175
+ test-framework or tier-config evolution.
@@ -0,0 +1,210 @@
1
+ ---
2
+ tier: lite
3
+ description: The Tier 0 hook (bassclef#1037) enforces test PRESENCE + RECENCY.
4
+ ---
5
+
6
+ # Test Sufficiency Rubric
7
+
8
+ The Tier 0 hook (bassclef#1037) enforces test PRESENCE + RECENCY. The test file exists. Its mtime is at least as new as the source file's. That catches tests written after source. It catches zero things about whether the test ASSERTS ENOUGH.
9
+
10
+ This rule codifies the 12 mechanical criteria the agent applies before committing any test file. Each criterion is greppable, countable, and verifiable against the source it tests. Together they form the sufficiency contract for bassclef's Tier 0 hooks, state-spine accessors, and adopter-stack code where systematic discipline earns its keep.
11
+
12
+ INSTEAD of relying on "feels complete" — the squishy framing that surfaced this gap during /longrun prep 2026-06-17 — the agent applies the 12 criteria mechanically. The agent marks each as covered or deferred with reason in the test-list block (bassclef#1090) and ships the result.
13
+
14
+ ## Anchor luminaries
15
+
16
+ - `@luminary kent-beck` — TDD rhythm anchor; test list is the planning artifact this rubric assumes
17
+ - `@luminary glenford-myers` — Coverage taxonomy anchor; criteria 1-4 are direct applications of Myers's 1979 categories
18
+ - `@luminary tony-hoare` — Contract testing anchor; criteria 5-10 are precondition / postcondition / invariant assertions in disguise
19
+
20
+ The rubric is what each luminary's lens produces when applied to a bassclef bash hook or state-spine accessor. The substrate operationalizes the discipline; the luminaries supply the why.
21
+
22
+ ## The 12 criteria
23
+
24
+ ### 1. Branch coverage (Myers — decision coverage)
25
+
26
+ Every branch in the source has at least one test that exercises it. Mechanically: `grep -cE '^\s*(if|elif|else|case|\|\|)' <source>` counts decision points; the test file owes that many `[x]` lines minimum.
27
+
28
+ Example for `auto-save-idle.sh` with 4 branches (no-changes / lock-present / defer-present / proceed): the test file owes 4 tests minimum to satisfy decision coverage alone.
29
+
30
+ INSTEAD of one test that covers the happy path: one test per branch. The marginal cost per test is small at write-time and compounds across every consumer adopting the hook.
31
+
32
+ ### 2. Exit code matrix (Hoare — postcondition)
33
+
34
+ Every distinct `exit N` in the source has at least one test that triggers that exit and asserts the code matches. Mechanically: `grep -oE 'exit [0-9]+' <source> | sort -u` enumerates the codes; the test file owes one assertion per code.
35
+
36
+ Example: `pre-commit-gate.sh` has `exit 0` (pass), `exit 2` (BLOCK), `exit 3` (validation fail). Test file asserts each appears under the input that triggers it.
37
+
38
+ INSTEAD of asserting only success exit: one assertion per distinct exit code. Exit codes ARE the hook's postcondition contract with the harness; the test list owes one Hoare-style `{P} S {Q-exit-N}` triple per code.
39
+
40
+ ### 3. Tool-name matcher (Myers — equivalence partitioning)
41
+
42
+ For hooks matching on `tool_name`, the test file has one test per matched tool AND at least one non-matched tool asserting pass-through. Each matched tool is one equivalence class; the non-matched set is another class with one representative.
43
+
44
+ Example: `pre-commit-gate.sh` matches `Edit|Write|MultiEdit`. Test file owes: Edit-fires test, Write-fires test, MultiEdit-fires test, Bash-pass-through test, NotebookEdit-pass-through test.
45
+
46
+ INSTEAD of asserting only the matched class: cover both halves of the discriminator. Pass-through tests catch matcher regressions that would silently expand the hook's surface.
47
+
48
+ ### 4. Path-matcher coverage (Myers — boundary value)
49
+
50
+ For hooks matching on file paths or globs, the test file has a positive test (path matches) AND a near-miss negative test (similar path doesn't match) per glob. Boundary value applied to string-shape inputs.
51
+
52
+ Example: hook matches `state/markers/temperance/*.marker`. Positive test: `state/markers/temperance/foo.marker`. Near-miss tests: `state/markers/temperance/foo` (no extension), `state/markers/temperance-x/foo.marker` (similar dir name), `state/temperance/foo.marker` (missing markers segment).
53
+
54
+ INSTEAD of asserting only the matched path: near-miss tests catch glob bugs that would either widen or narrow the matcher unexpectedly.
55
+
56
+ ### 5. Override path (Hoare — alternative precondition)
57
+
58
+ Every `SKIP_*` env var bypass in the source has one test that exercises the override AND asserts the override path fires (logged via trace-helper, hook exits with pass code, side effects suppressed).
59
+
60
+ Example: `auto-save-idle.sh` honors `SKIP_AUTO_SAVE=1`. Test file owes: with-flag-set test (asserts no-commit + trace log emission), without-flag test (asserts normal path).
61
+
62
+ INSTEAD of treating override paths as unused: they are alternative preconditions in Hoare's framing. Each override is a `{P-skip-set} S {Q-bypass}` triple the test list owes one entry for.
63
+
64
+ ### 6. stdin contract (Hoare — precondition discipline)
65
+
66
+ The hook reads JSON from the harness via stdin. Each discriminated input shape (different `tool_input` fields, different content shapes) has one test that exercises it.
67
+
68
+ Example: `pre-commit-gate.sh` discriminates on `tool_input.file_path` (string) and absent (some Edit calls omit it). Test file owes: file_path-present test, file_path-absent test, malformed-JSON test (graceful error path).
69
+
70
+ INSTEAD of asserting only the well-formed input: malformed JSON is a precondition violation the hook must handle without crashing. Tests assert the hook reports the violation rather than crashing the harness.
71
+
72
+ ### 7. External-binary handling (Hoare — invariant under environment variation)
73
+
74
+ For every external call (`jq`, `gh`, `git`, `flock`, `python3`, etc.), tests either mock the binary OR exercise a graceful-missing test asserting the hook handles binary-absent without crashing.
75
+
76
+ Example: hook calls `jq` to parse JSON. Test file owes: jq-present happy-path test, jq-missing test (asserts hook detects and exits with informative stderr OR falls back to bash-native parsing).
77
+
78
+ INSTEAD of assuming every adopter environment has every binary: external binaries are an environmental precondition the hook should fail soft against. Bassclef-internal hooks may assume `jq` (Tier 0). Adopter-distributed hooks should not.
79
+
80
+ ### 8. Trap / cleanup (Hoare — class invariant via `trap EXIT`)
81
+
82
+ Sources that use `trap '...' EXIT` (per defensive-bash.md) need two tests: one exercises the success path (verify cleanup ran) AND one exercises a failure path (verify cleanup still ran).
83
+
84
+ Example: hook acquires `flock` and uses `trap "flock -u" EXIT`. Test file owes: success-path test (verify lock released on normal exit), failure-path test (verify lock released when hook fails mid-execution).
85
+
86
+ INSTEAD of trusting `trap` works by inspection: the trap IS the class invariant; the test verifies the invariant holds across both observable paths. Trap regressions silently leak resources.
87
+
88
+ ### 9. stderr format (Hoare — postcondition contract for BLOCK exits)
89
+
90
+ For BLOCK exits (code 2 or 3), the test asserts the stderr message contains the required structured fields per `.claude/rules/blocked-items.md`: entity type, path being acted on, ajv error details (when applicable), three resolution paths.
91
+
92
+ Example: `state-validate.sh` BLOCKs on schema failure. Test file owes: BLOCK-exit test asserts stderr contains "Entity type:", "Path:", "ajv:", and the three resolution-path lines.
93
+
94
+ INSTEAD of asserting only exit code on BLOCK: the stderr message IS the contract with the agent. A BLOCK with empty stderr is a worse failure than no BLOCK at all — the agent loses the remediation path.
95
+
96
+ ### 10. State-file interactions (Hoare — postcondition on side effects)
97
+
98
+ For hooks that read or write markers in `state/markers/` (or any state file), tests cover both read paths and write paths. Read-path test: state file present in expected shape → hook reads correctly. Write-path test: hook write completes → file content matches the expected shape.
99
+
100
+ Example: `pre-commit-gate.sh` reads `state/markers/temperance/<branch-slug>.marker`. Test file owes: marker-present test (verify hook reads + passes), marker-absent test (verify hook BLOCKs + writes BLOCKED stderr), marker-malformed test (verify hook fails-soft with informative error).
101
+
102
+ INSTEAD of testing only the read OR only the write: state-file interactions are bidirectional contracts. Skipping the write-path test means schema drift between writer and reader goes unnoticed until production.
103
+
104
+ ### 11. Path resolution under both install classes (Hoare — invariant under install layout; added bet 27c WU-9)
105
+
106
+ For hooks that read project resources (standards files, configs, libs) AND declare `# install-class: dual` per `standards/hook-install-class.md`, tests pin behavior under both install layouts. Project class (CLAUDE_PROJECT_DIR set). Operator class (HOME_OVERRIDE pointing at fixture user-install layout). Mechanically: at least one test per resource read that fires under each layout.
107
+
108
+ Example: `turn-prose-kiss-check.sh` reads `standards/bassclef-internal-jargon.md` via `resolve_standards_path` from `lib/hook-inject.sh`. The test file owes four tests — Test 54 (CLAUDE_PROJECT_DIR layer 1), Test 55 (KISS_STANDARDS_FILE env layer 2), Test 56 (HOME_OVERRIDE layer 3), Test 57 (all layers fail; defensive fallback). PR #427 ships exactly those four tests.
109
+
110
+ INSTEAD of testing only project install: install class is an environmental precondition the hook MUST honor under both layouts. Skipping the operator install test produces the silent fail pattern that surfaced 2026-06-27 (kiss path resolution defect; chronicle Gate Evidence row 1). The cure pattern (HOME_OVERRIDE fixture + Tests 54-57 shape) is reusable across every dual-class hook.
111
+
112
+ This criterion fires when the source under test declares `install-class: dual` (most steering hooks; some adopter-distributed project hooks). Source declares `install-class: project` only → criterion does not fire. Project resources always resolve cleanly under project install.
113
+
114
+ ### 12. Invocation-mode variation for coordinated fragments (Feathers + Hoare — invariant under invocation mode; added bet 2026-07-30c WU-3)
115
+
116
+ For modules invoked by a coordinator (fragments under `session-reflection.d/`, `pre-commit-hooks.d/`, or any `*.d/` directory that a parent hook sources), tests pin behavior under BOTH invocation modes:
117
+
118
+ - **Standalone**: `bash "$module"` — exit codes propagate normally; the module runs as its own process
119
+ - **Sourced**: coordinator sources the module under `set -e`; the module MUST use `return` (or safe dual form) not `exit`, else the coordinator dies with the module's exit code
120
+
121
+ Mechanically: at least one test per fragment that (a) sources it under a set -e wrapper and asserts a canary after the source fires, and (b) invokes it standalone via bash and asserts no "return: can only" error appears in stderr.
122
+
123
+ Example: `.claude/hooks/session-reflection.d/*.sh` fragments. Test file `.claude/hooks/tests/session-reflection-coordinator-full.test.sh` (bet 2026-07-30c WU-1) runs Class A (sourced-parent-survives) plus Class B (standalone-no-return-error) for every fragment. The wrapper injects the coordinator-side helpers (`blocked_banner`, `trace_log`, `BASSCLEF_DIR`) as stubs so the fragment's contract with the parent scope is honored during the test.
124
+
125
+ INSTEAD of trusting that `exit` in a sourced file is fine: sourced `exit` kills the parent process silently. The class shipped in PR #542 (2026-06-30 — `05-active-bet.sh`) and cost 36 days of silent session-start check skips in every adopter session until PR #992 cured 2026-07-30. Criterion 12 exists so the class cannot recur — every new fragment PR ships with sourced-parent-survives AND standalone-no-return-error assertions.
126
+
127
+ This criterion fires when the source lives under a `*.d/` directory whose parent hook sources fragments in a loop. Detection: the source directory name ends in `.d/` AND at least one sibling `.sh` file in the parent directory sources files from that directory. Adopter-authored fragments that plug into bassclef coordinators inherit this criterion at PR time.
128
+
129
+ Coordinator-side coupling note: fragments frequently call helper functions the coordinator defines (e.g., `blocked_banner`, `trace_log`) and read environment variables the coordinator exports (e.g., `BASSCLEF_DIR`). The invocation-mode test wrapper MUST inject these helpers as stubs so the test characterizes the fragment under conditions matching real coordinator invocation. Skipping the stubs produces false-red on any fragment that calls a coordinator helper.
130
+
131
+ ## How to apply the rubric
132
+
133
+ 1. **Before writing any test**, list applicable criteria for the source under test. Some hooks have all 10; some (pure-function libraries) have fewer (no stdin contract, no override path).
134
+ 2. **Open the test file with the test-list block** (bassclef#1090). Use one `[ ]` line per criterion-derived assertion.
135
+ 3. **Implement each assertion**, marking `[x]` as each ships. Add new `[ ]` lines as additional cases surface during implementation.
136
+ 4. **Before commit**, scan the test-list for `[ ]` lines. Each is either:
137
+ - Implemented in this commit (mark `[x]`)
138
+ - Genuinely deferred (mark `[~]` with one-line reason per test-list-discipline.md)
139
+ - Required but missed (write the assertion now; do not commit with the line as `[ ]` when it should be `[x]`)
140
+ 5. **Verify mechanical counts** for criteria 1, 2, 7: branch count, exit count, external-binary count. The test count must equal or exceed each source count.
141
+
142
+ INSTEAD of relying on "feels complete": each criterion has a greppable verification. If the test file has fewer `[x]` lines than the source has branches, the rubric is not satisfied. Concrete, mechanical, auditable.
143
+
144
+ ## What the rubric does NOT cover
145
+
146
+ - **Test rhythm** — when to write tests (route to Beck via `testing-tier-config.md` Tier 0)
147
+ - **Test quality** — what makes a test well-written (route to `.claude/rules/testing.md`)
148
+ - **Test design** — whether the source's shape makes testing easy or hard (route to Ousterhout via `john-ousterhout.md`)
149
+ - **Property-based testing** — coverage of input space via QuickCheck-style generators (future addition if a stack adopts it; not in bassclef's bash substrate today)
150
+ - **Multi-fragment coordinator interactions** — how fragment N's side effects change fragment N+1's behavior (route to integration tests at the coordinator level, e.g., `session-reflection-coordinator.test.sh` per PR #992; criterion 12 covers per-fragment invocation-mode contract only)
151
+
152
+ INSTEAD of folding these into the rubric: each routes to a sister discipline. The rubric is sufficiency at the assertion-category layer; quality and rhythm and design are different surfaces.
153
+
154
+ ## Override
155
+
156
+ `SKIP_TEST_SUFFICIENCY=1 <command>` — logged via trace-helper. Use only for one-shot migration scripts, emergency rescue, or when adding the assertion would require infrastructure not yet in place (file follow-on ticket).
157
+
158
+ INSTEAD of overriding for routine work: write the assertion. Each criterion costs minutes at write-time; each missed assertion costs hours of debugging when it ships.
159
+
160
+ ## When this rule fires
161
+
162
+ Auto-loaded into every session via `additionalDirectories`. Agents self-check before committing any test file. The Tier 0 hook (`testing-tier-enforce.sh`) does not currently enforce the rubric mechanically.
163
+
164
+ INSTEAD: methodology layer with reviewer + agent self-check. V2 mechanical extension deferred to follow-on ticket. Count-based criteria (1, 2, 7) are auto-verifiable via the existing Tier 0 hook with a grep extension.
165
+
166
+ ## V2 — mechanical extension (deferred)
167
+
168
+ The operator files a follow-on ticket to extend the Tier 0 hook when recurring drift shows up in manual self-check. The follow-on covers:
169
+
170
+ - Branch count: grep source for decision points; count test `[x]` lines; BLOCK when test count < branch count
171
+ - Exit code matrix: grep source for `exit N`; verify test asserts each
172
+ - External binary handling: grep source for external calls; verify test mocks or graceful-missing path exists per call
173
+
174
+ Subjective criteria (3-6, 8-10) stay methodology-only. Beck + Myers + Hoare's disciplines pair best with reviewer judgment for those; mechanical enforcement at the subjective layer produces gaming, not quality.
175
+
176
+ ## Composes with
177
+
178
+ - `.claude/rules/test-list-discipline.md` (bassclef#1090) — Beck's planning artifact; the test-list IS the rubric tracking surface
179
+ - `.claude/rules/testing-tier-config.md` (bassclef#1037) — Tier 0 presence + recency; this rule layers sufficiency on top
180
+ - `.claude/rules/testing.md` — what makes a good test once you write it
181
+ - `.claude/rules/defensive-bash.md` — 7 bash disciplines; criteria 7-8 verify defensive-bash compliance in tests
182
+ - `.claude/rules/state-schema-validation.md` — state-spine accessor tests use criterion 10 (state-file interactions)
183
+ - `.claude/rules/blocked-items.md` — criterion 9 (stderr format) cites this rule's structured-field requirements
184
+ - `@luminary kent-beck` — TDD rhythm + test-list anchor
185
+ - `@luminary glenford-myers` — coverage taxonomy anchor (criteria 1-4)
186
+ - `@luminary tony-hoare` — contract testing anchor (criteria 5-10)
187
+
188
+ ## Distinct from
189
+
190
+ - `testing-tier-config.md` — WHICH paths require WHICH tier of enforcement; this is WHAT assertions a tier 0/1 test owes
191
+ - `testing.md` — WHAT makes a test well-written once you write it
192
+ - `test-list-discipline.md` — HOW to track which assertions are owed/done/deferred
193
+ - `iteration-bet-brief-completeness.md` — separate scope (delegation briefs, not test code)
194
+
195
+ ## Refs
196
+
197
+ - bassclef#1098 — this rule's filing ticket
198
+ - bassclef#1037 — Tier 0 strict-TDD discipline (Phase A); this extends with sufficiency layer
199
+ - bassclef#1090 — Beck test-list discipline (sister rule; planning artifact for this rubric)
200
+ - bassclef#1092 — Glenford Myers full luminary (anchor for criteria 1-4)
201
+ - bassclef#1094 — Tony Hoare full luminary (anchor for criteria 5-10)
202
+ - bassclef#1095 — full-from-filing policy (the luminaries cited here ship full)
203
+ - `docs/iteration-bets/2026-06-17b-sufficiency-rubric-plus-hardening-longrun.md` — goal authoring this rule (eat-own-dogfood: WU-2a/b/c hardening uses this rubric)
204
+ - Beck — *Test-Driven Development By Example* (Addison-Wesley, 2002)
205
+ - Myers — *The Art of Software Testing* (Wiley, 1979)
206
+ - Hoare — *Communications of the ACM* 12(10), 1969
207
+
208
+ ## Retirement condition
209
+
210
+ This rule retires only if bassclef migrates off systematic testing as a primary discipline. Specific criteria may evolve (V2 mechanical extension may absorb criteria 1, 2, 7; property-based testing may add criterion 11 if a stack adopts it) but the discipline of mechanical sufficiency-at-write-time survives.
@@ -0,0 +1,145 @@
1
+ ---
2
+ tier: lite
3
+ description: "Bassclef's testing discipline is configured per path-matcher in .claude/bassclef-configs.jsonc under the testing section."
4
+ ---
5
+
6
+ # Testing-Tier Config
7
+
8
+ Bassclef's testing discipline is configured per path-matcher in `.claude/bassclef-configs.jsonc` under the `testing` section. Four tiers — 0 strict TDD / 1 test-with / 2 smoke / 3 manual-verify — pick by blast radius. Most-specific path-matcher wins. The mtime-checking hook (`testing-tier-enforce.sh`) enforces Tier 0 mechanically; Tier 1 WARNs; Tier 2 + Tier 3 stay silent.
9
+
10
+ This rule supersedes the honor-system framing of `.claude/rules/testing.md`. That rule remains the test-content guidance (what makes a good test); this rule governs which paths require tests and what enforcement fires.
11
+
12
+ ## Why this rule exists
13
+
14
+ Bassclef shipped `.claude/rules/testing.md` with three layers — unit + integration + smoke — and a "skip glue code, trivial getters" exemption. That exemption ran on the honor system. No check fires when a new exported function ships without a test. No check fires when someone edits source without touching the test. No check fires when a hook update skips its test.
15
+
16
+ The 2026-06-15 substrate-archaeology session named the pattern. TDD-everywhere runs too verbose for adopter surfaces like chronicles and one-off scripts. TDD-nowhere runs too lax for bassclef's own hooks and schemas, where a silent bug hits every adopter. The tier framework lets impact pick the enforcement level.
17
+
18
+ The Tier 0 mtime hook does the actual work. INSTEAD of asking the agent to run tests from memory: the hook BLOCKs the commit when the test mtime did not advance with the source. A gate at the moment of failure beats a rule that fires after.
19
+
20
+ ## The four tiers
21
+
22
+ | Tier | Name | What it means | Enforcement |
23
+ |---|---|---|---|
24
+ | **0** | Strict TDD | Test mtime ≤ source mtime AND test file present | BLOCK at pre-commit |
25
+ | **1** | Test-with | New test expected when new source ships | WARN at pre-commit |
26
+ | **2** | Smoke | Smoke test exists somewhere for this surface | Silent (advisory only) |
27
+ | **3** | Manual verify | Operator eyeballs the change | Silent (no enforcement) |
28
+
29
+ **Tier 0** is for surfaces where silent bugs compound across every adopter. Bassclef hooks, state-spine schemas, lib/state.sh accessors, and the substrate's own pre-commit gates live here.
30
+
31
+ **Tier 1** is for bassclef's own glue scripts (`scripts/*.sh`) and adopter-app source files where TDD is expected but the WARN gives leeway for in-progress work.
32
+
33
+ **Tier 2** is for adopter-app routes and components where end-to-end smoke tests cover the surface without per-file unit tests. The path-matcher does not look for a sibling test file; the operator confirms a smoke test exists.
34
+
35
+ **Tier 3** is the silent default for narrative content. Chronicles, journal entries, iteration-goal docs, decompositions, and operator-private narrative all live here. INSTEAD of testing narrative: the operator reads it.
36
+
37
+ ## Path-matcher precedence
38
+
39
+ Two categories of path matchers, with different ownership:
40
+
41
+ 1. **Substrate-internal paths** — bassclef's own infrastructure. **Hard-coded in this rule body.** NOT adopter-configurable. Adopters read the discipline; they cannot edit it.
42
+ 2. **Adopter-app paths** — adopter's own code paths. Configured in adopter's `bassclef-configs.jsonc` `testing.path_matchers`. Per-adopter.
43
+
44
+ The hook walks both lists. Substrate paths take precedence; adopter overrides on substrate paths are silently ignored. When no entry matches, `testing.global_floor` applies. The default global floor is Tier 1 (WARN on missing test).
45
+
46
+ ### Substrate-internal path-matchers (HARD-CODED — not adopter-configurable)
47
+
48
+ | Path | Tier | Why this is Tier 0 / fixed |
49
+ |---|---|---|
50
+ | `.claude/hooks/*.sh` | 0 | Silent bugs compound across every adopter using bassclef |
51
+ | `scripts/migrate-adopter-references.sh` | 0 | Adopter-facing migration tool; Tier 0 hooks call it at session-start (per ADR-031); a defect hits every adopter machine. (Moved from `strategy/templates/` per bassclef-upstream#317 Phase 1b.) |
52
+ | `scripts/bassclef-orphan-parking.sh` | 0 | Adopter-facing migration tooling — same reason as above |
53
+ | `scripts/bassclef-migrate-summary.sh` | 0 | Adopter-facing migration tooling — same reason as above |
54
+ | `presence/install/*.sh` | 0 | Adopter-facing distribution dispatcher + sync + install bootstrap; a defect hits every adopter machine. (Moved from `strategy/templates/` per bassclef-upstream#317 Phase 1a.) |
55
+ | `standards/state-spine/schemas/*.json` | 0 | Schema defects corrupt the state spine universally |
56
+ | `lib/state.sh` | 0 | Accessor library central to all state reads; one defect breaks every consumer |
57
+ | `lib/intent-matcher.sh` | 0 | Intent dispatch — a defect breaks every chain |
58
+ | `scripts/*.sh` | 1 | Other operator-class scripts not listed above; WARN if missing test |
59
+ | `chronicle/*.md` | 3 | Narrative content; operator-read, no test |
60
+ | `docs/iteration-bets/*.md` | 3 | Narrative; operator-read |
61
+ | `docs/decompositions/*.md` | 3 | Narrative; operator-read |
62
+ | `docs/operator-private/journals/*.md` | 3 | Narrative; operator-read |
63
+
64
+ These are bassclef's substrate. Per ADR-031 and Linus's rule that bassclef keeps adopters working — discipline is a contract, not a preference. INSTEAD of letting adopters lower their tier: bassclef holds the discipline for its own substrate. Adopters read this rule; they cannot lower it themselves.
65
+
66
+ Why not adopter-configurable:
67
+ - **Substrate integrity is a contract.** Adopters rely on bassclef's reliability guarantee; lowering bassclef's own tier would undermine the contract while still depending on it.
68
+ - **Hyrum's Law applied to discipline.** Configurable tier means some adopter lowers it; their substrate degrades silently; bassclef's future fixes that assumed the discipline break their setup.
69
+ - **Linus's kernel/userspace boundary.** Linux exposes kernel state as READ surfaces, not WRITE permission over kernel correctness machinery. bassclef's tier discipline is the same shape.
70
+ - **Brooks's conceptual integrity.** Discipline is bassclef's coherent vision; configurability creates per-adopter drift.
71
+
72
+ ### Adopter-app path-matchers (configurable per adopter)
73
+
74
+ Adopters edit `testing.path_matchers` in their own `.claude/bassclef-configs.jsonc` for THEIR code:
75
+
76
+ ```jsonc
77
+ "testing": {
78
+ "global_floor": 1,
79
+ "path_matchers": [
80
+ { "match": "src/middleware.ts", "tier": 0 },
81
+ { "match": "src/lib/auth/**", "tier": 0 },
82
+ { "match": "src/app/**/route.ts", "tier": 2 },
83
+ { "match": "src/components/**/*.tsx", "tier": 2 }
84
+ ]
85
+ }
86
+ ```
87
+
88
+ The substrate entry wins when an adopter's `path_matchers` entry matches a substrate path (above). The hook ignores adopter attempts to lower substrate tiers — by design.
89
+
90
+ ## Test-file sibling mapping (Tier 0 only)
91
+
92
+ For Tier 0 paths, the hook expects a sibling test file:
93
+
94
+ | Source path | Expected test path |
95
+ |---|---|
96
+ | `.claude/hooks/<name>.sh` | `.claude/hooks/tests/<name>.test.sh` |
97
+ | `standards/state-spine/schemas/<name>.schema.json` | `standards/state-spine/schemas/tests/<name>.test.sh` |
98
+ | `lib/<name>.sh` | `lib/tests/<name>.test.sh` |
99
+
100
+ The hook BLOCKs when the source is edited and the test mtime did not advance. The BLOCK message names three things: the source path, the expected test path, and the mtime gap. INSTEAD of failing silently: the message tells the agent which test file to touch.
101
+
102
+ ## Override discipline
103
+
104
+ `SKIP_TESTING_TIER_ENFORCE=1 <command>` bypasses the hook for one command. The bypass is logged via `trace-helper.sh` per bassclef's existing override idiom.
105
+
106
+ Use it sparingly. Three cases warrant the override. One-shot migration scripts that bootstrap state. Emergency rescues when the hook itself is broken. Documentation changes where the agent has clear evidence the source change carries no behavior.
107
+
108
+ INSTEAD of overriding for routine work that fails the check: write the test. The hook fires at the cheapest moment to add one. Deferred test-writing almost never happens.
109
+
110
+ ## Allowlist baseline (grace window)
111
+
112
+ `.claude/hooks/testing-tier-enforce-allowlist.txt` lists existing Tier 0 sources that ship without tests as of the hook's land date. Each entry carries a dated grace window. The hook BLOCKs on listed paths in two cases. First — the grace window expires. Second — the source gets edited (new edits during grace still BLOCK). The allowlist covers pre-existing unedited state only.
113
+
114
+ The grace window is set per-adopter at `/onboard-repo` (default 60 days). Operator decides whether to backfill tests for grandfathered hooks or to remove them from the allowlist as they are next touched.
115
+
116
+ INSTEAD of forcing retroactive backfill at hook-land: the grace window covers existing untested bassclef hooks while new edits trigger enforcement. Per-hook backfill happens as those hooks are next touched.
117
+
118
+ ## When this rule fires
119
+
120
+ This rule is methodology — it loads in every session via `additionalDirectories`. Agents self-check before writing source files in Tier 0 paths. The `testing-tier-enforce.sh` hook is the mechanical layer (bassclef#1037 WU-3).
121
+
122
+ Together: rule supplies the why; hook fires at the moment of failure.
123
+
124
+ ## Composes with
125
+
126
+ - **@luminary kent-beck** — TDD anchor. Beck's red-green-refactor cycle is the discipline this rule does for Tier 0. A test written before source naturally satisfies "test mtime ≤ source mtime" — the test came first.
127
+ - **@luminary john-ousterhout** — Design-judgment anchor. The tier choice IS a design decision. Push every path to Tier 0 and you get shallow modules where tests just mirror source. Push every path to Tier 3 and you get silent regressions. The tier slider applies Ousterhout's "complexity is incremental" — pick the right tier per surface. Do not flatten the answer.
128
+ - `.claude/rules/testing.md` — the existing rule on test content (what makes a good test). This rule does not replace it; it adds the per-path tier decision that testing.md left implicit.
129
+ - `.claude/rules/state-schema-validation.md` — sister mechanical-enforcement rule at the state-spine write surface.
130
+ - `.claude/rules/blocked-items.md` — the BLOCK protocol the hook fires.
131
+ - `.claude/hooks/pre-commit-gate.sh` — the chain the new hook joins.
132
+ - `.claude/hooks/testing-tier-enforce.sh` — the mechanical implementation (bassclef#1037 WU-3).
133
+ - `.claude/bassclef-configs.jsonc` — the adopter-edit surface with inline comments.
134
+
135
+ ## Refs
136
+
137
+ - bassclef#1037 — this rule's filing ticket (testing-tier-config primitive)
138
+ - bassclef#1036 — sister ticket; Ousterhout luminary stub
139
+ - bassclef#867 — anchor-luminary verification pattern
140
+ - `docs/iteration-bets/2026-06-15a-discipline-and-versioning-foundations.md` — Phase A bet
141
+ - `docs/roadmaps/2026-06-15-engineering-discipline-and-versioning.md` — parent roadmap
142
+
143
+ ## Retirement condition
144
+
145
+ This rule retires only if the testing-tier framework itself is replaced by a different enforcement model (for example, vitest coverage gates per tier — Phase E). The discipline of per-path tier choice survives any future enforcement-mechanism swap.
@@ -0,0 +1,38 @@
1
+ ---
2
+ tier: lite
3
+ globs: ["**/*.ts", "**/*.tsx", "**/*.test.*", "**/*.spec.*"]
4
+ description: Three-layer test strategy — loads when touching source or test files
5
+ ---
6
+
7
+ > **Companion rule (bassclef#1037):** `.claude/rules/testing-tier-config.md` governs WHICH paths require which level of test enforcement (Tier 0 strict TDD / 1 test-with / 2 smoke / 3 manual verify). This rule (testing.md) governs WHAT makes a good test once a tier requires one. The tier-config rule supersedes the honor-system framing implicit in "skip glue code, trivial getters" below — tier choice is now configured per path-matcher in `.claude/bassclef-configs.jsonc`, and the `testing-tier-enforce.sh` hook fires mechanically.
8
+
9
+ Three layers, all required. Skipping shifts cost to production debugging.
10
+
11
+ Layer 1 — Unit (vitest):
12
+ Pure function logic. New pure function = new test, no exceptions.
13
+ Pre-commit WARNs if new source file has no matching test.
14
+ CI BLOCKs on failure.
15
+
16
+ Layer 2 — Integration:
17
+ Pipeline wiring. Real DB + real Redis, mock only external APIs at HTTP level.
18
+ CI BLOCKs on failure.
19
+
20
+ Layer 3 — E2E/Smoke:
21
+ Post-deploy. Curl-based minimum (health, API, auth redirect).
22
+ Playwright for critical user paths when needed.
23
+
24
+ Coverage policy:
25
+ 100% branch for safety rules. ≥80% line for business logic.
26
+ Skip glue code, trivial getters, third-party internals, LLM output content.
27
+
28
+ Naming: `test_{what}_{scenario}_{expected_outcome}`
29
+ Fixtures: factories not hardcoded data.
30
+
31
+ Change matrix:
32
+ Pure function → unit. Agent/worker → integration. API route → integration + smoke.
33
+ Schema migration → integration. Middleware/auth → E2E. UI component → smoke.
34
+
35
+ Order:
36
+ For pure functions and non-trivial logic, write the test before or
37
+ alongside the implementation — not after. If you wrote the test after,
38
+ state so in the verify summary so the reviewer can audit for tautology.