@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.
- package/dist/cli.cjs +23 -6
- package/dist/cli.js +23 -6
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lite/.claude/bassclef-configs.jsonc +56 -0
- package/dist/lite/.claude/bassclef-orientation.md +120 -0
- package/dist/lite/.claude/hooks/adr-discipline-check.sh +132 -0
- package/dist/lite/.claude/hooks/atomic-pr-check.sh +169 -0
- package/dist/lite/.claude/hooks/auto-save-idle.sh +51 -0
- package/dist/lite/.claude/hooks/bassclef-sync.sh +51 -13
- package/dist/lite/.claude/hooks/bet-doc-gate.sh +283 -0
- package/dist/lite/.claude/hooks/bug-diagnosis.sh +64 -0
- package/dist/lite/.claude/hooks/destructive-command-guard.sh +136 -0
- package/dist/lite/.claude/hooks/file-rename-discipline-check.sh +147 -0
- package/dist/lite/.claude/hooks/hook-classifier-canary.sh +158 -0
- package/dist/lite/.claude/hooks/kiss-default-check.sh +129 -0
- package/dist/lite/.claude/hooks/multi-tenancy-check.sh +83 -0
- package/dist/lite/.claude/hooks/oo-ad-entry-point-check.sh +216 -0
- package/dist/lite/.claude/hooks/operator-private-path-check.sh +203 -0
- package/dist/lite/.claude/hooks/pattern-annotation-validate.sh +142 -0
- package/dist/lite/.claude/hooks/plain-english-steering.sh +15 -1
- package/dist/lite/.claude/hooks/post-write-trace.sh +66 -0
- package/dist/lite/.claude/hooks/pr-body-scrub-check.sh +446 -0
- package/dist/lite/.claude/hooks/pre-commit-identifier-leak-scrub.sh +137 -0
- package/dist/lite/.claude/hooks/pre-gh-pr-body-scrub.sh +192 -0
- package/dist/lite/.claude/hooks/pre-gh-pr-title-scrub.sh +172 -0
- package/dist/lite/.claude/hooks/role-entry-gate.sh +115 -0
- package/dist/lite/.claude/hooks/save-state.sh +268 -0
- package/dist/lite/.claude/hooks/session-reflection.d/08-settings-drift.sh +8 -1
- package/dist/lite/.claude/hooks/session-reflection.d/10-abrupt-stop-recovery.sh +52 -0
- package/dist/lite/.claude/hooks/session-reflection.d/55-orientation-gate.sh +40 -4
- package/dist/lite/.claude/hooks/session-reflection.d/80-hook-heartbeat-check.sh +12 -5
- package/dist/lite/.claude/hooks/session-reflection.d/81-hook-manifest-staleness.sh +18 -2
- package/dist/lite/.claude/hooks/session-reflection.d/91-npm-version-drift.sh +54 -0
- package/dist/lite/.claude/hooks/session-reflection.d/tests/10-abrupt-stop-recovery.test.sh +95 -0
- package/dist/lite/.claude/hooks/session-reflection.sh +62 -15
- package/dist/lite/.claude/hooks/tool-failure-diagnosis.sh +50 -0
- package/dist/lite/.claude/luminaries/andrej-karpathy.md +165 -0
- package/dist/lite/.claude/luminaries/dave-gray.md +155 -0
- package/dist/lite/.claude/luminaries/gary-klein.md +166 -0
- package/dist/lite/.claude/luminaries/kaoru-ishikawa.md +162 -0
- package/dist/lite/.claude/luminaries/lean-product-canvas.md +33 -0
- package/dist/lite/.claude/luminaries/luke-wroblewski.md +224 -0
- package/dist/lite/.claude/luminaries/ryan-singer.md +113 -0
- package/dist/lite/.claude/luminaries/steve-krug.md +168 -0
- package/dist/lite/.claude/luminaries/tony-ulwick.md +134 -0
- package/dist/lite/.claude/rules/arc-level-orientation-before-leaf.md +60 -0
- package/dist/lite/.claude/rules/architect-review-discipline.md +71 -0
- package/dist/lite/.claude/rules/auto-fix-criteria.md +126 -0
- package/dist/lite/.claude/rules/bassclef-hook-drift-blocker.md +144 -0
- package/dist/lite/.claude/rules/bassclef-models-discovery.md +107 -0
- package/dist/lite/.claude/rules/bassclef-workflow-parity.md +104 -0
- package/dist/lite/.claude/rules/cost-governance.md +154 -0
- package/dist/lite/.claude/rules/file-rename-discipline.md +95 -0
- package/dist/lite/.claude/rules/mechanism-fidelity.md +88 -0
- package/dist/lite/.claude/rules/prototype-production-coupling.md +125 -0
- package/dist/lite/.claude/rules/quick-fix-criteria.md +90 -0
- package/dist/lite/.claude/rules/session-artifacts.md +30 -0
- package/dist/lite/.claude/rules/sibling-smoke-after-substrate-change.md +107 -0
- package/dist/lite/.claude/rules/substrate-as-system.md +67 -0
- package/dist/lite/.claude/rules/usability.md +22 -0
- package/dist/lite/.claude/settings.json +20 -0
- package/dist/lite/.claude/skills/agent-research-spawn/SKILL.md +100 -0
- package/dist/lite/.claude/skills/architect-review/SKILL.md +201 -0
- package/dist/lite/.claude/skills/auto-fix/SKILL.md +297 -0
- package/dist/lite/.claude/skills/autonomous/SKILL.md +300 -0
- package/dist/lite/.claude/skills/bind-subdomain/SKILL.md +232 -0
- package/dist/lite/.claude/skills/canvas/SKILL.md +41 -0
- package/dist/lite/.claude/skills/deploy-prod/SKILL.md +106 -0
- package/dist/lite/.claude/skills/docs-sync/SKILL.md +188 -0
- package/dist/lite/.claude/skills/eisenhower/SKILL.md +273 -0
- package/dist/lite/.claude/skills/empathy-map/SKILL.md +168 -0
- package/dist/lite/.claude/skills/frontend-design/SKILL.md +53 -0
- package/dist/lite/.claude/skills/interview-me/SKILL.md +171 -0
- package/dist/lite/.claude/skills/journal-export/SKILL.md +293 -0
- package/dist/lite/.claude/skills/jtbd-tasks/SKILL.md +203 -0
- package/dist/lite/.claude/skills/objectory-decompose/SKILL.md +256 -0
- package/dist/lite/.claude/skills/pick-luminaries/SKILL.md +100 -0
- package/dist/lite/.claude/skills/pre-mortem/SKILL.md +180 -0
- package/dist/lite/.claude/skills/prototype-gallery/SKILL.md +264 -0
- package/dist/lite/.claude/skills/provision-deploy-host/SKILL.md +257 -0
- package/dist/lite/.claude/skills/quick-fix/SKILL.md +273 -0
- package/dist/lite/.claude/skills/release/SKILL.md +311 -0
- package/dist/lite/.claude/skills/release-notes/SKILL.md +202 -0
- package/dist/lite/.claude/skills/rfc/SKILL.md +222 -0
- package/dist/lite/.claude/skills/riff-prototypes/SKILL.md +637 -0
- package/dist/lite/.claude/skills/secrets-bootstrap/SKILL.md +209 -0
- package/dist/lite/.claude/skills/skill/SKILL.md +304 -0
- package/dist/lite/.claude/skills/smoke-test/SKILL.md +100 -0
- package/dist/lite/.claude/skills/state/SKILL.md +204 -0
- package/dist/lite/.claude/skills/substrate-check/SKILL.md +218 -0
- package/dist/lite/.claude/skills/synthetic-user/SKILL.md +381 -0
- package/dist/lite/.claude/skills/task-scenarios/SKILL.md +105 -0
- package/dist/lite/.claude/skills/tier-dependency-audit/SKILL.md +139 -0
- package/dist/lite/.claude/skills/ux-migration/SKILL.md +424 -0
- package/dist/lite/.claude/skills/value-prop-canvas/SKILL.md +297 -0
- package/dist/lite/README.md +5 -1
- package/dist/lite/gitignore +6 -0
- package/dist/lite/lib/bassclef-dir-resolver.sh +85 -0
- package/dist/lite/lib/clone-failure-classifier.sh +59 -0
- package/dist/lite/lib/fixture-builder.sh +190 -0
- package/dist/lite/lib/fresh-install-check.sh +49 -0
- package/dist/lite/lib/hook-heartbeat.sh +0 -1
- package/dist/lite/lib/mechanism-fidelity.sh +569 -0
- package/dist/lite/lib/npm-version-check.sh +112 -0
- package/dist/lite/lib/preview-state.sh +229 -0
- package/dist/lite/lib/session-rescue.sh +0 -1
- package/dist/lite/scripts/generate-lite-manifest.sh +145 -4
- package/dist/lite/scripts/lite-manifest-drift-check.sh +5 -1
- package/dist/lite/standards/architect-review-discipline.md +222 -0
- package/dist/lite/standards/bassclef-platform.md +249 -0
- package/dist/lite/standards/bassclef-test-repos.md +97 -0
- package/dist/lite/standards/bassclef-wiring-manifest.json +25 -1
- package/dist/lite/standards/cold-adopter-harness.md +205 -0
- package/dist/lite/standards/data-management.md +135 -0
- package/dist/lite/standards/frontend-stack/nextjs-prisma.md +124 -0
- package/dist/lite/standards/lite-manifest-schema-changes.md +27 -0
- package/dist/lite/standards/lite-manifest.json +1456 -14
- package/dist/lite/standards/mechanism-fidelity.md +205 -0
- package/dist/lite/standards/operator-private-discipline.md +200 -0
- package/dist/lite/standards/operator-to-public-flow.md +195 -0
- package/dist/lite/standards/prototype-gallery.md +251 -0
- package/dist/lite/standards/pseudonym-key-schema.md +109 -0
- package/dist/lite/standards/sibling-validation-checklist.md +181 -0
- package/dist/lite/standards/slack-notifications.md +161 -0
- package/dist/lite/standards/synthetic-user-testing.md +173 -0
- package/dist/lite/standards/vendor-bindings.md +165 -0
- package/dist/lite/standards/vendor-plugins/frontend-design.md +66 -0
- package/dist/lite/standards/vpc-fit-validation.md +167 -0
- package/dist/lite/templates/gallery-yml.md +113 -0
- package/dist/lite/templates/product-canvas.md +169 -0
- package/dist/lite/templates/value-prop-canvas.md +180 -0
- package/package.json +3 -3
- /package/dist/lite/{whereami.md → docs/whereami.md} +0 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
---
|
|
2
|
+
tier: lite
|
|
3
|
+
name: value-prop-canvas
|
|
4
|
+
description: "Produce a Strategyzer Value Proposition Canvas per persona \u2014 Customer Profile (Jobs/Pains/Gains) plus Value Map (Products/Pain-relievers/Gain-creators) plus fit-validation. The diagnostic lens over /personas."
|
|
5
|
+
problem: "Value propositions get stated without persona anchoring. Jobs and pains and gains float free."
|
|
6
|
+
value: "Strategyzer Value Proposition Canvas per persona. Customer profile plus value map."
|
|
7
|
+
inputs: [Persona name from personas file]
|
|
8
|
+
outputs: [Customer profile — Jobs plus Pains plus Gains, Value map, Fit gap]
|
|
9
|
+
user_invocable: true
|
|
10
|
+
disable_model_invocation: false
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# /value-prop-canvas — Strategyzer VPC
|
|
14
|
+
|
|
15
|
+
Produce a Value Proposition Canvas (Strategyzer) per persona. Reads
|
|
16
|
+
`/personas` output; emits one canvas per persona; validates fit — every
|
|
17
|
+
Pain Reliever addresses a named Pain, every Gain Creator addresses a
|
|
18
|
+
named Gain. Orphan entries (relievers/creators without a target)
|
|
19
|
+
surface as validation failures.
|
|
20
|
+
|
|
21
|
+
## Distinction from `/value-prop`
|
|
22
|
+
|
|
23
|
+
Two adjacent but non-overlapping skills; both keep their slots.
|
|
24
|
+
|
|
25
|
+
| Skill | Output shape | Purpose | When to use |
|
|
26
|
+
|-------|-------------|---------|-------------|
|
|
27
|
+
| `/value-prop` | Positioning statement (Moore) + 3 RTBs (Dunford Proof) | Compressed sales-ready framing | Decks, landing pages, pitch prep |
|
|
28
|
+
| `/value-prop-canvas` | Two-sided canvas (Customer Profile + Value Map) per persona | Diagnostic mapping — do our relievers/creators actually address named pains/gains? | Before committing to positioning; during product shaping; validating fit |
|
|
29
|
+
|
|
30
|
+
If you're trying to *say* something, `/value-prop`. If you're trying to
|
|
31
|
+
*check* something, `/value-prop-canvas`. Most iterations benefit from
|
|
32
|
+
running VPC first (to find the fit) then `/value-prop` (to phrase it).
|
|
33
|
+
|
|
34
|
+
## Sources read
|
|
35
|
+
|
|
36
|
+
- Alexander Osterwalder et al. — Strategyzer *Value Proposition Design* (2014). Bassclef VPC source. https://www.strategyzer.com/library/the-value-proposition-canvas
|
|
37
|
+
- IxDF — *Value Proposition Canvas* overview. https://ixdf.org/literature/topics/value-proposition-canvas
|
|
38
|
+
- IxDF + Medium — *JTBD + VPC relationship* (Customer Jobs connection). https://medium.com/@pminanutshell/npd-phase-1-5-introducing-value-proposition-canvas-jobs-to-be-done-f5036ea6ff9
|
|
39
|
+
- `.claude/skills/personas/SKILL.md` — upstream read contract (file-system convention `docs/personas/*.md`)
|
|
40
|
+
- `standards/persona-schema.md` — persona shape this skill reads
|
|
41
|
+
- `standards/vpc-fit-validation.md` — companion standard (this skill's Stack-2 artifact) codifying the fit rules
|
|
42
|
+
- `templates/value-prop-canvas.md` — bassclef template (this skill's Stack-2 artifact)
|
|
43
|
+
- `.claude/skills/value-prop/SKILL.md` — adjacent skill; cross-linked in output but not reshaped
|
|
44
|
+
- `.claude/skills/jtbd-tasks/SKILL.md` — JTBD feeds Customer Jobs (functional jobs especially)
|
|
45
|
+
|
|
46
|
+
## What I'm NOT reading
|
|
47
|
+
|
|
48
|
+
- `/lean-canvas` or `/business-model-canvas` — those are broader strategic canvases at the organization level; VPC is product-market-fit specific. If a consumer wants lean canvas or BMC, those are separate skills for future promotion
|
|
49
|
+
- Cross-persona aggregation (one VPC for "all customers") — explicitly out of scope per ticket; VPC is 1:1 with persona
|
|
50
|
+
|
|
51
|
+
## When to invoke
|
|
52
|
+
|
|
53
|
+
- After `/personas` has produced at least one persona file (operator-default counts but produces a thin canvas — warn surfaced)
|
|
54
|
+
- During Elaboration or early Construction when fit between product decisions and customer needs is still being settled
|
|
55
|
+
- Before `/value-prop` if positioning feels ungrounded — VPC produces the evidence `/value-prop` needs
|
|
56
|
+
- When a `/risk-test-synthetic` matrix (future promotion) needs per-persona fit signal
|
|
57
|
+
|
|
58
|
+
## Usage
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
/value-prop-canvas → produce VPC for every persona in the repo
|
|
62
|
+
/value-prop-canvas [slug] → VPC for one persona
|
|
63
|
+
/value-prop-canvas validate → re-check fit on existing canvases at docs/value-prop-canvas/
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Procedure
|
|
67
|
+
|
|
68
|
+
### Step 1: Load personas
|
|
69
|
+
|
|
70
|
+
Read personas via the `/personas` contract:
|
|
71
|
+
|
|
72
|
+
1. Check `docs/personas/*.md` (preferred)
|
|
73
|
+
2. Check `docs/design/personas/*.md` (legacy)
|
|
74
|
+
3. If neither has files → emit operator-default warning and halt with:
|
|
75
|
+
"No personas found. Run `/personas default` to see the fallback, or
|
|
76
|
+
create real persona files in `docs/personas/` before producing VPC."
|
|
77
|
+
|
|
78
|
+
For a specific slug (`/value-prop-canvas couple-founders`), load only
|
|
79
|
+
that file. For the default invocation, enumerate all.
|
|
80
|
+
|
|
81
|
+
### Step 2: Extract Customer Profile signals
|
|
82
|
+
|
|
83
|
+
For each persona, derive the three Customer Profile quadrants from its
|
|
84
|
+
content:
|
|
85
|
+
|
|
86
|
+
| VPC field | Persona source |
|
|
87
|
+
|-----------|---------------|
|
|
88
|
+
| **Customer Jobs** | Persona's `goal`, `tasks`, JTBD statements (if present), workflow descriptions. Split into functional / social / emotional |
|
|
89
|
+
| **Pains** | Persona's "Pain points" section (with evidence traces). Each pain is a named Pain with severity signal if traceable |
|
|
90
|
+
| **Gains** | Persona's "Motivators" section + "What would make them switch" + implicit gains inferable from pain inversions |
|
|
91
|
+
|
|
92
|
+
**Do not invent.** If the persona doesn't have the data, the VPC
|
|
93
|
+
quadrant stays thin and surfaces as WARN in fit-validation. Inventing
|
|
94
|
+
entries produces false-positive fit signals downstream — the whole
|
|
95
|
+
point of VPC is that the fit must be defensible.
|
|
96
|
+
|
|
97
|
+
### Step 3: Build Value Map
|
|
98
|
+
|
|
99
|
+
For the product / iteration / feature being reasoned about:
|
|
100
|
+
|
|
101
|
+
| VPC field | Source |
|
|
102
|
+
|-----------|--------|
|
|
103
|
+
| **Products / Services** | What we ship — features, capabilities, affordances |
|
|
104
|
+
| **Pain Relievers** | Specific product behaviors that address specific Pains |
|
|
105
|
+
| **Gain Creators** | Specific product behaviors that produce specific Gains |
|
|
106
|
+
|
|
107
|
+
Each Pain Reliever is annotated with the Pain it addresses (by Pain ID
|
|
108
|
+
from Step 2). Each Gain Creator is annotated with the Gain.
|
|
109
|
+
|
|
110
|
+
Example format:
|
|
111
|
+
|
|
112
|
+
```markdown
|
|
113
|
+
## Pain Relievers
|
|
114
|
+
|
|
115
|
+
### PR1 — Shared context digest
|
|
116
|
+
**Addresses:** P1 (Duplicate context rebuilding), P3 ("What did AI say
|
|
117
|
+
about X?" side-channel)
|
|
118
|
+
Description: auto-digest of both partners' AI conversations into a
|
|
119
|
+
shared daily read.
|
|
120
|
+
|
|
121
|
+
### PR2 — Per-member session continuity
|
|
122
|
+
**Addresses:** P2 (Account-sharing workaround) — each member has their
|
|
123
|
+
own memory, shared context is opt-in not default.
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Step 4: Fit validation
|
|
127
|
+
|
|
128
|
+
Walk every Pain Reliever and Gain Creator. Check:
|
|
129
|
+
|
|
130
|
+
- [ ] Each Pain Reliever names ≥1 Pain it addresses (by ID or exact label)
|
|
131
|
+
- [ ] Each Gain Creator names ≥1 Gain it produces (by ID or exact label)
|
|
132
|
+
- [ ] No Pain has zero Relievers (WARN — unaddressed Pain is a scope signal, not a fit failure per se)
|
|
133
|
+
- [ ] No Gain has zero Creators (WARN — same)
|
|
134
|
+
|
|
135
|
+
**Orphan entries fail validation.** A Pain Reliever without a named
|
|
136
|
+
Pain is either (a) a feature looking for a problem, or (b) addressing
|
|
137
|
+
a pain that should be named but isn't yet. Both are signals — write
|
|
138
|
+
them down in the canvas's "Orphans surfaced" section with operator-
|
|
139
|
+
facing next steps ("Add P6: [inferred pain], or drop PR3 from the
|
|
140
|
+
value map").
|
|
141
|
+
|
|
142
|
+
Unaddressed Pains/Gains (coverage gaps) are WARN, not FAIL — they
|
|
143
|
+
might be deliberately out of scope, and the canvas surfaces them for
|
|
144
|
+
operator decision rather than blocking.
|
|
145
|
+
|
|
146
|
+
### Step 5: Emit canvas
|
|
147
|
+
|
|
148
|
+
Write to `docs/value-prop-canvas/{persona-slug}.md`. One file per
|
|
149
|
+
persona. Never merge multiple personas into one canvas.
|
|
150
|
+
|
|
151
|
+
Template: `templates/value-prop-canvas.md`. Structure:
|
|
152
|
+
|
|
153
|
+
```markdown
|
|
154
|
+
# Value Proposition Canvas — [Persona Name]
|
|
155
|
+
|
|
156
|
+
**Persona:** [slug] ([path to persona file])
|
|
157
|
+
**Iteration / feature:** [goal path or feature name]
|
|
158
|
+
**Generated:** [YYYY-MM-DD]
|
|
159
|
+
**Fit validation:** PASS | WARN (coverage gaps) | FAIL (orphans)
|
|
160
|
+
|
|
161
|
+
## Customer Profile
|
|
162
|
+
|
|
163
|
+
### Customer Jobs
|
|
164
|
+
- [J1] Functional: [label] — [one line]
|
|
165
|
+
- [J2] Social: [label] — [one line]
|
|
166
|
+
- [J3] Emotional: [label] — [one line]
|
|
167
|
+
|
|
168
|
+
### Pains
|
|
169
|
+
- [P1] [label] — [one line; source-trace if present in persona]
|
|
170
|
+
- [P2] ...
|
|
171
|
+
|
|
172
|
+
### Gains
|
|
173
|
+
- [G1] [label] — [one line]
|
|
174
|
+
- [G2] ...
|
|
175
|
+
|
|
176
|
+
## Value Map
|
|
177
|
+
|
|
178
|
+
### Products / Services
|
|
179
|
+
- [Feature A]
|
|
180
|
+
- [Feature B]
|
|
181
|
+
|
|
182
|
+
### Pain Relievers
|
|
183
|
+
- [PR1] [label] — addresses: [Pain IDs]. Description.
|
|
184
|
+
- [PR2] ...
|
|
185
|
+
|
|
186
|
+
### Gain Creators
|
|
187
|
+
- [GC1] [label] — produces: [Gain IDs]. Description.
|
|
188
|
+
- [GC2] ...
|
|
189
|
+
|
|
190
|
+
## Fit summary
|
|
191
|
+
|
|
192
|
+
| Element | Count | Addressed | Unaddressed |
|
|
193
|
+
|---------|-------|-----------|-------------|
|
|
194
|
+
| Pains | N | M | K |
|
|
195
|
+
| Gains | N | M | K |
|
|
196
|
+
|
|
197
|
+
## Orphans surfaced
|
|
198
|
+
|
|
199
|
+
[Each orphan: the entry, why it has no target, operator decision needed]
|
|
200
|
+
|
|
201
|
+
## Notes
|
|
202
|
+
[Optional: edge cases, deferred decisions, open questions]
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Step 6: Output summary
|
|
206
|
+
|
|
207
|
+
Print a compact summary to the conversation:
|
|
208
|
+
|
|
209
|
+
```markdown
|
|
210
|
+
## VPC produced: [N] canvases
|
|
211
|
+
|
|
212
|
+
| Persona | Status | Orphans | Coverage gaps |
|
|
213
|
+
|---------|--------|---------|---------------|
|
|
214
|
+
| couple-founders | PASS | 0 | 1 gain unaddressed |
|
|
215
|
+
| cofounder-pair | WARN | 0 | 2 pains unaddressed |
|
|
216
|
+
| operator-advisor | FAIL | 1 orphan PR | — |
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Worked example — inline fit-validation demo
|
|
220
|
+
|
|
221
|
+
To make the fit rule concrete, here's a deliberate orphan that
|
|
222
|
+
validation should catch:
|
|
223
|
+
|
|
224
|
+
**Persona P1 (hypothetical):** "cofounder-pair" — two technical
|
|
225
|
+
co-founders frustrated by ChatGPT Business gaps.
|
|
226
|
+
|
|
227
|
+
**Pains:**
|
|
228
|
+
- P1: Chat history siloed between accounts
|
|
229
|
+
- P2: Can't share project context with teammates
|
|
230
|
+
- P3: Monthly cost scales badly (2 seats = $50/mo)
|
|
231
|
+
|
|
232
|
+
**Pain Relievers drafted:**
|
|
233
|
+
- PR1: Shared workspace with cross-account memory → **addresses P1, P2**
|
|
234
|
+
- PR2: Per-project billing pool → **addresses P3**
|
|
235
|
+
- PR3: AI-powered meeting notes → **addresses — (no Pain named)**
|
|
236
|
+
|
|
237
|
+
Running fit-validation catches PR3: orphan. Either a P4 ("meeting
|
|
238
|
+
context lost between sessions") should be added, or PR3 should drop
|
|
239
|
+
from the Value Map. Operator decides. `/value-prop-canvas validate`
|
|
240
|
+
fails this canvas until the orphan resolves.
|
|
241
|
+
|
|
242
|
+
Without the explicit Pain-addressing rule, PR3 would sneak into the
|
|
243
|
+
value map as a feature-looking-for-a-problem. Classic failure mode.
|
|
244
|
+
|
|
245
|
+
## Common pitfalls
|
|
246
|
+
|
|
247
|
+
- **Inventing customer jobs** — VPC Customer Profile MUST derive from the persona. If the persona doesn't have the job, the job isn't there yet. Fix by enriching the persona (via research), not by padding the VPC.
|
|
248
|
+
- **"General" VPC** — one canvas that covers "all customers" destroys the fit signal. If you have three personas, you have three canvases. Compressing them hides which fit is strong and which is weak.
|
|
249
|
+
- **Aspirational Pain Relievers** — "We'll solve X better than anyone" is not a reliever. Relievers are specific product behaviors tied to specific pains.
|
|
250
|
+
- **Pattern-matching Gains from Pains (lazy inversion)** — "Pain: context lost" → "Gain: context preserved" is a low-value inversion. Real gains are desired outcomes the customer would articulate independently — often motivational rather than problem-solving.
|
|
251
|
+
- **Skipping fit validation** — emitting a canvas without running the fit walk defeats the entire point. `/value-prop-canvas validate` MUST run before any canvas is considered complete.
|
|
252
|
+
|
|
253
|
+
## Relationship to other skills
|
|
254
|
+
|
|
255
|
+
| Skill | Relationship |
|
|
256
|
+
|-------|-------------|
|
|
257
|
+
| `/personas` | Upstream read. This skill halts without personas. |
|
|
258
|
+
| `/value-prop` | Sibling. VPC is diagnostic (check fit); `/value-prop` is generative (compose positioning). Run VPC first, then `/value-prop`. |
|
|
259
|
+
| `/jtbd-tasks` | JTBD statements → Customer Jobs (especially functional jobs) |
|
|
260
|
+
| `/risk-test-synthetic` | Reads VPC per persona to build risk × persona × artifact matrix |
|
|
261
|
+
| `/user-stories` | Pain Relievers + Gain Creators inform the `<benefit>` clause in INVEST stories |
|
|
262
|
+
| `/spec` | VPC Value Map maps to spec "success criteria" |
|
|
263
|
+
| `/riff-prototypes` | VPC fit-validation per variant surfaces which variant addresses the most pains — selection input |
|
|
264
|
+
|
|
265
|
+
## Chain position
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
/personas (WHO)
|
|
269
|
+
↓
|
|
270
|
+
/jtbd-tasks (WHY — what they're hiring for)
|
|
271
|
+
↓
|
|
272
|
+
/value-prop-canvas (WHAT TO SAY / HOW TO HELP) ← this skill
|
|
273
|
+
↓
|
|
274
|
+
/value-prop (COMPRESSED POSITIONING — uses VPC as evidence)
|
|
275
|
+
↓
|
|
276
|
+
/user-stories (HOW WE BUILD IT — INVEST)
|
|
277
|
+
↓
|
|
278
|
+
/ia-model + /interaction-design (HOW IT RENDERS)
|
|
279
|
+
↓
|
|
280
|
+
/decompose + /spec (HOW IT IMPLEMENTS)
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Provenance & evolution
|
|
284
|
+
|
|
285
|
+
- Drafted in an operator project during user-centric chain shaping
|
|
286
|
+
- Promoted to bassclef via issue #201
|
|
287
|
+
- Stack-2 of iteration `docs/iteration-bets/2026-04-19e-persona-vpc-promotions.md` (Stack-1 shipped `/personas`)
|
|
288
|
+
|
|
289
|
+
Future promotions in the same family (separate goals):
|
|
290
|
+
- `/user-stories` — downstream consumer
|
|
291
|
+
- `/risk-test-synthetic` — downstream consumer
|
|
292
|
+
- `/psychographic-mine` — upstream persona generator
|
|
293
|
+
- `/auto-user-research` — persona generator pipeline
|
|
294
|
+
|
|
295
|
+
## Output discipline
|
|
296
|
+
|
|
297
|
+
Dispatch `/kiss words --rewrite` on your skill output before you return it. See `standards/skill-output-discipline.md` for the contract.
|
package/dist/lite/README.md
CHANGED
|
@@ -11,7 +11,11 @@ description: A shared substrate for working with AI coding agents.
|
|
|
11
11
|
>
|
|
12
12
|
> Then read the rest of this README as background. About 10 minutes total.
|
|
13
13
|
|
|
14
|
-
**A shared substrate for working with AI coding agents.** Install it once into your repo, and every Claude Code session starts loaded:
|
|
14
|
+
**A shared substrate for working with AI coding agents.** Install it once into your repo, and every Claude Code session starts loaded: skills you invoke, rules the agent reads, specialist agents you dispatch, practitioner profiles it designs against, hooks that catch mistakes, plus the standards and decision records that wire them together.
|
|
15
|
+
|
|
16
|
+
Run `/skills` or `/luminary` in any session to see exactly what your install carries. The counts differ by tier, so this README does not print them.
|
|
17
|
+
|
|
18
|
+
The agent reads your rules, knows the catalog, handles failure cleanly, and ships with discipline. You stop re-explaining your conventions every session — the difference between an assistant you brief and a band that already knows the set.
|
|
15
19
|
|
|
16
20
|
It is not a product. It is not a SaaS. It's a public framework, Apache 2.0: clone the repo, run the install script, own your stack.
|
|
17
21
|
|
package/dist/lite/gitignore
CHANGED
|
@@ -56,3 +56,9 @@ substrate.secrets.md
|
|
|
56
56
|
|
|
57
57
|
# Adopter-specific ignores below
|
|
58
58
|
# <adopter-content-below>
|
|
59
|
+
|
|
60
|
+
# Bassclef synced substrate — vendored via `bassclef init` + `bassclef sync`.
|
|
61
|
+
# Ignore by default so `git add -A` does not sweep hundreds of files.
|
|
62
|
+
# Adopters who want to pin substrate: remove these lines OR add `!path/to/track`.
|
|
63
|
+
.claude/
|
|
64
|
+
.bassclef/
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
#
|
|
4
|
+
# bassclef-dir-resolver.sh — resolve BASSCLEF_DIR across install layouts.
|
|
5
|
+
# Cures sunj-labs/bassclef-cli#105 root cause where session-reflection.sh:43
|
|
6
|
+
# derived BASSCLEF_DIR by walking up two levels from the dispatcher's own
|
|
7
|
+
# path, resolving to $HOME under operator install (dispatcher is a real
|
|
8
|
+
# file at $HOME/.claude/hooks/, not a symlink to a checkout).
|
|
9
|
+
#
|
|
10
|
+
# The resolver probes four paths in order per Chain-of-Responsibility:
|
|
11
|
+
# 1. Peer-checkout — $CWD/../bassclef with sentinel file present
|
|
12
|
+
# 2. $HOME/bassclef — tarball adopter path with sentinel file present
|
|
13
|
+
# 3. Bundled path — walk up from $HOOK_REAL_PATH with sentinel present
|
|
14
|
+
# 4. Fallback — $CWD (adopter repo); safer than $HOME because the files
|
|
15
|
+
# fragments want ship into the repo via bassclef init
|
|
16
|
+
#
|
|
17
|
+
# The sentinel file is presence/install/bassclef-hook-connect.sh. Every
|
|
18
|
+
# probe requires it — an empty peer dir or empty $HOME/bassclef does NOT win.
|
|
19
|
+
#
|
|
20
|
+
# Silent contract: never crashes, never writes to stderr on the resolve path.
|
|
21
|
+
# Consumers (13 fragments plus session-reflection.sh coordinator) treat the
|
|
22
|
+
# result as authoritative.
|
|
23
|
+
#
|
|
24
|
+
# Anchor luminaries:
|
|
25
|
+
# @luminary john-ousterhout — deep module (narrow interface hides 4-path probe)
|
|
26
|
+
# @luminary linus-torvalds — adopter contract (fallback preserves the case
|
|
27
|
+
# where an adopter's checkout is not in a
|
|
28
|
+
# well-known place)
|
|
29
|
+
# @luminary tony-hoare — postcondition (returns an existing path OR $CWD;
|
|
30
|
+
# never returns $HOME under operator install)
|
|
31
|
+
# @luminary michael-nygard — stability pattern (silent-return; no crash path)
|
|
32
|
+
#
|
|
33
|
+
# Companion audible-fail helper: emit_missing_dep_notice — used by fragments
|
|
34
|
+
# whose dependencies still cannot resolve after the coordinator sets
|
|
35
|
+
# $BASSCLEF_DIR. Cures the two silent-fail fragments named in cli#105 body.
|
|
36
|
+
|
|
37
|
+
# @pattern patterns/code/gof/chain-of-responsibility.md
|
|
38
|
+
# Four probe paths in order; first hit wins; fallback covers the null case.
|
|
39
|
+
resolve_bassclef_dir() {
|
|
40
|
+
local hook_path="${HOOK_REAL_PATH:-}"
|
|
41
|
+
local cwd="${CWD:-.}"
|
|
42
|
+
local home_dir="${HOME_OVERRIDE:-${HOME:-/nonexistent}}"
|
|
43
|
+
local sentinel="presence/install/bassclef-hook-connect.sh"
|
|
44
|
+
|
|
45
|
+
# 1. Peer-checkout — sibling bassclef dir next to $CWD
|
|
46
|
+
if [ -d "$cwd/../bassclef" ] && [ -f "$cwd/../bassclef/$sentinel" ]; then
|
|
47
|
+
( cd "$cwd/../bassclef" 2>/dev/null && pwd ) && return 0
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# 2. $HOME/bassclef — tarball adopter path
|
|
51
|
+
if [ -d "$home_dir/bassclef" ] && [ -f "$home_dir/bassclef/$sentinel" ]; then
|
|
52
|
+
printf '%s\n' "$home_dir/bassclef" && return 0
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# 3. Bundled path — walk up two levels from dispatcher
|
|
56
|
+
if [ -n "$hook_path" ]; then
|
|
57
|
+
local candidate
|
|
58
|
+
candidate="$( cd "$(dirname "$hook_path")/../.." 2>/dev/null && pwd )" || candidate=""
|
|
59
|
+
if [ -n "$candidate" ] && [ -f "$candidate/$sentinel" ]; then
|
|
60
|
+
printf '%s\n' "$candidate" && return 0
|
|
61
|
+
fi
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# 4. Fallback — $CWD (adopter repo). Safer than $HOME because the files
|
|
65
|
+
# fragments want (init manifest, presence/install/*) ship into the repo.
|
|
66
|
+
( cd "$cwd" 2>/dev/null && pwd ) || printf '%s\n' "$cwd"
|
|
67
|
+
return 0
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# emit_missing_dep_notice — companion audible-fail helper.
|
|
71
|
+
# Fragments call this when their dependency still cannot resolve after
|
|
72
|
+
# $BASSCLEF_DIR is set. Prints ONE plain line to stderr naming the
|
|
73
|
+
# fragment and the expected path. Silent-safe — never crashes the caller.
|
|
74
|
+
#
|
|
75
|
+
# Usage: emit_missing_dep_notice <fragment-name> <expected-path>
|
|
76
|
+
emit_missing_dep_notice() {
|
|
77
|
+
local fragment="${1:-unknown-fragment}"
|
|
78
|
+
local expected="${2:-<unknown-path>}"
|
|
79
|
+
printf '%s: skip — dependency not at %s. Sync bassclef or set BASSCLEF_DIR.\n' \
|
|
80
|
+
"$fragment" "$expected" >&2
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# Export for consumers that source this lib.
|
|
84
|
+
export -f resolve_bassclef_dir 2>/dev/null || true
|
|
85
|
+
export -f emit_missing_dep_notice 2>/dev/null || true
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
#
|
|
4
|
+
# clone-failure-classifier.sh — classify a git clone failure by its stderr.
|
|
5
|
+
# Cures sunj-labs/bassclef-cli#106 where bassclef-sync.sh printed
|
|
6
|
+
# "set up GitHub auth" even when authentication was fine and the cause
|
|
7
|
+
# was repo visibility (private repo, adopter cannot see it).
|
|
8
|
+
#
|
|
9
|
+
# Contract: takes git stderr as $1. Prints a multi-line message to stdout
|
|
10
|
+
# naming the cause (auth / visibility / network / unknown) and a hint the
|
|
11
|
+
# adopter can act on. Includes the raw stderr in the message. Exit 0.
|
|
12
|
+
#
|
|
13
|
+
# Anchor luminaries:
|
|
14
|
+
# @luminary donald-norman — visibility of system status (adopter hears
|
|
15
|
+
# the real cause, not a wrong guess)
|
|
16
|
+
# @luminary michael-nygard — the case statement is a strategy per cause
|
|
17
|
+
# class; each cause gets its own hint
|
|
18
|
+
# @luminary michael-feathers — 3 canonical error shapes fixture-tested
|
|
19
|
+
|
|
20
|
+
# @pattern patterns/code/gof/strategy.md
|
|
21
|
+
# One strategy per error shape; unknown falls through to verbatim passthrough.
|
|
22
|
+
classify_clone_failure() {
|
|
23
|
+
local git_stderr="${1:-}"
|
|
24
|
+
local cause hint
|
|
25
|
+
|
|
26
|
+
case "$git_stderr" in
|
|
27
|
+
*"Authentication failed"*|*"could not read Username"*|*"Permission denied (publickey)"*)
|
|
28
|
+
cause="auth"
|
|
29
|
+
hint="Optional: set up GitHub auth to unlock full-tier sync — run: gh auth login"
|
|
30
|
+
;;
|
|
31
|
+
*"Repository not found"*|*"repository "*"not found"*)
|
|
32
|
+
cause="visibility"
|
|
33
|
+
hint="Repository sunj-labs/bassclef may be private and your account cannot see it. Confirm access or continue on lite tier only."
|
|
34
|
+
;;
|
|
35
|
+
*"Could not resolve host"*|*"Connection timed out"*|*"Failed to connect"*)
|
|
36
|
+
cause="network"
|
|
37
|
+
hint="Network unavailable. Lite tier works without it."
|
|
38
|
+
;;
|
|
39
|
+
*)
|
|
40
|
+
cause="unknown"
|
|
41
|
+
hint="Unknown git failure. See the raw error above."
|
|
42
|
+
;;
|
|
43
|
+
esac
|
|
44
|
+
|
|
45
|
+
cat <<MSG
|
|
46
|
+
### Bassclef sync — full-tier not synced (${cause})
|
|
47
|
+
|
|
48
|
+
Git said:
|
|
49
|
+
${git_stderr}
|
|
50
|
+
|
|
51
|
+
${hint}
|
|
52
|
+
|
|
53
|
+
Lite tier works without it — your session is fine.
|
|
54
|
+
Skills / rules / agents beyond lite become available once sync succeeds.
|
|
55
|
+
MSG
|
|
56
|
+
return 0
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export -f classify_clone_failure 2>/dev/null || true
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# Deep module for adopter-environment fixtures used by bassclef Tier 0 tests.
|
|
4
|
+
# Per ADR-038 (adopter testing harness redesign) + R5 amendment to bet 30a
|
|
5
|
+
# (@luminary john-ousterhout deep modules — the 5 fixture builders are thin
|
|
6
|
+
# orchestrators on top of this library).
|
|
7
|
+
#
|
|
8
|
+
# Public API (functions tests source + call):
|
|
9
|
+
# fixture_init(name, tmproot) → echo path; returns 0
|
|
10
|
+
# fixture_cleanup(path) → safe rm; refuses paths without sentinel
|
|
11
|
+
# fixture_cleanup_all() → cleanup every fixture this process created
|
|
12
|
+
# fixture_setup_substrate_sibling(fixture_dir, substrate_name)
|
|
13
|
+
# fixture_inject_pre_rename_refs(adopter_dir, canonical_substrate_dir)
|
|
14
|
+
#
|
|
15
|
+
# Override: SKIP_FIXTURE_BUILDER=1 — every public function exits 0 silently
|
|
16
|
+
# (fixture_init echoes empty path).
|
|
17
|
+
# Logged via trace-helper per bassclef hook idiom.
|
|
18
|
+
#
|
|
19
|
+
# Per .claude/rules/defensive-bash.md — strict mode, quoted vars, local-safe,
|
|
20
|
+
# trap-safe (composes with caller's trap via fixture_cleanup_all).
|
|
21
|
+
|
|
22
|
+
set -u
|
|
23
|
+
|
|
24
|
+
# Sentinel filename written into every fixture root by fixture_init.
|
|
25
|
+
# fixture_cleanup refuses to remove a path that lacks this file — safe by default.
|
|
26
|
+
# Prefixed underscore is convention for "do not depend on this name from outside"
|
|
27
|
+
# but the symbol stays in scope so functions can reference it. The test for
|
|
28
|
+
# global-var pollution (T14) checks specifically for `FIXTURE_TMP_INTERNAL` which
|
|
29
|
+
# this lib never declares.
|
|
30
|
+
_FIXTURE_SENTINEL=".fixture-builder-marker"
|
|
31
|
+
|
|
32
|
+
# Track every fixture created in this shell so fixture_cleanup_all sweeps them.
|
|
33
|
+
# Bash 3.2 compatible — newline-separated list of paths in one string.
|
|
34
|
+
_FIXTURE_REGISTRY=""
|
|
35
|
+
|
|
36
|
+
# -----------------------------------------------------------------------------
|
|
37
|
+
# fixture_init <name> <tmproot>
|
|
38
|
+
# Create a fresh fixture dir under tmproot, write the sentinel, register the
|
|
39
|
+
# path, and echo it to stdout.
|
|
40
|
+
# Override SKIP_FIXTURE_BUILDER=1 — echo nothing, return 0.
|
|
41
|
+
# -----------------------------------------------------------------------------
|
|
42
|
+
fixture_init() {
|
|
43
|
+
if [ "${SKIP_FIXTURE_BUILDER:-0}" = "1" ]; then
|
|
44
|
+
return 0
|
|
45
|
+
fi
|
|
46
|
+
local name="${1:?fixture_init requires name}"
|
|
47
|
+
local tmproot="${2:?fixture_init requires tmproot}"
|
|
48
|
+
if [ ! -d "$tmproot" ]; then
|
|
49
|
+
echo "fixture_init: tmproot does not exist: $tmproot" >&2
|
|
50
|
+
return 1
|
|
51
|
+
fi
|
|
52
|
+
local d
|
|
53
|
+
d=$(mktemp -d "$tmproot/${name}.XXXXXX")
|
|
54
|
+
touch "$d/$_FIXTURE_SENTINEL"
|
|
55
|
+
_FIXTURE_REGISTRY="${_FIXTURE_REGISTRY}${d}
|
|
56
|
+
"
|
|
57
|
+
echo "$d"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# -----------------------------------------------------------------------------
|
|
61
|
+
# fixture_cleanup <path>
|
|
62
|
+
# Remove the fixture dir. Refuses unless the sentinel file is present at the
|
|
63
|
+
# path — prevents accidental rm of system dirs even when caller passes
|
|
64
|
+
# an unexpected argument.
|
|
65
|
+
# Idempotent — missing dirs return 0 silently.
|
|
66
|
+
# -----------------------------------------------------------------------------
|
|
67
|
+
fixture_cleanup() {
|
|
68
|
+
if [ "${SKIP_FIXTURE_BUILDER:-0}" = "1" ]; then
|
|
69
|
+
return 0
|
|
70
|
+
fi
|
|
71
|
+
local path="${1:?fixture_cleanup requires path}"
|
|
72
|
+
if [ ! -e "$path" ]; then
|
|
73
|
+
# Idempotent — nothing to do
|
|
74
|
+
return 0
|
|
75
|
+
fi
|
|
76
|
+
if [ ! -f "$path/$_FIXTURE_SENTINEL" ]; then
|
|
77
|
+
echo "fixture_cleanup: refusing to remove path without sentinel: $path" >&2
|
|
78
|
+
return 1
|
|
79
|
+
fi
|
|
80
|
+
rm -rf "$path"
|
|
81
|
+
return 0
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# -----------------------------------------------------------------------------
|
|
85
|
+
# fixture_cleanup_all
|
|
86
|
+
# Sweep every fixture this shell created. Caller wires this into their trap:
|
|
87
|
+
# trap fixture_cleanup_all EXIT
|
|
88
|
+
# -----------------------------------------------------------------------------
|
|
89
|
+
fixture_cleanup_all() {
|
|
90
|
+
if [ "${SKIP_FIXTURE_BUILDER:-0}" = "1" ]; then
|
|
91
|
+
return 0
|
|
92
|
+
fi
|
|
93
|
+
local IFS=$'\n'
|
|
94
|
+
local path
|
|
95
|
+
for path in $_FIXTURE_REGISTRY; do
|
|
96
|
+
[ -n "$path" ] || continue
|
|
97
|
+
fixture_cleanup "$path" 2>/dev/null || true
|
|
98
|
+
done
|
|
99
|
+
_FIXTURE_REGISTRY=""
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# -----------------------------------------------------------------------------
|
|
103
|
+
# fixture_setup_substrate_sibling <fixture_dir> <substrate_name>
|
|
104
|
+
# Create the .claude tree under <fixture_dir>/<substrate_name>/, stocked with
|
|
105
|
+
# stub hook files that adopter symlinks point into. Used by every fixture
|
|
106
|
+
# that needs a sibling-substrate layout.
|
|
107
|
+
# -----------------------------------------------------------------------------
|
|
108
|
+
fixture_setup_substrate_sibling() {
|
|
109
|
+
if [ "${SKIP_FIXTURE_BUILDER:-0}" = "1" ]; then
|
|
110
|
+
return 0
|
|
111
|
+
fi
|
|
112
|
+
local fixture_dir="${1:?fixture_setup_substrate_sibling requires fixture_dir}"
|
|
113
|
+
local substrate_name="${2:?fixture_setup_substrate_sibling requires substrate_name}"
|
|
114
|
+
local substrate="$fixture_dir/$substrate_name"
|
|
115
|
+
mkdir -p "$substrate/.claude/hooks"
|
|
116
|
+
mkdir -p "$substrate/.claude/skills"
|
|
117
|
+
mkdir -p "$substrate/.claude/rules"
|
|
118
|
+
mkdir -p "$substrate/.claude/agents"
|
|
119
|
+
mkdir -p "$substrate/scripts"
|
|
120
|
+
# Stock the substrate with the hook files the existing tests reference
|
|
121
|
+
local hook
|
|
122
|
+
for hook in pre-commit-gate.sh type-check.sh artifact-ingestion-gate.sh bassclef-sync.sh; do
|
|
123
|
+
cat > "$substrate/.claude/hooks/$hook" <<EOF
|
|
124
|
+
#!/bin/bash
|
|
125
|
+
# Stub hook for fixture testing — emits no-op exit 0
|
|
126
|
+
exit 0
|
|
127
|
+
EOF
|
|
128
|
+
chmod +x "$substrate/.claude/hooks/$hook"
|
|
129
|
+
done
|
|
130
|
+
# Stock a representative script the migrate workflow references
|
|
131
|
+
cat > "$substrate/scripts/migrate-adopter-references.sh" <<'EOF'
|
|
132
|
+
#!/bin/bash
|
|
133
|
+
# Stub for fixture testing — emits no-op exit 0
|
|
134
|
+
exit 0
|
|
135
|
+
EOF
|
|
136
|
+
chmod +x "$substrate/scripts/migrate-adopter-references.sh"
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
# -----------------------------------------------------------------------------
|
|
140
|
+
# fixture_inject_pre_rename_refs <adopter_dir> <canonical_substrate_dir>
|
|
141
|
+
# Drop canonical-* references into an adopter setup — symlinks under
|
|
142
|
+
# .claude/hooks/ pointing at the sibling canonical substrate, plus a
|
|
143
|
+
# settings.json that references canonical paths. Simulates an adopter who
|
|
144
|
+
# synced BEFORE the canonical → bassclef rename.
|
|
145
|
+
#
|
|
146
|
+
# No-op if adopter_dir does not exist (returns 0).
|
|
147
|
+
# -----------------------------------------------------------------------------
|
|
148
|
+
fixture_inject_pre_rename_refs() {
|
|
149
|
+
if [ "${SKIP_FIXTURE_BUILDER:-0}" = "1" ]; then
|
|
150
|
+
return 0
|
|
151
|
+
fi
|
|
152
|
+
local adopter_dir="${1:?fixture_inject_pre_rename_refs requires adopter_dir}"
|
|
153
|
+
local canonical_dir="${2:?fixture_inject_pre_rename_refs requires canonical_dir}"
|
|
154
|
+
if [ ! -d "$adopter_dir" ]; then
|
|
155
|
+
# Caller will see no side effects — adopter dir absent, nothing to do
|
|
156
|
+
return 0
|
|
157
|
+
fi
|
|
158
|
+
mkdir -p "$adopter_dir/.claude/hooks"
|
|
159
|
+
# Create canonical-targeted symlinks for each hook in the sibling
|
|
160
|
+
local hook
|
|
161
|
+
for hook in pre-commit-gate.sh type-check.sh artifact-ingestion-gate.sh bassclef-sync.sh; do
|
|
162
|
+
# Symlink relative path so the fixture is portable; target uses "canonical"
|
|
163
|
+
# literal name so the fixture exposes pre-rename state.
|
|
164
|
+
ln -sf "../../canonical/.claude/hooks/$hook" "$adopter_dir/.claude/hooks/$hook" 2>/dev/null || true
|
|
165
|
+
done
|
|
166
|
+
# Drop a settings.json that references canonical hook paths (matches how an
|
|
167
|
+
# adopter who synced pre-rename would have wired things).
|
|
168
|
+
cat > "$adopter_dir/.claude/settings.json" <<'EOF'
|
|
169
|
+
{
|
|
170
|
+
"hooks": {
|
|
171
|
+
"PreToolUse": [
|
|
172
|
+
{
|
|
173
|
+
"matcher": "Edit|Write|MultiEdit",
|
|
174
|
+
"hooks": [
|
|
175
|
+
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-commit-gate.sh" }
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
],
|
|
179
|
+
"SessionStart": [
|
|
180
|
+
{
|
|
181
|
+
"hooks": [
|
|
182
|
+
{ "type": "command", "command": "bash ../canonical/.claude/hooks/bassclef-sync.sh" }
|
|
183
|
+
]
|
|
184
|
+
}
|
|
185
|
+
]
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
EOF
|
|
189
|
+
return 0
|
|
190
|
+
}
|