@thebassclef/lite 0.0.1 → 0.1.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/LICENSE +202 -0
- package/README.md +85 -4
- package/dist/cli.cjs +1610 -0
- package/dist/cli.js +1587 -0
- package/dist/index.cjs +4 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/package.json +51 -3
- package/substrate/.bassclef/lite-manifest.json +1794 -0
- package/substrate/.claude/agents/architect.md +70 -0
- package/substrate/.claude/agents/builder.md +114 -0
- package/substrate/.claude/agents/designer.md +156 -0
- package/substrate/.claude/agents/reviewer.md +88 -0
- package/substrate/.claude/hooks/artifact-ingestion-gate.sh +357 -0
- package/substrate/.claude/hooks/assert-verify-steering.sh +77 -0
- package/substrate/.claude/hooks/bassclef-source-config-validate.sh +215 -0
- package/substrate/.claude/hooks/bassclef-sync.sh +634 -0
- package/substrate/.claude/hooks/compound-noun-scrub.sh +292 -0
- package/substrate/.claude/hooks/kiss-expansion-inject.sh +69 -0
- package/substrate/.claude/hooks/plain-english-steering.sh +156 -0
- package/substrate/.claude/hooks/post-skill-friction-check.sh +177 -0
- package/substrate/.claude/hooks/post-skill-telemetry.sh +62 -0
- package/substrate/.claude/hooks/pre-build-gate.sh +511 -0
- package/substrate/.claude/hooks/pre-commit-gate.sh +452 -0
- package/substrate/.claude/hooks/session-end.sh +433 -0
- package/substrate/.claude/hooks/session-reflection.sh +303 -0
- package/substrate/.claude/hooks/skill-body-grade-gate.sh +219 -0
- package/substrate/.claude/hooks/skill-body-intent-drift.sh +107 -0
- package/substrate/.claude/hooks/skill-step-list-check.sh +171 -0
- package/substrate/.claude/hooks/state-validate.sh +271 -0
- package/substrate/.claude/hooks/substrate-clarity-gate.sh +1110 -0
- package/substrate/.claude/hooks/temperance-gate.sh +147 -0
- package/substrate/.claude/hooks/testing-tier-enforce.sh +233 -0
- package/substrate/.claude/hooks/turn-prose-grade-measure.sh +219 -0
- package/substrate/.claude/hooks/turn-prose-kiss-check.sh +434 -0
- package/substrate/.claude/hooks/vocabulary-migration-check.sh +171 -0
- package/substrate/.claude/hooks/whereami-utc-gate.sh +142 -0
- package/substrate/.claude/luminaries/alan-cooper.md +170 -0
- package/substrate/.claude/luminaries/alistair-cockburn.md +140 -0
- package/substrate/.claude/luminaries/amazon-pr-faq.md +34 -0
- package/substrate/.claude/luminaries/ash-maurya.md +121 -0
- package/substrate/.claude/luminaries/bill-buxton.md +210 -0
- package/substrate/.claude/luminaries/charles-sanders-peirce.md +150 -0
- package/substrate/.claude/luminaries/david-ogilvy.md +192 -0
- package/substrate/.claude/luminaries/don-norman.md +173 -0
- package/substrate/.claude/luminaries/edward-tufte.md +179 -0
- package/substrate/.claude/luminaries/eric-evans.md +160 -0
- package/substrate/.claude/luminaries/frederick-brooks.md +50 -0
- package/substrate/.claude/luminaries/gang-of-four.md +157 -0
- package/substrate/.claude/luminaries/jason-fried-dhh.md +46 -0
- package/substrate/.claude/luminaries/jesse-james-garrett.md +154 -0
- package/substrate/.claude/luminaries/john-ousterhout.md +94 -0
- package/substrate/.claude/luminaries/karl-popper.md +132 -0
- package/substrate/.claude/luminaries/kent-beck.md +168 -0
- package/substrate/.claude/luminaries/linus-torvalds.md +218 -0
- package/substrate/.claude/luminaries/martin-fowler.md +164 -0
- package/substrate/.claude/luminaries/michael-feathers.md +125 -0
- package/substrate/.claude/luminaries/michael-nygard.md +118 -0
- package/substrate/.claude/luminaries/robert-c-martin.md +164 -0
- package/substrate/.claude/luminaries/saltzer-schroeder.md +194 -0
- package/substrate/.claude/luminaries/sophia-prater.md +193 -0
- package/substrate/.claude/luminaries/stephen-toulmin.md +131 -0
- package/substrate/.claude/rules/artifact-ingestion.md +179 -0
- package/substrate/.claude/rules/assert-only-after-verify.md +137 -0
- package/substrate/.claude/rules/blocked-items.md +146 -0
- package/substrate/.claude/rules/branching.md +28 -0
- package/substrate/.claude/rules/commit-conventions.md +22 -0
- package/substrate/.claude/rules/context-engineering.md +202 -0
- package/substrate/.claude/rules/context-management.md +85 -0
- package/substrate/.claude/rules/destructive-operations.md +69 -0
- package/substrate/.claude/rules/diagnosis.md +38 -0
- package/substrate/.claude/rules/guardrails.md +73 -0
- package/substrate/.claude/rules/identifier-leak-prevention.md +104 -0
- package/substrate/.claude/rules/lite-manifest-schema-change-discipline.md +98 -0
- package/substrate/.claude/rules/oo-ad-entry-point.md +117 -0
- package/substrate/.claude/rules/operator-facing-prose.md +196 -0
- package/substrate/.claude/rules/plain-english-discipline.md +154 -0
- package/substrate/.claude/rules/pr-body-shape.md +317 -0
- package/substrate/.claude/rules/pr-strategy.md +167 -0
- package/substrate/.claude/rules/sdlc-gates.md +149 -0
- package/substrate/.claude/rules/session-artifacts.md +236 -0
- package/substrate/.claude/rules/state-schema-validation.md +162 -0
- package/substrate/.claude/rules/whereami-load-bearing.md +202 -0
- package/substrate/.claude/rules/writing-craft-discipline.md +92 -0
- package/substrate/.claude/skills/build/SKILL.md +640 -0
- package/substrate/.claude/skills/decompose/SKILL.md +280 -0
- package/substrate/.claude/skills/diagnose/SKILL.md +297 -0
- package/substrate/.claude/skills/howdoi/SKILL.md +105 -0
- package/substrate/.claude/skills/ia-model/SKILL.md +108 -0
- package/substrate/.claude/skills/interaction-design/SKILL.md +112 -0
- package/substrate/.claude/skills/interpret-input/SKILL.md +180 -0
- package/substrate/.claude/skills/kiss/SKILL.md +449 -0
- package/substrate/.claude/skills/launch/SKILL.md +924 -0
- package/substrate/.claude/skills/lean-canvas/SKILL.md +332 -0
- package/substrate/.claude/skills/longrun/SKILL.md +426 -0
- package/substrate/.claude/skills/luminary/SKILL.md +481 -0
- package/substrate/.claude/skills/ogilvy-writing-audit/SKILL.md +177 -0
- package/substrate/.claude/skills/onboard-repo/SKILL.md +2190 -0
- package/substrate/.claude/skills/pattern-review/SKILL.md +99 -0
- package/substrate/.claude/skills/personas/SKILL.md +207 -0
- package/substrate/.claude/skills/promote/SKILL.md +283 -0
- package/substrate/.claude/skills/requirement/SKILL.md +98 -0
- package/substrate/.claude/skills/retro/SKILL.md +117 -0
- package/substrate/.claude/skills/riff/SKILL.md +114 -0
- package/substrate/.claude/skills/roadmap-reconcile/SKILL.md +163 -0
- package/substrate/.claude/skills/session-end/SKILL.md +309 -0
- package/substrate/.claude/skills/session-log/SKILL.md +299 -0
- package/substrate/.claude/skills/skills/SKILL.md +228 -0
- package/substrate/.claude/skills/spec/SKILL.md +105 -0
- package/substrate/.claude/skills/sprint/SKILL.md +392 -0
- package/substrate/.claude/skills/stage/SKILL.md +384 -0
- package/substrate/.claude/skills/state-a-problem/SKILL.md +185 -0
- package/substrate/.claude/skills/temperance/SKILL.md +108 -0
- package/substrate/.claude/skills/use-case/SKILL.md +417 -0
- package/substrate/.claude/skills/user-stories/SKILL.md +268 -0
- package/substrate/.claude/skills/value-prop/SKILL.md +251 -0
- package/substrate/.claude/skills/verify/SKILL.md +160 -0
- package/substrate/.claude/skills/whereami/SKILL.md +307 -0
- package/substrate/AGENTS.md +79 -0
- package/substrate/CLAUDE-lite.md +85 -0
- package/substrate/CODE_OF_CONDUCT.md +28 -0
- package/substrate/CONTRIBUTING.md +177 -0
- package/substrate/README.md +173 -0
- package/substrate/SECURITY.md +19 -0
- package/substrate/architecture/decisions/ADR-029-release-pipeline.md +79 -0
- package/substrate/architecture/decisions/ADR-031-non-breaking-changes-adopter-discipline.md +139 -0
- package/substrate/architecture/decisions/ADR-032-adopter-sync-dispatcher-architecture.md +192 -0
- package/substrate/architecture/decisions/ADR-039-release-tagging-scheme.md +145 -0
- package/substrate/architecture/decisions/ADR-040-planning-skill-vocabulary-and-lite-profile.md +155 -0
- package/substrate/architecture/decisions/ADR-044-unified-skill-body-template.md +162 -0
- package/substrate/lib/clean-artifacts-sweep.sh +112 -0
- package/substrate/lib/code-comment-discipline.sh +144 -0
- package/substrate/lib/composer-preflight.sh +459 -0
- package/substrate/lib/hook-inject.sh +255 -0
- package/substrate/lib/luminary-pick.sh +96 -0
- package/substrate/lib/output-discipline.sh +143 -0
- package/substrate/lib/rewrite-check.sh +214 -0
- package/substrate/lib/state.sh +1372 -0
- package/substrate/lib/telemetry.sh +205 -0
- package/substrate/lib/tier-check.sh +138 -0
- package/substrate/lib/tier-dependency-audit.sh +933 -0
- package/substrate/presence/install/bassclef-hook-connect.sh +178 -0
- package/substrate/presence/install/bassclef-sync.dispatcher.template.sh +841 -0
- package/substrate/presence/install/bassclef-sync.template.sh +2076 -0
- package/substrate/presence/install/schedule-auto-save.cron.sh +88 -0
- package/substrate/presence/install/schedule-auto-save.taskscheduler.md +122 -0
- package/substrate/scripts/aggregate-telemetry.sh +217 -0
- package/substrate/scripts/analyze-tier-dependencies.sh +239 -0
- package/substrate/scripts/generate-lite-manifest.sh +489 -0
- package/substrate/scripts/generate-tier-manifest.sh +28 -0
- package/substrate/scripts/intent-drift-check.sh +456 -0
- package/substrate/scripts/lite-manifest-drift-check.sh +146 -0
- package/substrate/scripts/render-lite-manifest-doc.sh +150 -0
- package/substrate/standards/bassclef-configs-schema.md +232 -0
- package/substrate/standards/bassclef-evolution.md +143 -0
- package/substrate/standards/bassclef-internal-jargon.md +244 -0
- package/substrate/standards/bassclef-managed-sentinel.md +96 -0
- package/substrate/standards/bassclef-source-config.md +228 -0
- package/substrate/standards/deferred-actions-schema.md +204 -0
- package/substrate/standards/docs-sync-allowlist.md +76 -0
- package/substrate/standards/graceful-exit.md +227 -0
- package/substrate/standards/hook-idempotency.md +102 -0
- package/substrate/standards/lite-manifest-schema-changes.md +75 -0
- package/substrate/standards/luminary-problem-patterns.md +481 -0
- package/substrate/standards/ogilvy-writing-rules.md +225 -0
- package/substrate/standards/opener-discipline.md +96 -0
- package/substrate/standards/operator-facing-prose-discipline.md +201 -0
- package/substrate/standards/project-directory-layout.md +276 -0
- package/substrate/standards/release-tagging.md +137 -0
- package/substrate/standards/sdlc-compliance.md +286 -0
- package/substrate/standards/secrets-lifecycle.md +210 -0
- package/substrate/standards/security-scanner-adapter.md +145 -0
- package/substrate/standards/skill-output-discipline.md +90 -0
- package/substrate/standards/state-spine.md +511 -0
- package/substrate/standards/tech-stack-config.md +109 -0
- package/substrate/standards/tier-dependency-analysis.md +167 -0
- package/substrate/standards/tier-runtime-deps/lite.md +57 -0
- package/substrate/standards/tier-tag-schema.md +155 -0
- package/substrate/standards/whereami-schema.md +301 -0
- package/substrate/standards/writing-guide.md +213 -0
- package/substrate/templates/deferred-action-template.md +45 -0
- package/substrate/templates/whereami-template.md +101 -0
|
@@ -0,0 +1,2190 @@
|
|
|
1
|
+
---
|
|
2
|
+
tier: lite
|
|
3
|
+
name: onboard-repo
|
|
4
|
+
description: "Bootstrap bassclef framework in a new repo. Installs sync hook + scaffolds config + whereami + CLAUDE.md. Modes: default / --with-deploy-host / --with-secrets / --full / --greenfield-from-intent (cold-adopter magic demo from a paragraph — uses /launch --local)."
|
|
5
|
+
problem: "Setting up bassclef in a new repo takes an hour of trial. Missing directories and wrong hook wiring."
|
|
6
|
+
value: "One command bootstraps everything \u2014 sync hook and config file and whereami scaffold."
|
|
7
|
+
inputs: [Optional flags — --with-deploy-host or --with-secrets or --full]
|
|
8
|
+
outputs: [Installed bassclef-sync hook, Config scaffold, Whereami stub]
|
|
9
|
+
user_invocable: true
|
|
10
|
+
disable_model_invocation: false
|
|
11
|
+
requires:
|
|
12
|
+
cli: [git, gh]
|
|
13
|
+
network: true
|
|
14
|
+
modes: [default, --with-deploy-host, --with-secrets, --full, --greenfield-from-intent]
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# /onboard-repo — bassclef substrate adoption for a new repo
|
|
18
|
+
|
|
19
|
+
Brings a repo from "zero bassclef wiring" to "inherits full substrate
|
|
20
|
+
on desktop + mobile" in a single operator-gated session. Distinct from:
|
|
21
|
+
|
|
22
|
+
- `scripts/boot-bassclef.sh` — **machine-level** one-time setup (gh
|
|
23
|
+
auth, service account, launchd). Run once per machine.
|
|
24
|
+
- `/autonomous start` — **session-level** scaffolding (iteration goal,
|
|
25
|
+
phase state, risk register) for a repo that's ALREADY wired into
|
|
26
|
+
bassclef.
|
|
27
|
+
|
|
28
|
+
This skill sits between them: it's **repo-level** substrate adoption.
|
|
29
|
+
Run once per new consumer repo.
|
|
30
|
+
|
|
31
|
+
## When to invoke
|
|
32
|
+
|
|
33
|
+
- Adding a new repo that will use bassclef
|
|
34
|
+
- Re-onboarding a repo that pre-dates the bassclef substrate
|
|
35
|
+
- Refreshing substrate wiring in an existing consumer (idempotent)
|
|
36
|
+
|
|
37
|
+
## Prerequisites (fail fast if missing)
|
|
38
|
+
|
|
39
|
+
Different prereqs apply to Path A (standard onboarding) vs Path B
|
|
40
|
+
(`--greenfield-from-intent` cold-adopter magic demo). Check the path
|
|
41
|
+
the operator picked before validating.
|
|
42
|
+
|
|
43
|
+
### Path A — standard onboarding
|
|
44
|
+
|
|
45
|
+
- `gh` authenticated (`gh auth status`)
|
|
46
|
+
- `claude` resolvable in PATH (`command -v claude`) — required for the
|
|
47
|
+
acceptance test at the end. If missing from a Zellij/tmux subpane,
|
|
48
|
+
move `eval "$(/opt/homebrew/bin/brew shellenv)"` from `~/.zprofile`
|
|
49
|
+
to `~/.zshenv` so non-login shells pick up homebrew's PATH.
|
|
50
|
+
- Target repo exists on GitHub under your org
|
|
51
|
+
- Target repo has a default branch (`main` preferred)
|
|
52
|
+
- Bassclef is cloned as a peer at `$BASSCLEF_DIR` (default `~/src/<your-org>/bassclef`, for
|
|
53
|
+
desktop `additionalDirectories` resolution)
|
|
54
|
+
- You are currently `cd`'d into the target repo's working directory
|
|
55
|
+
- Claude Code session was started with `--add-dir ~/src/sunj-labs/bassclef`
|
|
56
|
+
so this skill file is accessible
|
|
57
|
+
|
|
58
|
+
### Path B — `--greenfield-from-intent` (cold-adopter magic demo)
|
|
59
|
+
|
|
60
|
+
- `claude` resolvable in PATH
|
|
61
|
+
- `git` available
|
|
62
|
+
- `python3` available (for the localhost gallery server)
|
|
63
|
+
- `jq` available (JSON parsing for bassclef-sync + LLM-pick adapter)
|
|
64
|
+
- `curl` available (bassclef-sync fallback + LLM-pick API calls)
|
|
65
|
+
- No separate API key needed. The skill uses whichever LLM your Claude Code session provides. Per the tier reframe (bassclef-web#190 + #193), tier is about catalog size, not credentials.
|
|
66
|
+
- You are currently `cd`'d into an **empty target directory** (or one
|
|
67
|
+
with only files Path B is allowed to overwrite — see "Working
|
|
68
|
+
directory convention" below)
|
|
69
|
+
- `gh` is NOT required (no GitHub repo gets created)
|
|
70
|
+
- A peer bassclef checkout at `~/src/sunj-labs/bassclef` is preferred
|
|
71
|
+
but not required (the thin-pointer dispatcher falls back to the
|
|
72
|
+
GitHub tarball when the sibling is absent — Path B's `Phase B.3`
|
|
73
|
+
installs it)
|
|
74
|
+
|
|
75
|
+
### When prereqs are missing — operator menu
|
|
76
|
+
|
|
77
|
+
If a prereq fails, surface it to the operator and present this menu
|
|
78
|
+
verbatim before stopping:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
Prerequisite missing for standard onboarding: <name of failed check>.
|
|
82
|
+
|
|
83
|
+
Pick one:
|
|
84
|
+
|
|
85
|
+
1. Cold-adopter magic demo — runs /launch --local, no GitHub repo
|
|
86
|
+
created, no vendor accounts needed. Switches this dispatch to
|
|
87
|
+
--greenfield-from-intent (Path B below). No separate API key
|
|
88
|
+
needed — uses the LLM your Claude Code session provides.
|
|
89
|
+
2. Fix the prereq and re-run standard onboarding (Path A).
|
|
90
|
+
3. Cancel.
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Operator picks `1` → continue under Path B. Picks `2` → stop and wait
|
|
94
|
+
for the operator to fix + re-dispatch. Picks `3` → stop.
|
|
95
|
+
|
|
96
|
+
Silence is not deferral. Wait for an explicit choice.
|
|
97
|
+
|
|
98
|
+
## Working directory convention
|
|
99
|
+
|
|
100
|
+
| Path | Working directory |
|
|
101
|
+
|---|---|
|
|
102
|
+
| Path A — Phases 1–3 | **target repo** (existing GitHub repo) |
|
|
103
|
+
| Path A — Phase 4 | retired (see Phase 4 note below) |
|
|
104
|
+
| Path B — all phases | **target directory** (typically empty; never leaves it) |
|
|
105
|
+
|
|
106
|
+
For Path A the `pwd` check at the start of each phase exists for a
|
|
107
|
+
reason — don't lose track. Path B runs entirely inside one directory
|
|
108
|
+
and never reaches out to GitHub.
|
|
109
|
+
|
|
110
|
+
## Procedure
|
|
111
|
+
|
|
112
|
+
The Procedure branches at the top into **Path A** (standard
|
|
113
|
+
onboarding) or **Path B** (`--greenfield-from-intent` cold-adopter
|
|
114
|
+
magic demo). Decide first, then walk only the phases under the chosen
|
|
115
|
+
path. Do not mix.
|
|
116
|
+
|
|
117
|
+
### Choosing the path
|
|
118
|
+
|
|
119
|
+
| Signal | Path |
|
|
120
|
+
|---|---|
|
|
121
|
+
| Operator dispatched `/onboard-repo --greenfield-from-intent ...` | B |
|
|
122
|
+
| Operator picked option `1` from the prereqs-missing menu | B |
|
|
123
|
+
| All Path A prereqs pass + no `--greenfield-from-intent` flag | A |
|
|
124
|
+
| Operator dispatched `/onboard-repo --with-deploy-host` / `--with-secrets` / `--full` | A (with Phase 3.5 chained) |
|
|
125
|
+
|
|
126
|
+
When in doubt, ask the operator which path; don't infer.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
### Path B — `--greenfield-from-intent` (cold-adopter magic demo)
|
|
131
|
+
|
|
132
|
+
**Working directory: empty.** Never leaves it. No GitHub repo. No vendor accounts touched. The path runs in ≤5 steps using the LLM your Claude Code session provides.
|
|
133
|
+
|
|
134
|
+
The task list the agent generates for Path B MUST be exactly these
|
|
135
|
+
five tasks, in this order. Do not add Phase 1 GitHub work, Phase 1.3
|
|
136
|
+
prod environment, Phase 3.5 follow-on prompts, or Phase 4 matrix PRs.
|
|
137
|
+
Those belong to Path A.
|
|
138
|
+
|
|
139
|
+
#### Phase B.1 — Confirm intent
|
|
140
|
+
|
|
141
|
+
Operator pastes a paragraph or voice transcript describing what they
|
|
142
|
+
want to build (the recipe-test example: "a wiki app for sharing
|
|
143
|
+
recipes with family"). Agent echoes the intent back and asks for
|
|
144
|
+
operator confirmation before continuing. No edits hit disk yet.
|
|
145
|
+
|
|
146
|
+
#### Phase B.2 — Init local git
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
git init
|
|
150
|
+
git commit --allow-empty -m "chore: init repo"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
First empty commit so subsequent file writes have a baseline.
|
|
154
|
+
|
|
155
|
+
#### Phase B.3 — Scaffold the bassclef wiring
|
|
156
|
+
|
|
157
|
+
Subset of Path A Phase 2 — only the files `/launch --local` actually
|
|
158
|
+
reads. Skip everything that needs a remote.
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
mkdir -p .claude/hooks docs
|
|
162
|
+
|
|
163
|
+
# Install the thin-pointer dispatcher (same source as Path A Phase 2.1).
|
|
164
|
+
# Sibling-fast-path preferred; the curl fallback works for cold adopters.
|
|
165
|
+
cp ~/src/sunj-labs/bassclef/presence/install/bassclef-sync.dispatcher.template.sh \
|
|
166
|
+
.claude/hooks/bassclef-sync.sh 2>/dev/null \
|
|
167
|
+
|| curl -sL https://raw.githubusercontent.com/sunj-labs/bassclef/main/presence/install/bassclef-sync.dispatcher.template.sh \
|
|
168
|
+
-o .claude/hooks/bassclef-sync.sh
|
|
169
|
+
chmod +x .claude/hooks/bassclef-sync.sh
|
|
170
|
+
|
|
171
|
+
# Minimal settings.json — only the SessionStart hook
|
|
172
|
+
cat > .claude/settings.json <<'JSON'
|
|
173
|
+
{
|
|
174
|
+
"permissions": {
|
|
175
|
+
"additionalDirectories": ["../bassclef"]
|
|
176
|
+
},
|
|
177
|
+
"hooks": {
|
|
178
|
+
"SessionStart": [
|
|
179
|
+
{
|
|
180
|
+
"matcher": "",
|
|
181
|
+
"hooks": [
|
|
182
|
+
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/bassclef-sync.sh", "timeout": 30 }
|
|
183
|
+
]
|
|
184
|
+
}
|
|
185
|
+
]
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
JSON
|
|
189
|
+
|
|
190
|
+
# Minimal substrate.config.md
|
|
191
|
+
cat > substrate.config.md <<'MD'
|
|
192
|
+
# substrate.config.md
|
|
193
|
+
|
|
194
|
+
hosting_platform: none
|
|
195
|
+
deploy_targets:
|
|
196
|
+
prod: false
|
|
197
|
+
budget:
|
|
198
|
+
session_ceiling: $0
|
|
199
|
+
iteration_ceiling: $0
|
|
200
|
+
MD
|
|
201
|
+
|
|
202
|
+
# Minimal .claude/bassclef-configs.jsonc — adopter-default seed (bassclef#1135 WU-4)
|
|
203
|
+
cat > .claude/bassclef-configs.jsonc <<'JSONC'
|
|
204
|
+
// bassclef-configs.jsonc — unified adopter configuration discovery surface.
|
|
205
|
+
// JSON Schema: standards/state-spine/schemas/bassclef-configs.schema.json
|
|
206
|
+
{
|
|
207
|
+
"sync": { "enabled": false },
|
|
208
|
+
"model_tier": {
|
|
209
|
+
"Builder": "balanced",
|
|
210
|
+
"Closer": "cheap-fast",
|
|
211
|
+
"Deployer": "cheap-fast",
|
|
212
|
+
"DocsWriter": "cheap-fast",
|
|
213
|
+
"Explorer": "cheap-fast",
|
|
214
|
+
"Orchestrator": "cheap-fast",
|
|
215
|
+
"Reviewer": "cheap-fast",
|
|
216
|
+
"Shaper": "capable"
|
|
217
|
+
},
|
|
218
|
+
"plan_tier": "tier-1",
|
|
219
|
+
"prose_discipline": { "kiss_words_turn_prose": true },
|
|
220
|
+
"testing": { "global_floor": 1, "path_matchers": [] }
|
|
221
|
+
}
|
|
222
|
+
JSONC
|
|
223
|
+
|
|
224
|
+
# Minimal CLAUDE.md — operator owns this going forward
|
|
225
|
+
cat > CLAUDE.md <<'MD'
|
|
226
|
+
# <repo-name>
|
|
227
|
+
|
|
228
|
+
## What this repo is
|
|
229
|
+
|
|
230
|
+
Cold-adopter magic demo from a stated intent. Substrate inherited from
|
|
231
|
+
bassclef via `--add-dir ../bassclef` or thin-pointer fetch.
|
|
232
|
+
|
|
233
|
+
## Stack
|
|
234
|
+
|
|
235
|
+
To be determined by `/launch --local` output.
|
|
236
|
+
MD
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Skip in Path B:
|
|
240
|
+
|
|
241
|
+
- `gh repo create` (no remote)
|
|
242
|
+
- Phase 1.1 standard labels (no remote to apply them to)
|
|
243
|
+
- Phase 1.2 branch protection (same)
|
|
244
|
+
- Phase 1.3 prod environment (same)
|
|
245
|
+
- `.bassclef-source.json` (only needed for cloud/mobile sessions
|
|
246
|
+
where the sibling sync isn't available; cold adopters running
|
|
247
|
+
locally don't need it for the magic demo)
|
|
248
|
+
- Phase 3.5 interactive follow-on prompts (no `/provision-deploy-host`,
|
|
249
|
+
no `/secrets-bootstrap` — Path B has no vendor surfaces to bind)
|
|
250
|
+
|
|
251
|
+
#### Phase B.4 — Dispatch `/launch --local --voice-input`
|
|
252
|
+
|
|
253
|
+
Hand the intent paragraph straight to `/launch`:
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
/launch medium --local --voice-input "<intent paragraph from Phase B.1>"
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Path B's last action is this dispatch. Do not chain `/build` or
|
|
260
|
+
`/launch-preview` afterward — those belong to the extended path for
|
|
261
|
+
operators with vendor bindings.
|
|
262
|
+
|
|
263
|
+
#### Phase B.5 — `/launch --local` runs
|
|
264
|
+
|
|
265
|
+
`/launch` writes the full artifact chain to disk (input-artifact, spec,
|
|
266
|
+
prototypes gallery, user stories, decomposition, migration plan) and
|
|
267
|
+
serves the gallery on `localhost:8000+`. The localhost URL is the
|
|
268
|
+
deliverable. Operator inspects, picks a direction, and either keeps
|
|
269
|
+
iterating locally or moves to standard onboarding (Path A) when they're
|
|
270
|
+
ready to push to a GitHub repo.
|
|
271
|
+
|
|
272
|
+
When `/launch --local` returns, Path B is done. Skip everything below
|
|
273
|
+
this divider.
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
### Path A — standard onboarding
|
|
278
|
+
|
|
279
|
+
Run each phase in order. Operator confirms before destructive or
|
|
280
|
+
cross-repo actions. Each phase is independently re-runnable.
|
|
281
|
+
|
|
282
|
+
#### Phase 0 — Refuse to run from main (bassclef#956 V0.5)
|
|
283
|
+
|
|
284
|
+
Substrate changes go through PRs, same as feature work. If `/onboard-repo`
|
|
285
|
+
runs from `main`, every scaffold (`.claude/`, `CLAUDE.md`,
|
|
286
|
+
`docs/whereami.md`, `substrate.config.md`, `.bassclef-source.json`,
|
|
287
|
+
`.gitignore`) lands as a direct-to-main commit by surprise. Path A
|
|
288
|
+
refuses that shape and prints the remediation:
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
CURRENT_BRANCH=$(git branch --show-current 2>/dev/null || echo "")
|
|
292
|
+
|
|
293
|
+
case "$CURRENT_BRANCH" in
|
|
294
|
+
main|master)
|
|
295
|
+
cat >&2 <<'EOF'
|
|
296
|
+
|
|
297
|
+
🛑 /onboard-repo refuses to run from main.
|
|
298
|
+
|
|
299
|
+
Why: substrate scaffolding (`.claude/`, `CLAUDE.md`, `docs/whereami.md`,
|
|
300
|
+
`substrate.config.md`, `.bassclef-source.json`, `.gitignore`) is a
|
|
301
|
+
substrate change — it goes through a PR, not a direct commit to main.
|
|
302
|
+
|
|
303
|
+
Remediation:
|
|
304
|
+
|
|
305
|
+
git checkout -b chore/bassclef-bootstrap
|
|
306
|
+
# re-run /onboard-repo
|
|
307
|
+
|
|
308
|
+
When you reach the end of Phase 3, /onboard-repo will guide you to
|
|
309
|
+
open the bootstrap PR; merging it lands the substrate on main with
|
|
310
|
+
audit history (this is flywheel turn 1 from the first-use sequence —
|
|
311
|
+
see docs/first-use-sequence.md).
|
|
312
|
+
|
|
313
|
+
V1 (bassclef#956 follow-up) will auto-create the branch + PR. V0.5
|
|
314
|
+
keeps the operator in the loop on the branching decision.
|
|
315
|
+
|
|
316
|
+
EOF
|
|
317
|
+
exit 1
|
|
318
|
+
;;
|
|
319
|
+
"")
|
|
320
|
+
cat >&2 <<'EOF'
|
|
321
|
+
|
|
322
|
+
🛑 /onboard-repo cannot detect the current git branch.
|
|
323
|
+
|
|
324
|
+
The cwd may not be a git repository, OR git is in a detached-HEAD state.
|
|
325
|
+
|
|
326
|
+
Remediation:
|
|
327
|
+
|
|
328
|
+
git init # if not a git repo
|
|
329
|
+
git checkout -b chore/bassclef-bootstrap # from a fresh branch
|
|
330
|
+
# re-run /onboard-repo
|
|
331
|
+
|
|
332
|
+
EOF
|
|
333
|
+
exit 1
|
|
334
|
+
;;
|
|
335
|
+
*)
|
|
336
|
+
# Any other branch — proceed
|
|
337
|
+
;;
|
|
338
|
+
esac
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
The remediation message is the contract. If a cold adopter dispatches
|
|
342
|
+
`/onboard-repo` from `main`, they see exactly what to do — no operator
|
|
343
|
+
coaching, no second-guess. V0.5 ships in bassclef#956; V1
|
|
344
|
+
(auto-create branch + PR) is post-launch hardening per the goal's
|
|
345
|
+
operator-decision items.
|
|
346
|
+
|
|
347
|
+
#### Phase 1 — Set up the GitHub repo (labels, branch protection)
|
|
348
|
+
|
|
349
|
+
**Working directory: target repo** (`pwd` should match `gh repo view --json name -q .name`).
|
|
350
|
+
|
|
351
|
+
Idempotent API calls against the target repo via `gh`.
|
|
352
|
+
|
|
353
|
+
##### 1.1 Create the 11 standard labels
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
# Read from git remote — don't infer from cwd basename (cwd can be renamed)
|
|
357
|
+
REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
|
|
358
|
+
|
|
359
|
+
declare -A LABELS=(
|
|
360
|
+
[bassclef-evolution]="A6E0F2:Candidate for promotion to bassclef"
|
|
361
|
+
[agent-user-proposed]="D4A5F5:Proposed by agent in an app repo"
|
|
362
|
+
[bassclef-sync]="8FD4A4:Related to bassclef substrate syncing"
|
|
363
|
+
[review-required]="F5B94A:Blocks — human review required"
|
|
364
|
+
[bug]="E4443A:Defect"
|
|
365
|
+
[enhancement]="A2EEEF:New or improved functionality"
|
|
366
|
+
[security]="FF0000:Security-relevant"
|
|
367
|
+
[infra]="C9D1D9:Infrastructure / workflows / tooling"
|
|
368
|
+
[priority-high]="E4443A:High priority"
|
|
369
|
+
[priority-medium]="F5B94A:Medium priority"
|
|
370
|
+
[question]="D876E3:Needs operator input or design discussion"
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
for NAME in "${!LABELS[@]}"; do
|
|
374
|
+
COLOR="${LABELS[$NAME]%%:*}"
|
|
375
|
+
DESC="${LABELS[$NAME]#*:}"
|
|
376
|
+
gh label create "$NAME" --repo "$REPO" --color "$COLOR" --description "$DESC" --force >/dev/null
|
|
377
|
+
done
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Uses `--force` so the call is idempotent (updates existing label or
|
|
381
|
+
creates new). This matches the bootstrap logic shipped in bassclef
|
|
382
|
+
#160 (sync hook does the same for `bassclef-evolution` + `agent-user-proposed`);
|
|
383
|
+
this phase adds the broader set up-front so the operator can label issues
|
|
384
|
+
immediately without waiting for a sync cycle.
|
|
385
|
+
|
|
386
|
+
##### 1.2 Protect main from force-push and deletion
|
|
387
|
+
|
|
388
|
+
```bash
|
|
389
|
+
DEFAULT_BRANCH=$(gh repo view "$REPO" --json defaultBranchRef -q '.defaultBranchRef.name')
|
|
390
|
+
|
|
391
|
+
gh api "repos/$REPO/branches/$DEFAULT_BRANCH/protection" \
|
|
392
|
+
--method PUT \
|
|
393
|
+
--input - <<EOF
|
|
394
|
+
{
|
|
395
|
+
"required_status_checks": null,
|
|
396
|
+
"enforce_admins": false,
|
|
397
|
+
"required_pull_request_reviews": null,
|
|
398
|
+
"restrictions": null,
|
|
399
|
+
"allow_force_pushes": false,
|
|
400
|
+
"allow_deletions": false
|
|
401
|
+
}
|
|
402
|
+
EOF
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
Minimum defense: no force-push or deletion of main. PR reviews NOT
|
|
406
|
+
required here — agent-created PRs (bassclef-sync) need to merge
|
|
407
|
+
without human intervention for the mobile-sync story. Add stricter
|
|
408
|
+
rules per-repo in `.github/branch-protection.yml` if the repo warrants.
|
|
409
|
+
|
|
410
|
+
##### 1.3 Add the production approval gate (if this repo deploys to prod)
|
|
411
|
+
|
|
412
|
+
If the repo will deploy to production, per `manual-prod-approval.md`:
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
# Prompt operator: "Does this repo deploy to prod? [y/N]"
|
|
416
|
+
# If yes:
|
|
417
|
+
USER_ID=$(gh api user --jq '.id')
|
|
418
|
+
gh api "repos/$REPO/environments/prod" --method PUT \
|
|
419
|
+
-f "wait_timer=0" \
|
|
420
|
+
-f "deployment_branch_policy[protected_branches]=false" \
|
|
421
|
+
-f "deployment_branch_policy[custom_branch_policies]=true"
|
|
422
|
+
|
|
423
|
+
gh api "repos/$REPO/environments/prod" --method PUT \
|
|
424
|
+
-F "reviewers[][type]=User" \
|
|
425
|
+
-F "reviewers[][id]=$USER_ID"
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
Skip if repo is docs-only (like bassclef itself) or doesn't have a
|
|
429
|
+
production deploy target.
|
|
430
|
+
|
|
431
|
+
#### Phase 2 — Install bassclef wiring (settings, sync script, config)
|
|
432
|
+
|
|
433
|
+
**Working directory: target repo.** All file writes happen here.
|
|
434
|
+
|
|
435
|
+
##### 2.1 Install the auto-update script (off by default per bassclef#939)
|
|
436
|
+
|
|
437
|
+
```bash
|
|
438
|
+
mkdir -p .claude/hooks
|
|
439
|
+
|
|
440
|
+
# Install thin-pointer dispatcher from bassclef's own template directory.
|
|
441
|
+
# This is the source-of-truth file consumers vendor.
|
|
442
|
+
cp ~/src/sunj-labs/bassclef/presence/install/bassclef-sync.dispatcher.template.sh \
|
|
443
|
+
.claude/hooks/bassclef-sync.sh
|
|
444
|
+
|
|
445
|
+
# OR — if bassclef isn't checked out alongside this repo, fetch from
|
|
446
|
+
# bassclef's main branch on GitHub (public tarball release works too
|
|
447
|
+
# once bassclef itself goes public):
|
|
448
|
+
# curl -sL https://raw.githubusercontent.com/sunj-labs/bassclef/main/presence/install/bassclef-sync.dispatcher.template.sh \
|
|
449
|
+
# -o .claude/hooks/bassclef-sync.sh
|
|
450
|
+
|
|
451
|
+
chmod +x .claude/hooks/bassclef-sync.sh
|
|
452
|
+
|
|
453
|
+
# Verify the version header reads `thin-pointer-2026-05-15` or later:
|
|
454
|
+
head -2 .claude/hooks/bassclef-sync.sh
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
The thin-pointer dispatcher does NOT vendor bassclef's sync logic.
|
|
458
|
+
It reads `.bassclef-source.json` (Phase 2.4) for the upstream tarball
|
|
459
|
+
URL + auth, fetches it, and execs the included template — always-current.
|
|
460
|
+
Eliminates two prior patching layers: hook content drift (vendored
|
|
461
|
+
template diverging from bassclef's) and URL refresh ceremony.
|
|
462
|
+
|
|
463
|
+
When the dispatcher can't load the substrate (sibling missing AND curl
|
|
464
|
+
fallback unconfigured / unauthenticated), it prints a plain-language
|
|
465
|
+
explanation with three resolution paths (public adopter, local dev,
|
|
466
|
+
private fork). External adopters get actionable guidance instead of
|
|
467
|
+
internal-vocabulary errors. Closes the adoption-friendliness gap noted
|
|
468
|
+
in bassclef#715.
|
|
469
|
+
|
|
470
|
+
##### 2.2 Configure Claude Code for this repo
|
|
471
|
+
|
|
472
|
+
Standard bassclef settings — SessionStart → `bassclef-sync.sh`, +
|
|
473
|
+
other hooks per-agent configuration. Copy from a reference consumer
|
|
474
|
+
(POA or ops) and scrub for repo-specific paths:
|
|
475
|
+
|
|
476
|
+
```bash
|
|
477
|
+
cat > .claude/settings.json <<'JSON'
|
|
478
|
+
{
|
|
479
|
+
"permissions": {
|
|
480
|
+
"additionalDirectories": ["../bassclef"]
|
|
481
|
+
},
|
|
482
|
+
"hooks": {
|
|
483
|
+
"SessionStart": [
|
|
484
|
+
{
|
|
485
|
+
"matcher": "",
|
|
486
|
+
"hooks": [
|
|
487
|
+
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/bassclef-sync.sh", "timeout": 30 }
|
|
488
|
+
]
|
|
489
|
+
}
|
|
490
|
+
]
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
JSON
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
Operator confirms — may want to add app-specific hooks (pre-build-gate,
|
|
497
|
+
pre-commit-gate) if the repo has source code.
|
|
498
|
+
|
|
499
|
+
##### 2.2.1 Wire turn-prose-kiss-check Stop hook (bassclef#1138 T5)
|
|
500
|
+
|
|
501
|
+
Symlink the bassclef Stop hook that scans the most recent assistant
|
|
502
|
+
message for bassclef-internal jargon (the `/kiss words` BLOCK list
|
|
503
|
+
from `standards/bassclef-internal-jargon.md`). When
|
|
504
|
+
`prose_discipline.kiss_words_turn_prose` is `true` in
|
|
505
|
+
`.claude/bassclef-configs.jsonc` (the adopter-default Phase 2.3.5
|
|
506
|
+
writes), the hook fires after every assistant turn and surfaces
|
|
507
|
+
findings; when `false`, the hook short-circuits silently.
|
|
508
|
+
|
|
509
|
+
Symlink the hook (bassclef lives one directory up per the
|
|
510
|
+
`additionalDirectories` convention):
|
|
511
|
+
|
|
512
|
+
```bash
|
|
513
|
+
mkdir -p .claude/hooks
|
|
514
|
+
ln -sf "../../../bassclef/.claude/hooks/turn-prose-kiss-check.sh" \
|
|
515
|
+
".claude/hooks/turn-prose-kiss-check.sh"
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
Add the Stop hook block to `.claude/settings.json`. If the file already
|
|
519
|
+
has a Stop block (for example, `session-end.sh`), append this hook
|
|
520
|
+
alongside — both run, turn-prose-kiss-check first, session-end second:
|
|
521
|
+
|
|
522
|
+
```jsonc
|
|
523
|
+
{
|
|
524
|
+
"hooks": {
|
|
525
|
+
"Stop": [
|
|
526
|
+
{
|
|
527
|
+
"matcher": "",
|
|
528
|
+
"hooks": [
|
|
529
|
+
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/turn-prose-kiss-check.sh", "timeout": 10 }
|
|
530
|
+
]
|
|
531
|
+
}
|
|
532
|
+
]
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
**Composition rule** — when an existing Stop hook is present, leave it
|
|
538
|
+
in place and append this one. The turn-prose-kiss-check hook exits 0
|
|
539
|
+
even when findings surface (default toggle: advisory), so a downstream
|
|
540
|
+
session-end hook still runs. When the adopter opts in to strict mode
|
|
541
|
+
(`prose_discipline.kiss_words_turn_prose: "strict"` in
|
|
542
|
+
bassclef-configs.jsonc), the hook exits 2 and blocks the stop — that's
|
|
543
|
+
the conscious-strict trade-off.
|
|
544
|
+
|
|
545
|
+
**Conditional fire** — the hook reads
|
|
546
|
+
`.claude/bassclef-configs.jsonc` `prose_discipline.kiss_words_turn_prose`
|
|
547
|
+
at runtime and short-circuits when `false`. Adopters who never want the
|
|
548
|
+
check can flip the toggle without unwiring the hook.
|
|
549
|
+
|
|
550
|
+
**Verify the wiring:**
|
|
551
|
+
|
|
552
|
+
```bash
|
|
553
|
+
# Sanity: hook is executable + reachable
|
|
554
|
+
ls -l .claude/hooks/turn-prose-kiss-check.sh
|
|
555
|
+
|
|
556
|
+
# Sanity: settings.json parses
|
|
557
|
+
python3 -c "import json; json.load(open('.claude/settings.json'))"
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
**Composes with downstream Phase 2.x steps:**
|
|
561
|
+
|
|
562
|
+
- Phase 2.3.5 writes `prose_discipline.kiss_words_turn_prose: true` to
|
|
563
|
+
`.claude/bassclef-configs.jsonc` as the adopter default — this hook
|
|
564
|
+
fires when that toggle is true.
|
|
565
|
+
- Phase 2.5 (security scanning) is a separate hook wiring path; both
|
|
566
|
+
hooks can co-exist in the Stop block.
|
|
567
|
+
|
|
568
|
+
Source: bassclef#1051 (hook origin), bassclef#1138 (this wiring step).
|
|
569
|
+
|
|
570
|
+
##### 2.2.2 Wire substrate-clarity-gate PreToolUse hook (bassclef#1137 T4)
|
|
571
|
+
|
|
572
|
+
Symlink the bassclef PreToolUse hook that BLOCKs writes to
|
|
573
|
+
`.claude/skills/`, `.claude/rules/`, and `.claude/luminaries/` when
|
|
574
|
+
they violate the clarity discipline bassclef applies to its own
|
|
575
|
+
substrate — skill descriptions over 280 chars, rule + luminary
|
|
576
|
+
"NEVER" without paired "INSTEAD:" block, jargon usage where plain
|
|
577
|
+
language exists. Adopter writes to substrate paths trigger the same
|
|
578
|
+
gate bassclef does.
|
|
579
|
+
|
|
580
|
+
Symlink the hook + the bassclef baseline allowlist (the file that
|
|
581
|
+
grandfathers pre-existing violators during a grace window):
|
|
582
|
+
|
|
583
|
+
```bash
|
|
584
|
+
mkdir -p .claude/hooks
|
|
585
|
+
ln -sf "../../../bassclef/.claude/hooks/substrate-clarity-gate.sh" \
|
|
586
|
+
".claude/hooks/substrate-clarity-gate.sh"
|
|
587
|
+
ln -sf "../../../bassclef/.claude/hooks/substrate-clarity-allowlist.txt" \
|
|
588
|
+
".claude/hooks/substrate-clarity-allowlist.txt"
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
Add the PreToolUse Edit|Write block to `.claude/settings.json`. If an
|
|
592
|
+
`Edit|Write` matcher already exists from a prior phase, append this
|
|
593
|
+
hook to its `hooks` array instead of duplicating the matcher entry:
|
|
594
|
+
|
|
595
|
+
```jsonc
|
|
596
|
+
{
|
|
597
|
+
"hooks": {
|
|
598
|
+
"PreToolUse": [
|
|
599
|
+
{
|
|
600
|
+
"matcher": "Edit|Write",
|
|
601
|
+
"hooks": [
|
|
602
|
+
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/substrate-clarity-gate.sh" }
|
|
603
|
+
]
|
|
604
|
+
}
|
|
605
|
+
]
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
**Adopter allowlist convention** — the symlinked
|
|
611
|
+
`substrate-clarity-allowlist.txt` carries bassclef's baseline
|
|
612
|
+
grandfather list (pre-existing violators with a documented grace
|
|
613
|
+
window). When the adopter has their own pre-existing substrate
|
|
614
|
+
violations they want to grandfather during their own migration,
|
|
615
|
+
they create a sibling file:
|
|
616
|
+
|
|
617
|
+
```bash
|
|
618
|
+
touch .claude/hooks/substrate-clarity-allowlist-adopter.txt
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
The hook reads BOTH files in series — bassclef's baseline first,
|
|
622
|
+
then the adopter-side append. Entries in the adopter file follow
|
|
623
|
+
the same one-path-per-line format as bassclef's baseline. The
|
|
624
|
+
adopter file is git-tracked in the adopter repo (not symlinked
|
|
625
|
+
into bassclef), so each adopter manages their own grandfather
|
|
626
|
+
list independently.
|
|
627
|
+
|
|
628
|
+
**Composition rule** — when an existing PreToolUse Edit|Write
|
|
629
|
+
matcher block is present (for example, `artifact-ingestion-gate.sh`
|
|
630
|
+
from Phase 2.x), append this hook's command entry to the same
|
|
631
|
+
matcher's `hooks` array. The matcher entry must not be duplicated;
|
|
632
|
+
the array of hooks under one matcher can hold multiple commands
|
|
633
|
+
that fire in series.
|
|
634
|
+
|
|
635
|
+
**Override path** — adopter can bypass per-call with
|
|
636
|
+
`SKIP_SUBSTRATE_CLARITY=1 <command>` (logged via trace-helper per
|
|
637
|
+
bassclef override idiom). The override is for migration scripts
|
|
638
|
+
and emergency rescue; routine use should fix the violation, not
|
|
639
|
+
bypass.
|
|
640
|
+
|
|
641
|
+
**Verify the wiring:**
|
|
642
|
+
|
|
643
|
+
```bash
|
|
644
|
+
# Sanity: hook executable + reachable
|
|
645
|
+
ls -l .claude/hooks/substrate-clarity-gate.sh
|
|
646
|
+
ls -l .claude/hooks/substrate-clarity-allowlist.txt
|
|
647
|
+
|
|
648
|
+
# Sanity: settings.json parses + has the matcher
|
|
649
|
+
python3 -c "import json; d=json.load(open('.claude/settings.json')); \
|
|
650
|
+
print('substrate-clarity-gate wired' if any( \
|
|
651
|
+
'substrate-clarity-gate' in h.get('command','') \
|
|
652
|
+
for entry in d.get('hooks',{}).get('PreToolUse',[]) \
|
|
653
|
+
for h in entry.get('hooks',[])) else 'NOT wired')"
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
Source: bassclef#382 (hook origin), bassclef#1137 (this wiring step), bassclef#1135 (T2 config default — separate concern).
|
|
657
|
+
|
|
658
|
+
##### 2.2.3 Wire artifact-ingestion-gate PreToolUse hook (bassclef#1140 T7)
|
|
659
|
+
|
|
660
|
+
Symlink the bassclef PreToolUse hook that BLOCKs writes to adopter
|
|
661
|
+
substantive artifacts (specs, decompositions, prototypes, ADRs,
|
|
662
|
+
proposals, iteration goals) when the artifact body lacks a
|
|
663
|
+
`## Sources read` section. The discipline catches the
|
|
664
|
+
"pattern-matched on generic X" failure mode that
|
|
665
|
+
`.claude/rules/artifact-ingestion.md` was built to close — applied at
|
|
666
|
+
adopter writes, not just at bassclef's own writes.
|
|
667
|
+
|
|
668
|
+
Symlink the hook:
|
|
669
|
+
|
|
670
|
+
```bash
|
|
671
|
+
mkdir -p .claude/hooks
|
|
672
|
+
ln -sf "../../../bassclef/.claude/hooks/artifact-ingestion-gate.sh" \
|
|
673
|
+
".claude/hooks/artifact-ingestion-gate.sh"
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
Add the PreToolUse Write block to `.claude/settings.json`. The hook
|
|
677
|
+
matches on `Write` (not `Edit`) — edits to existing artifacts pass
|
|
678
|
+
through; only new artifact writes trigger the gate. If a Write
|
|
679
|
+
matcher already exists from a prior phase, append this hook to its
|
|
680
|
+
`hooks` array:
|
|
681
|
+
|
|
682
|
+
```jsonc
|
|
683
|
+
{
|
|
684
|
+
"hooks": {
|
|
685
|
+
"PreToolUse": [
|
|
686
|
+
{
|
|
687
|
+
"matcher": "Write",
|
|
688
|
+
"hooks": [
|
|
689
|
+
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/artifact-ingestion-gate.sh" }
|
|
690
|
+
]
|
|
691
|
+
}
|
|
692
|
+
]
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
**Adopter-default path matchers** — the hook reads
|
|
698
|
+
`.claude/bassclef-configs.jsonc` `artifact_ingestion.paths` to
|
|
699
|
+
decide which paths fire the gate. Phase 2.3.5 (bassclef-configs
|
|
700
|
+
adopter-default seed) appends this block with the bassclef default
|
|
701
|
+
list. Adopters edit the list per repo — adding paths their substrate
|
|
702
|
+
considers substantive, or removing paths that do not apply:
|
|
703
|
+
|
|
704
|
+
```jsonc
|
|
705
|
+
"artifact_ingestion": {
|
|
706
|
+
"paths": [
|
|
707
|
+
"docs/specs/**/*.md",
|
|
708
|
+
"docs/decompositions/**/*.md",
|
|
709
|
+
"docs/prototypes/**/*.html",
|
|
710
|
+
"docs/proposals/**/*.md",
|
|
711
|
+
"docs/adrs/**/*.md",
|
|
712
|
+
"docs/use-cases/**/*.md",
|
|
713
|
+
"docs/user-stories/**/*.md",
|
|
714
|
+
"docs/ia-models/**/*.md",
|
|
715
|
+
"docs/interaction-design/**/*",
|
|
716
|
+
"docs/personas/**/*.md",
|
|
717
|
+
"docs/jtbd-tasks/**/*.md",
|
|
718
|
+
"docs/value-props/**/*.md",
|
|
719
|
+
"docs/iteration-bets/**/*.md"
|
|
720
|
+
]
|
|
721
|
+
}
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
When the `artifact_ingestion.paths` field is absent (older adopters
|
|
725
|
+
predating this Phase), the hook falls back to its built-in default
|
|
726
|
+
matcher list — the wiring stays safe and quiet.
|
|
727
|
+
|
|
728
|
+
**Composition rule** — when an existing PreToolUse Write matcher is
|
|
729
|
+
present (for example, from a sibling hook in another Phase 2.x step),
|
|
730
|
+
append this hook's command entry to the same matcher's `hooks` array.
|
|
731
|
+
The matcher entry must not be duplicated.
|
|
732
|
+
|
|
733
|
+
**Override path** — adopter can bypass per-call with
|
|
734
|
+
`SKIP_ARTIFACT_INGESTION=1 <command>` (logged via trace-helper). Use
|
|
735
|
+
for migration scripts and emergency rescue.
|
|
736
|
+
|
|
737
|
+
**Verify the wiring:**
|
|
738
|
+
|
|
739
|
+
```bash
|
|
740
|
+
# Sanity: hook executable + reachable
|
|
741
|
+
ls -l .claude/hooks/artifact-ingestion-gate.sh
|
|
742
|
+
|
|
743
|
+
# Sanity: settings.json has the matcher
|
|
744
|
+
python3 -c "import json; d=json.load(open('.claude/settings.json')); \
|
|
745
|
+
print('artifact-ingestion-gate wired' if any( \
|
|
746
|
+
'artifact-ingestion-gate' in h.get('command','') \
|
|
747
|
+
for entry in d.get('hooks',{}).get('PreToolUse',[]) \
|
|
748
|
+
for h in entry.get('hooks',[])) else 'NOT wired')"
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
Source: bassclef#116 (hook origin), bassclef#1140 (this wiring step).
|
|
752
|
+
|
|
753
|
+
##### 2.2.4 Wire pattern-annotation-validate PreToolUse hook (bassclef#1139 T6)
|
|
754
|
+
|
|
755
|
+
**Lite adopter note (bassclef#740 Task 1.2).** This hook ships at `tier: standard`. If your adopter tier is lite (resolved via `lib/tier-check.sh`), skip this Phase step and continue with 2.2.5. The bassclef-lite tarball does not carry this hook.
|
|
756
|
+
|
|
757
|
+
Symlink the bassclef PreToolUse hook that validates `@pattern <catalog-path>` annotations in adopter source code. Layer 3 of bassclef#478 traceability — code that instantiates a named pattern from bassclef's catalog (`patterns/<shelf>/<source>/<slug>.md`) must mark itself with the annotation so forward (which code uses this pattern) and reverse (which patterns is this code using) lookups work across the adopter portfolio.
|
|
758
|
+
|
|
759
|
+
Symlink the hook:
|
|
760
|
+
|
|
761
|
+
```bash
|
|
762
|
+
mkdir -p .claude/hooks
|
|
763
|
+
ln -sf "../../../bassclef/.claude/hooks/pattern-annotation-validate.sh" \
|
|
764
|
+
".claude/hooks/pattern-annotation-validate.sh"
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
Add the PreToolUse Edit|Write|MultiEdit block to `.claude/settings.json`. If an `Edit|Write` matcher already exists from a prior phase, append this hook to its `hooks` array (the bassclef settings.json convention is to widen matchers when adding new hooks that share a tool set):
|
|
768
|
+
|
|
769
|
+
```jsonc
|
|
770
|
+
{
|
|
771
|
+
"hooks": {
|
|
772
|
+
"PreToolUse": [
|
|
773
|
+
{
|
|
774
|
+
"matcher": "Edit|Write|MultiEdit",
|
|
775
|
+
"hooks": [
|
|
776
|
+
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pattern-annotation-validate.sh" }
|
|
777
|
+
]
|
|
778
|
+
}
|
|
779
|
+
]
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
**Rule propagation** — the `pattern-annotation` rule lives at bassclef's `.claude/rules/pattern-annotation.md` and loads into adopter agent context via the `additionalDirectories: ["../bassclef"]` entry Phase 2.2 wrote. No copy needed; bassclef's rule body is the source of truth.
|
|
785
|
+
|
|
786
|
+
**Tech-stack-aware scoping** — the hook reads adopter's `.claude/bassclef-configs.jsonc` `tech_stack.lang` (Phase 2.3.6 writes) and fires only on source files matching the declared language: `.ts/.tsx` for `typescript`, `.py` for `python`, `.rb` for `ruby`, `.go` for `go`, `.rs` for `rust`. Non-source files silent. When `tech_stack.lang` is absent (older adopters or skip path), the hook fires on the union of language matchers (safe default).
|
|
787
|
+
|
|
788
|
+
**Composition rule** — when an existing PreToolUse Edit|Write matcher block is present (substrate-clarity-gate from 2.2.2 uses `Edit|Write`), the matcher set widens to `Edit|Write|MultiEdit` to cover both hooks. Adopters can keep the matchers separate if they prefer narrower scoping per hook.
|
|
789
|
+
|
|
790
|
+
**Override path** — `SKIP_PATTERN_ANNOTATION=1 <command>` (logged via trace-helper). Use for migration scripts when annotating retroactively is in flight.
|
|
791
|
+
|
|
792
|
+
**Verify the wiring:**
|
|
793
|
+
|
|
794
|
+
```bash
|
|
795
|
+
# Sanity: hook executable + reachable
|
|
796
|
+
ls -l .claude/hooks/pattern-annotation-validate.sh
|
|
797
|
+
|
|
798
|
+
# Sanity: rule loads via additionalDirectories
|
|
799
|
+
test -f ../bassclef/.claude/rules/pattern-annotation.md && echo "rule reachable" || echo "rule NOT reachable"
|
|
800
|
+
|
|
801
|
+
# Sanity: settings.json has the matcher
|
|
802
|
+
python3 -c "import json; d=json.load(open('.claude/settings.json')); \
|
|
803
|
+
print('pattern-annotation-validate wired' if any( \
|
|
804
|
+
'pattern-annotation-validate' in h.get('command','') \
|
|
805
|
+
for entry in d.get('hooks',{}).get('PreToolUse',[]) \
|
|
806
|
+
for h in entry.get('hooks',[])) else 'NOT wired')"
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
Source: bassclef#478 (parent traceability goal), bassclef#1139 (this wiring step), bassclef#1127 (tech_stack vocabulary the scoping reads).
|
|
810
|
+
|
|
811
|
+
##### 2.2.5 Wire pre-commit-gate + state-validate PreToolUse hooks (bassclef#1136 T3)
|
|
812
|
+
|
|
813
|
+
Symlink the two bassclef PreToolUse hooks that gate adopter writes.
|
|
814
|
+
|
|
815
|
+
`pre-commit-gate.sh` runs the SDLC pre-build checks per `.claude/rules/sdlc-gates.md`. Those checks are temperance + diagnosis + scope drift.
|
|
816
|
+
|
|
817
|
+
`state-validate.sh` validates state-spine entity writes against JSON Schema per `.claude/rules/state-schema-validation.md`.
|
|
818
|
+
|
|
819
|
+
Together the hooks catch two failure classes before any source edit lands: skipped gates (silent SDLC bypass) and malformed state writes (silent schema drift).
|
|
820
|
+
|
|
821
|
+
Symlink both hooks:
|
|
822
|
+
|
|
823
|
+
```bash
|
|
824
|
+
mkdir -p .claude/hooks
|
|
825
|
+
ln -sf "../../../bassclef/.claude/hooks/pre-commit-gate.sh" \
|
|
826
|
+
".claude/hooks/pre-commit-gate.sh"
|
|
827
|
+
ln -sf "../../../bassclef/.claude/hooks/state-validate.sh" \
|
|
828
|
+
".claude/hooks/state-validate.sh"
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
Add the PreToolUse Edit|Write|MultiEdit block to `.claude/settings.json`. If an `Edit|Write` or `Edit|Write|MultiEdit` matcher already exists from a prior phase (substrate-clarity-gate from 2.2.2, pattern-annotation-validate from 2.2.4), append both hooks to its `hooks` array — the matcher set widens to `Edit|Write|MultiEdit` when MultiEdit is not already covered:
|
|
832
|
+
|
|
833
|
+
```jsonc
|
|
834
|
+
{
|
|
835
|
+
"hooks": {
|
|
836
|
+
"PreToolUse": [
|
|
837
|
+
{
|
|
838
|
+
"matcher": "Edit|Write|MultiEdit",
|
|
839
|
+
"hooks": [
|
|
840
|
+
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-commit-gate.sh" },
|
|
841
|
+
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/state-validate.sh" }
|
|
842
|
+
]
|
|
843
|
+
}
|
|
844
|
+
]
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
**Order matters** — `pre-commit-gate.sh` runs first (cheap SDLC checks), `state-validate.sh` runs second (heavier ajv-cli schema validation). The matcher block lists them in that order; a BLOCK from the first stops the chain before the second fires.
|
|
850
|
+
|
|
851
|
+
**Rule propagation** — both rules (`sdlc-gates.md` + `state-schema-validation.md`) live at bassclef's `.claude/rules/` and load into adopter agent context via the `additionalDirectories: ["../bassclef"]` entry Phase 2.2 wrote. No copy needed; bassclef's rule body is the source of truth.
|
|
852
|
+
|
|
853
|
+
**Composition rule** — when an existing PreToolUse Edit|Write matcher block is present from earlier Phase 2.x steps, append these two hooks to its `hooks` array (do not duplicate the matcher entry). Hook order within the array determines execution order; place `pre-commit-gate.sh` before `state-validate.sh` per the order rule above.
|
|
854
|
+
|
|
855
|
+
**Override paths** — `SKIP_PRE_COMMIT_GATE=1 <command>` and `SKIP_STATE_VALIDATE=1 <command>` (both logged via trace-helper). Use only for one-shot migration scripts (state bootstrapping) or emergency rescue when the hook itself is broken.
|
|
856
|
+
|
|
857
|
+
**Verify the wiring:**
|
|
858
|
+
|
|
859
|
+
```bash
|
|
860
|
+
# Sanity: both hooks executable + reachable
|
|
861
|
+
ls -l .claude/hooks/pre-commit-gate.sh .claude/hooks/state-validate.sh
|
|
862
|
+
|
|
863
|
+
# Sanity: both rules reachable via additionalDirectories
|
|
864
|
+
test -f ../bassclef/.claude/rules/sdlc-gates.md && echo "sdlc-gates reachable" || echo "sdlc-gates NOT reachable"
|
|
865
|
+
test -f ../bassclef/.claude/rules/state-schema-validation.md && echo "state-schema-validation reachable" || echo "state-schema-validation NOT reachable"
|
|
866
|
+
|
|
867
|
+
# Sanity: settings.json has both hooks wired in matcher block
|
|
868
|
+
python3 -c "import json; d=json.load(open('.claude/settings.json')); \
|
|
869
|
+
hooks=[h.get('command','') for entry in d.get('hooks',{}).get('PreToolUse',[]) for h in entry.get('hooks',[])]; \
|
|
870
|
+
print('pre-commit-gate wired' if any('pre-commit-gate' in c for c in hooks) else 'NOT wired'); \
|
|
871
|
+
print('state-validate wired' if any('state-validate' in c for c in hooks) else 'NOT wired')"
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
Source: bassclef#1136 (this wiring step — T3, iteration-2 BLOCKER). Sister rules: `.claude/rules/sdlc-gates.md` + `.claude/rules/state-schema-validation.md`.
|
|
875
|
+
|
|
876
|
+
##### 2.2.6 Wire destructive-command-guard + multi-tenancy-check PreToolUse hooks (bassclef#1141 T8 — partial)
|
|
877
|
+
|
|
878
|
+
**Lite adopter note (bassclef#740 Task 1.2).** Both hooks ship at `tier: standard`. Skip this step if your tier is lite. Resolve tier via `lib/tier-check.sh`. Continue with 2.2.7.
|
|
879
|
+
|
|
880
|
+
Symlink two bassclef PreToolUse hooks that catch runtime-safety problems before they reach the operator's filesystem or database. `destructive-command-guard.sh` fires on Bash and detects destructive verbs + force flags + amplifiers (catches "prisma migrate reset", "rm -rf", "FLUSHALL" patterns). `multi-tenancy-check.sh` fires on Edit/Write of source files and warns when tenant-scoped patterns are missing (queries without tenant context).
|
|
881
|
+
|
|
882
|
+
Bet 2026-06-18b scopes T8 to these two hooks; the ticket's other two (`operator-private-path-check.sh` + `type-check.sh`) are deferred to a follow-on per goal narrowing.
|
|
883
|
+
|
|
884
|
+
Symlink both hooks:
|
|
885
|
+
|
|
886
|
+
```bash
|
|
887
|
+
mkdir -p .claude/hooks
|
|
888
|
+
ln -sf "../../../bassclef/.claude/hooks/destructive-command-guard.sh" \
|
|
889
|
+
".claude/hooks/destructive-command-guard.sh"
|
|
890
|
+
ln -sf "../../../bassclef/.claude/hooks/multi-tenancy-check.sh" \
|
|
891
|
+
".claude/hooks/multi-tenancy-check.sh"
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
Add both PreToolUse blocks to `.claude/settings.json`. The matchers differ — `destructive-command-guard` watches Bash; `multi-tenancy-check` watches Edit|Write|MultiEdit. The Edit|Write|MultiEdit block likely already exists from Phase 2.2.2 / 2.2.3 / 2.2.4 / 2.2.5 — append `multi-tenancy-check` to its `hooks` array (do not duplicate the matcher entry). The Bash block is new for most adopters; create it if absent.
|
|
895
|
+
|
|
896
|
+
```jsonc
|
|
897
|
+
{
|
|
898
|
+
"hooks": {
|
|
899
|
+
"PreToolUse": [
|
|
900
|
+
{
|
|
901
|
+
"matcher": "Bash",
|
|
902
|
+
"hooks": [
|
|
903
|
+
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/destructive-command-guard.sh" }
|
|
904
|
+
]
|
|
905
|
+
},
|
|
906
|
+
{
|
|
907
|
+
"matcher": "Edit|Write|MultiEdit",
|
|
908
|
+
"hooks": [
|
|
909
|
+
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/multi-tenancy-check.sh" }
|
|
910
|
+
]
|
|
911
|
+
}
|
|
912
|
+
]
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
**Customization** — `multi-tenancy-check.sh` is a template. Adopters with multi-tenant codebases override `TENANT_MODELS` + `TENANT_CONTEXT_FN` + `TENANT_PARAMS_FN` via env vars OR drop a custom `multi-tenancy-check.sh` in `.claude/hooks/` (which wins over the symlink). Single-tenant adopters can disable per-call with `SKIP_MULTI_TENANCY_CHECK=1`.
|
|
918
|
+
|
|
919
|
+
**Rule propagation** — `.claude/rules/destructive-operations.md` loads into adopter agent context via the `additionalDirectories: ["../bassclef"]` entry Phase 2.2 wrote. No copy needed.
|
|
920
|
+
|
|
921
|
+
**Composition rule** — Bash and Edit|Write|MultiEdit matchers stay separate; never collapse them. When other Phase 2.x steps add Bash hooks (Phase 2.2.7 ships atomic-pr-check on Bash; type-check on Bash if T8 follow-on ships), append to the Bash matcher's `hooks` array — do not duplicate the matcher entry.
|
|
922
|
+
|
|
923
|
+
**Override paths** — `SKIP_DESTRUCTIVE_GUARD=1 <command>` (logged) and `SKIP_MULTI_TENANCY_CHECK=1 <command>` (logged). Both bypass logged via trace-helper. Use only for genuine cases (one-shot migrations, single-tenant repos where the check is noise).
|
|
924
|
+
|
|
925
|
+
**Verify the wiring:**
|
|
926
|
+
|
|
927
|
+
```bash
|
|
928
|
+
# Sanity: both hooks executable + reachable
|
|
929
|
+
ls -l .claude/hooks/destructive-command-guard.sh .claude/hooks/multi-tenancy-check.sh
|
|
930
|
+
|
|
931
|
+
# Sanity: destructive-operations rule reachable via additionalDirectories
|
|
932
|
+
test -f ../bassclef/.claude/rules/destructive-operations.md && echo "destructive-operations reachable" || echo "NOT reachable"
|
|
933
|
+
|
|
934
|
+
# Sanity: settings.json has both hooks wired with correct matchers
|
|
935
|
+
python3 -c "import json; d=json.load(open('.claude/settings.json')); \
|
|
936
|
+
bash_hooks=[h.get('command','') for entry in d.get('hooks',{}).get('PreToolUse',[]) if entry.get('matcher')=='Bash' for h in entry.get('hooks',[])]; \
|
|
937
|
+
edit_hooks=[h.get('command','') for entry in d.get('hooks',{}).get('PreToolUse',[]) if 'Edit' in entry.get('matcher','') for h in entry.get('hooks',[])]; \
|
|
938
|
+
print('destructive-command-guard wired (Bash)' if any('destructive-command-guard' in c for c in bash_hooks) else 'NOT wired (Bash)'); \
|
|
939
|
+
print('multi-tenancy-check wired (Edit|Write|MultiEdit)' if any('multi-tenancy-check' in c for c in edit_hooks) else 'NOT wired (Edit|Write|MultiEdit)')"
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
Source: bassclef#1141 (this wiring step — T8, runtime-safety chain). Sister rule: `.claude/rules/destructive-operations.md`. Follow-on: `operator-private-path-check.sh` + `type-check.sh` wires deferred from T8 goal scope.
|
|
943
|
+
|
|
944
|
+
##### 2.2.7 Wire atomic-pr-check + post-write-trace workflow hooks (bassclef#1142 T9)
|
|
945
|
+
|
|
946
|
+
**Lite adopter note (bassclef#740 Task 1.2).** Both hooks ship at `tier: standard`. Skip this step if your tier is lite. Resolve tier via `lib/tier-check.sh`. Continue with the next step.
|
|
947
|
+
|
|
948
|
+
Symlink two bassclef workflow hooks. `atomic-pr-check.sh` fires on Bash calls matching `git push` patterns and BLOCKs multi-step branches that lack `bundle: true` in the iteration goal's frontmatter — closes the silent atomic-PR-discipline gap that `.claude/rules/pr-strategy.md` describes. `post-write-trace.sh` fires on PostToolUse Write/Edit and records traceability rows so the agent can audit what wrote what.
|
|
949
|
+
|
|
950
|
+
Symlink both hooks:
|
|
951
|
+
|
|
952
|
+
```bash
|
|
953
|
+
mkdir -p .claude/hooks
|
|
954
|
+
ln -sf "../../../bassclef/.claude/hooks/atomic-pr-check.sh" \
|
|
955
|
+
".claude/hooks/atomic-pr-check.sh"
|
|
956
|
+
ln -sf "../../../bassclef/.claude/hooks/post-write-trace.sh" \
|
|
957
|
+
".claude/hooks/post-write-trace.sh"
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
Add a PreToolUse Bash block + a PostToolUse Write|Edit block to `.claude/settings.json`. The Bash matcher likely already exists from Phase 2.2.6 (destructive-command-guard) — append `atomic-pr-check` to its `hooks` array. The PostToolUse block is new for most adopters.
|
|
961
|
+
|
|
962
|
+
```jsonc
|
|
963
|
+
{
|
|
964
|
+
"hooks": {
|
|
965
|
+
"PreToolUse": [
|
|
966
|
+
{
|
|
967
|
+
"matcher": "Bash",
|
|
968
|
+
"hooks": [
|
|
969
|
+
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/atomic-pr-check.sh" }
|
|
970
|
+
]
|
|
971
|
+
}
|
|
972
|
+
],
|
|
973
|
+
"PostToolUse": [
|
|
974
|
+
{
|
|
975
|
+
"matcher": "Write|Edit",
|
|
976
|
+
"hooks": [
|
|
977
|
+
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/post-write-trace.sh" }
|
|
978
|
+
]
|
|
979
|
+
}
|
|
980
|
+
]
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
```
|
|
984
|
+
|
|
985
|
+
**Bet narrowing** — ticket #1142 (T9) also lists `post-skill-friction-check.sh`. The goal scopes this step to two hooks; the third deferred to a follow-on per goal shape.
|
|
986
|
+
|
|
987
|
+
**Composition rule** — when the Bash matcher already exists (Phase 2.2.6 wires destructive-command-guard on the same matcher), append `atomic-pr-check` to its `hooks` array; do not duplicate the matcher entry. Hook order within the Bash array does not matter for these two — destructive-command-guard and atomic-pr-check check different signals (destructive verbs vs git-push patterns) and never conflict on the same command.
|
|
988
|
+
|
|
989
|
+
**Rule propagation** — `.claude/rules/pr-strategy.md` (the methodology atomic-pr-check enforces) loads via `additionalDirectories`. No copy needed.
|
|
990
|
+
|
|
991
|
+
**Override paths** — `SKIP_ATOMIC_PR_CHECK=1 <command>` (logged via trace-helper) bypasses the atomic-PR gate for genuine bundle cases that the goal's `bundle: true` flag did not capture. `post-write-trace.sh` has no override — the trace is append-only and never blocks.
|
|
992
|
+
|
|
993
|
+
**Verify the wiring:**
|
|
994
|
+
|
|
995
|
+
```bash
|
|
996
|
+
# Sanity: both hooks executable + reachable
|
|
997
|
+
ls -l .claude/hooks/atomic-pr-check.sh .claude/hooks/post-write-trace.sh
|
|
998
|
+
|
|
999
|
+
# Sanity: pr-strategy rule reachable via additionalDirectories
|
|
1000
|
+
test -f ../bassclef/.claude/rules/pr-strategy.md && echo "pr-strategy reachable" || echo "NOT reachable"
|
|
1001
|
+
|
|
1002
|
+
# Sanity: settings.json has both hooks wired with correct matchers
|
|
1003
|
+
python3 -c "import json; d=json.load(open('.claude/settings.json')); \
|
|
1004
|
+
bash_hooks=[h.get('command','') for entry in d.get('hooks',{}).get('PreToolUse',[]) if entry.get('matcher')=='Bash' for h in entry.get('hooks',[])]; \
|
|
1005
|
+
post_hooks=[h.get('command','') for entry in d.get('hooks',{}).get('PostToolUse',[]) if 'Write' in entry.get('matcher','') or 'Edit' in entry.get('matcher','') for h in entry.get('hooks',[])]; \
|
|
1006
|
+
print('atomic-pr-check wired (PreToolUse Bash)' if any('atomic-pr-check' in c for c in bash_hooks) else 'NOT wired'); \
|
|
1007
|
+
print('post-write-trace wired (PostToolUse Write|Edit)' if any('post-write-trace' in c for c in post_hooks) else 'NOT wired')"
|
|
1008
|
+
```
|
|
1009
|
+
|
|
1010
|
+
Source: bassclef#1142 (this wiring step — T9, workflow chain). Sister rule: `.claude/rules/pr-strategy.md`. Follow-on: `post-skill-friction-check.sh` wire deferred from T9 goal scope.
|
|
1011
|
+
|
|
1012
|
+
##### 2.2.8 Wire testing-tier-enforce + allowlist (bassclef#1143 T10)
|
|
1013
|
+
|
|
1014
|
+
Symlink the bassclef testing-tier enforcer into adopter's `.claude/hooks/`. The hook covers Tier 0 through Tier 3. It does not register as its own PreToolUse entry. Instead it runs inside `pre-commit-gate.sh` from Phase 2.2.5. When an adopter edits a Tier 0 source file without touching its matching test file, `pre-commit-gate.sh` calls `testing-tier-enforce.sh`. The commit BLOCKs with a structured stderr message.
|
|
1015
|
+
|
|
1016
|
+
Symlink both the hook and the allowlist baseline:
|
|
1017
|
+
|
|
1018
|
+
```bash
|
|
1019
|
+
mkdir -p .claude/hooks
|
|
1020
|
+
ln -sf "../../../bassclef/.claude/hooks/testing-tier-enforce.sh" \
|
|
1021
|
+
".claude/hooks/testing-tier-enforce.sh"
|
|
1022
|
+
ln -sf "../../../bassclef/.claude/hooks/testing-tier-enforce-allowlist.txt" \
|
|
1023
|
+
".claude/hooks/testing-tier-enforce-allowlist.txt"
|
|
1024
|
+
```
|
|
1025
|
+
|
|
1026
|
+
The allowlist baseline grandfathers existing untested adopter source files for 60 days (the default grace window). Adopter operators can backfill tests as files are next touched, OR adjust the grace window per `testing-tier-config.md` § Allowlist.
|
|
1027
|
+
|
|
1028
|
+
**Composition chain** — `pre-commit-gate.sh` (Phase 2.2.5) calls `testing-tier-enforce.sh` as one of its check steps. No new PreToolUse settings.json entry is needed; the hook fires whenever pre-commit-gate fires (PreToolUse Edit|Write|MultiEdit). This is the chained-composition pattern: Phase 2.2.5 wired the parent gate; Phase 2.2.8 plugs the tier-enforce check into the existing chain.
|
|
1029
|
+
|
|
1030
|
+
**Goal-vs-implementation note** — the goal 2026-06-18b WU-4 acceptance describes `testing-tier-enforce.sh` as "PreToolUse Edit|Write" wiring. The actual wiring is via pre-commit-gate composition (no separate matcher entry). Documented per the actual implementation.
|
|
1031
|
+
|
|
1032
|
+
**Rule propagation** — `.claude/rules/testing-tier-config.md` + `.claude/rules/test-list-discipline.md` + `.claude/rules/test-sufficiency.md` load via `additionalDirectories`. No copy needed.
|
|
1033
|
+
|
|
1034
|
+
**Override path** — `SKIP_TESTING_TIER_ENFORCE=1 <command>` (logged via trace-helper). Use only for one-shot migration scripts that bootstrap test files into the spine.
|
|
1035
|
+
|
|
1036
|
+
**Verify the wiring:**
|
|
1037
|
+
|
|
1038
|
+
```bash
|
|
1039
|
+
# Sanity: hook + allowlist reachable
|
|
1040
|
+
ls -l .claude/hooks/testing-tier-enforce.sh .claude/hooks/testing-tier-enforce-allowlist.txt
|
|
1041
|
+
|
|
1042
|
+
# Sanity: pre-commit-gate composes testing-tier-enforce
|
|
1043
|
+
grep -q "testing-tier-enforce.sh" .claude/hooks/pre-commit-gate.sh \
|
|
1044
|
+
&& echo "tier-enforce composed in pre-commit-gate" \
|
|
1045
|
+
|| echo "NOT composed (pre-commit-gate may be stale; resync bassclef)"
|
|
1046
|
+
|
|
1047
|
+
# Sanity: testing-tier-config rule reachable
|
|
1048
|
+
test -f ../bassclef/.claude/rules/testing-tier-config.md && echo "tier-config rule reachable" || echo "NOT reachable"
|
|
1049
|
+
```
|
|
1050
|
+
|
|
1051
|
+
The `testing.path_matchers` seed (per-tech-stack defaults) lands in Phase 2.3.7 below — that is the step the goal flagged for operator-eyes design choices.
|
|
1052
|
+
|
|
1053
|
+
Source: bassclef#1143 (this wiring step — T10, iteration-2 BLOCKER). Sister rules: `.claude/rules/testing-tier-config.md` + `.claude/rules/test-list-discipline.md` + `.claude/rules/test-sufficiency.md`. Pairs with Phase 2.2.5 (pre-commit-gate parent chain).
|
|
1054
|
+
|
|
1055
|
+
##### 2.2.9 Verify test-discipline rules propagate to the adopter session (bassclef#1144 T11)
|
|
1056
|
+
|
|
1057
|
+
The two test-discipline rules propagate to the adopter session on their own. Those rules are `.claude/rules/test-list-discipline.md` (Beck's test list as planning artifact) and `.claude/rules/test-sufficiency.md` (10-criterion rubric per Myers + Hoare).
|
|
1058
|
+
|
|
1059
|
+
The `additionalDirectories: ["../bassclef"]` entry that Phase 2.2 wrote does the work. It tells Claude Code to load every file under bassclef's `.claude/rules/` into the adopter's session context at session-start.
|
|
1060
|
+
|
|
1061
|
+
No copy needed. No per-rule symlink needed.
|
|
1062
|
+
|
|
1063
|
+
This step is the explicit verification — without it, the install is silent and the adopter does not know the rules are live.
|
|
1064
|
+
|
|
1065
|
+
Verify the rules are reachable + loaded:
|
|
1066
|
+
|
|
1067
|
+
```bash
|
|
1068
|
+
# Sanity: both rule files reachable via additionalDirectories
|
|
1069
|
+
test -f ../bassclef/.claude/rules/test-list-discipline.md \
|
|
1070
|
+
&& echo "test-list-discipline reachable" \
|
|
1071
|
+
|| echo "test-list-discipline NOT reachable"
|
|
1072
|
+
test -f ../bassclef/.claude/rules/test-sufficiency.md \
|
|
1073
|
+
&& echo "test-sufficiency reachable" \
|
|
1074
|
+
|| echo "test-sufficiency NOT reachable"
|
|
1075
|
+
|
|
1076
|
+
# Sanity: settings.json carries the additionalDirectories entry
|
|
1077
|
+
python3 -c "import json; d=json.load(open('.claude/settings.json')); \
|
|
1078
|
+
dirs=d.get('permissions',{}).get('additionalDirectories',[]); \
|
|
1079
|
+
print('bassclef loaded via additionalDirectories' if '../bassclef' in dirs else 'NOT loaded — check Phase 2.2 wiring')"
|
|
1080
|
+
```
|
|
1081
|
+
|
|
1082
|
+
**What this gives the adopter** — every adopter session that touches a test file gets the Beck test-list discipline (open with `# test-list:` block; mark `[x]` as each assertion ships; pending `[ ]` lines are honest in-flight signal) AND the 10-criterion sufficiency rubric (branch coverage / exit-code matrix / matcher coverage / override paths / stdin contract / external-binary handling / trap cleanup / stderr format / state-file interactions). Together they catch tests that pass but miss the intended assertion — the failure class eugene-supplements hit when 26 tests landed after source with zero asserted intent visible.
|
|
1083
|
+
|
|
1084
|
+
**Composition** — the rules pair with Phase 2.2.5 (`pre-commit-gate.sh` parent chain) + Phase 2.2.8 (`testing-tier-enforce.sh` Tier 0/1/2/3 mechanical enforcer). Methodology layer (rules) supplies the why; mechanical layer (hooks) fires at the moment of failure.
|
|
1085
|
+
|
|
1086
|
+
**No override needed** — rules are read-only context; they neither block writes nor produce stderr. Adopters can choose to ignore the rules per session, but the agent's own self-check during artifact-write keeps them honest.
|
|
1087
|
+
|
|
1088
|
+
Source: bassclef#1144 (this propagation step — T11, iteration-2 BLOCKER). Sister rules: `.claude/rules/test-list-discipline.md` + `.claude/rules/test-sufficiency.md`. Pairs with Phase 2.2.8 (the enforcer the rules guide).
|
|
1089
|
+
|
|
1090
|
+
##### 2.2.10 Wire role-entry-gate PreToolUse hook (bassclef#1145 T13)
|
|
1091
|
+
|
|
1092
|
+
**Lite adopter note (bassclef#740 Task 1.2).** This hook ships at `tier: standard`. Skip this step if your tier is lite. Resolve tier via `lib/tier-check.sh`.
|
|
1093
|
+
|
|
1094
|
+
Symlink the bassclef PreToolUse hook that catches source-code edits when the matching agent role has not been entered for the current session. MVP scope: Builder role only (matches source files in common languages). Other roles (Architect, Reviewer, Designer) deferred to a follow-on per goal narrowing — the multi-role matching matrix deserves its own design pass.
|
|
1095
|
+
|
|
1096
|
+
The hook reads `$CWD/state/markers/role/<role>/<session_id>.marker`. The marker is written by the agent once the role's persona has been loaded (sequential mode: Claude reads `~/.claude/agents/builder.md`, writes the marker, proceeds with the role's discipline loaded; orchestrator-gated mode: Orchestrator routes the dispatch to the Builder agent which writes its own marker before proceeding).
|
|
1097
|
+
|
|
1098
|
+
Symlink the hook:
|
|
1099
|
+
|
|
1100
|
+
```bash
|
|
1101
|
+
mkdir -p .claude/hooks
|
|
1102
|
+
ln -sf "../../../bassclef/.claude/hooks/role-entry-gate.sh" \
|
|
1103
|
+
".claude/hooks/role-entry-gate.sh"
|
|
1104
|
+
```
|
|
1105
|
+
|
|
1106
|
+
Add to the PreToolUse Edit|Write|MultiEdit matcher block in `.claude/settings.json`. The matcher likely exists from earlier Phase 2.x steps (substrate-clarity-gate from 2.2.2, pattern-annotation-validate from 2.2.4, the parent gate chain from 2.2.5) — append `role-entry-gate` to its `hooks` array; do not duplicate the matcher entry.
|
|
1107
|
+
|
|
1108
|
+
```jsonc
|
|
1109
|
+
{
|
|
1110
|
+
"hooks": {
|
|
1111
|
+
"PreToolUse": [
|
|
1112
|
+
{
|
|
1113
|
+
"matcher": "Edit|Write|MultiEdit",
|
|
1114
|
+
"hooks": [
|
|
1115
|
+
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/role-entry-gate.sh" }
|
|
1116
|
+
]
|
|
1117
|
+
}
|
|
1118
|
+
]
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
```
|
|
1122
|
+
|
|
1123
|
+
**MVP scope** — the hook fires on source-code file extensions only (`.ts`, `.tsx`, `.js`, `.jsx`, `.py`, `.rb`, `.go`, `.rs`, `.java`, `.kt`, `.swift`, `.cs`, `.cpp`, `.c`, `.h`). Non-source paths pass through. Tightening per `tech_stack.lang` reading (so a Python adopter only fires on `.py`) is a follow-on; the safe default union catches the common cases.
|
|
1124
|
+
|
|
1125
|
+
**Marker writing** — the agent writes the marker once it enters the role. Marker shape:
|
|
1126
|
+
|
|
1127
|
+
```json
|
|
1128
|
+
{
|
|
1129
|
+
"role": "builder",
|
|
1130
|
+
"session_id": "<uuid from harness>",
|
|
1131
|
+
"entered_at": "<ISO 8601 UTC>"
|
|
1132
|
+
}
|
|
1133
|
+
```
|
|
1134
|
+
|
|
1135
|
+
A future `/role-entry` skill automates the marker write + persona load; until then, the agent writes the marker by hand after reading the persona file. The BLOCK message documents the exact one-liner.
|
|
1136
|
+
|
|
1137
|
+
**Composition rule** — when the Edit|Write|MultiEdit matcher block already exists, append `role-entry-gate` to its `hooks` array. Order within the array: place `role-entry-gate` AFTER substrate-clarity-gate + artifact-ingestion-gate + pattern-annotation-validate + pre-commit-gate + state-validate + multi-tenancy-check, so structural / format / state validation fires first; role-discipline check fires last. Role-entry is the methodology gate, not the structural gate — it should not run before the cheap structural checks pass.
|
|
1138
|
+
|
|
1139
|
+
**Override path** — `SKIP_ROLE_ENTRY=1 <command>` (logged via trace-helper). Use for one-shot scripts the operator runs without entering an agent role, OR when the adopter is on a workflow that does not map to bassclef's role taxonomy.
|
|
1140
|
+
|
|
1141
|
+
**Verify the wiring:**
|
|
1142
|
+
|
|
1143
|
+
```bash
|
|
1144
|
+
# Sanity: hook executable + reachable
|
|
1145
|
+
ls -l .claude/hooks/role-entry-gate.sh
|
|
1146
|
+
|
|
1147
|
+
# Sanity: agent personas reachable via additionalDirectories (the BLOCK message refers to them)
|
|
1148
|
+
test -f ~/.claude/agents/builder.md && echo "builder persona reachable" || echo "builder persona NOT reachable (user-level agents missing)"
|
|
1149
|
+
|
|
1150
|
+
# Sanity: settings.json has the hook wired under the Edit matcher
|
|
1151
|
+
python3 -c "import json; d=json.load(open('.claude/settings.json')); \
|
|
1152
|
+
hooks=[h.get('command','') for entry in d.get('hooks',{}).get('PreToolUse',[]) if 'Edit' in entry.get('matcher','') for h in entry.get('hooks',[])]; \
|
|
1153
|
+
print('role-entry-gate wired' if any('role-entry-gate' in c for c in hooks) else 'NOT wired')"
|
|
1154
|
+
```
|
|
1155
|
+
|
|
1156
|
+
**Not yet wired in bassclef** — this hook ships to the adopter via `/onboard-repo` first. Bassclef's own settings.json adds the hook in a follow-on once the marker convention has run in the wild for a few sessions and the multi-role matching follow-on lands.
|
|
1157
|
+
|
|
1158
|
+
Source: bassclef#1145 (this hook step — T13). Sister agent personas: `~/.claude/agents/builder.md` (MVP target) + `~/.claude/agents/architect.md` + `~/.claude/agents/reviewer.md` + `~/.claude/agents/designer.md` (follow-on coverage). Follow-on tickets: multi-role matching matrix + `/role-entry` skill + `state/markers/role/` schema authoring under `standards/state-spine.md`.
|
|
1159
|
+
|
|
1160
|
+
##### 2.2.11 Wire temperance-gate PreToolUse hook (bassclef#1147 T15)
|
|
1161
|
+
|
|
1162
|
+
Symlink the bassclef hook that fires the /temperance question at decision boundaries. MVP scope: one active trigger — "high-impact path edit". It catches edits to `.claude/`, `standards/`, `lib/state.sh`, and SKILL.md files when the session has no fresh temperance marker. Other triggers stay as stubs. Follow-on tickets will design them.
|
|
1163
|
+
|
|
1164
|
+
<!-- FKGL-EXEMPT: source ticket enumeration -->
|
|
1165
|
+
Sources: `2026-06-18b` WU-7 goal (explain-mode entry, chain-anchor entry, non-stacked-branch first edit, post-compaction recovery); ticket #1147 (N consecutive edits, substrate proposal without catalog browse, diagnose-repro marker absence).
|
|
1166
|
+
<!-- /FKGL-EXEMPT -->
|
|
1167
|
+
|
|
1168
|
+
The hook pairs with `pre-commit-gate.sh` from Phase 2.2.5. That hook handles the first edit on a branch that stands alone. This hook adds one more check. It catches edits to high-impact paths that the other hook misses.
|
|
1169
|
+
|
|
1170
|
+
Symlink the hook:
|
|
1171
|
+
|
|
1172
|
+
```bash
|
|
1173
|
+
mkdir -p .claude/hooks
|
|
1174
|
+
ln -sf "../../../bassclef/.claude/hooks/temperance-gate.sh" \
|
|
1175
|
+
".claude/hooks/temperance-gate.sh"
|
|
1176
|
+
```
|
|
1177
|
+
|
|
1178
|
+
Add to the PreToolUse Edit|Write|MultiEdit matcher block in `.claude/settings.json`. The matcher likely already exists; append `temperance-gate` to its `hooks` array. Order: place `temperance-gate` AFTER structural checks (substrate-clarity-gate, artifact-ingestion-gate, pattern-annotation-validate, pre-commit-gate, state-validate, multi-tenancy-check) and AFTER role-entry-gate. Methodology gate fires last; structural + role checks fire first.
|
|
1179
|
+
|
|
1180
|
+
```jsonc
|
|
1181
|
+
{
|
|
1182
|
+
"hooks": {
|
|
1183
|
+
"PreToolUse": [
|
|
1184
|
+
{
|
|
1185
|
+
"matcher": "Edit|Write|MultiEdit",
|
|
1186
|
+
"hooks": [
|
|
1187
|
+
{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/temperance-gate.sh" }
|
|
1188
|
+
]
|
|
1189
|
+
}
|
|
1190
|
+
]
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
```
|
|
1194
|
+
|
|
1195
|
+
**Marker convention** — the hook reads `$CWD/state/markers/temperance/<session_id>.marker` (session-scoped) AND falls back to any existing marker under `state/markers/temperance/` (loose branch-scoped check; tighter matching-branch check is a follow-on). The temperance marker is written by the agent (or by `/temperance` skill invocation) once the right-thing / right-way questions have been answered.
|
|
1196
|
+
|
|
1197
|
+
**Marker shape:**
|
|
1198
|
+
|
|
1199
|
+
```json
|
|
1200
|
+
{
|
|
1201
|
+
"session_id": "<uuid from harness>",
|
|
1202
|
+
"entered_at": "<ISO 8601 UTC>",
|
|
1203
|
+
"question_answered": "yes"
|
|
1204
|
+
}
|
|
1205
|
+
```
|
|
1206
|
+
|
|
1207
|
+
**Override path** — `SKIP_TEMPERANCE_GATE=1 <command>` (logged via trace-helper). Use sparingly; the trigger is the point of the discipline.
|
|
1208
|
+
|
|
1209
|
+
**Verify the wiring:**
|
|
1210
|
+
|
|
1211
|
+
```bash
|
|
1212
|
+
# Sanity: hook executable + reachable
|
|
1213
|
+
ls -l .claude/hooks/temperance-gate.sh
|
|
1214
|
+
|
|
1215
|
+
# Sanity: temperance skill reachable via additionalDirectories
|
|
1216
|
+
test -f ../bassclef/.claude/skills/temperance/SKILL.md && echo "temperance skill reachable" || echo "NOT reachable"
|
|
1217
|
+
|
|
1218
|
+
# Sanity: settings.json has the hook wired under the Edit matcher
|
|
1219
|
+
python3 -c "import json; d=json.load(open('.claude/settings.json')); \
|
|
1220
|
+
hooks=[h.get('command','') for entry in d.get('hooks',{}).get('PreToolUse',[]) if 'Edit' in entry.get('matcher','') for h in entry.get('hooks',[])]; \
|
|
1221
|
+
print('temperance-gate wired' if any('temperance-gate' in c for c in hooks) else 'NOT wired')"
|
|
1222
|
+
```
|
|
1223
|
+
|
|
1224
|
+
**Not yet wired in bassclef** — adopters first; bassclef's own settings.json adds the hook in a follow-on once the MVP trigger has run in the wild for a few sessions and the trigger-taxonomy follow-on lands.
|
|
1225
|
+
|
|
1226
|
+
Source: bassclef#1147 (this hook step — T15). Sister skill: `.claude/skills/temperance/SKILL.md` (the methodology this gate enforces mechanically). Follow-on tickets: explain-mode + chain-anchor + post-compaction + non-stacked first edit + N consecutive edits + substrate proposal + diagnose-repro triggers + `standards/temperance-discipline.md` documenting the full trigger taxonomy.
|
|
1227
|
+
|
|
1228
|
+
##### 2.3 Create the project config file
|
|
1229
|
+
|
|
1230
|
+
Copy from `presence/install/substrate-config.md`, then prompt operator
|
|
1231
|
+
for:
|
|
1232
|
+
|
|
1233
|
+
- `journal_doc_id` (optional — create Google Doc now or later)
|
|
1234
|
+
- `changelog_doc_id` (optional)
|
|
1235
|
+
- `deploy_targets` (local/staging/prod booleans)
|
|
1236
|
+
- `budget.session_ceiling` / `iteration_ceiling` (sequential mode → `$0`)
|
|
1237
|
+
- `hosting_platform` (REQUIRED for any consumer that ships a UI). Default
|
|
1238
|
+
to `amplify` per ADR-008 + app-consumer precedent. Set `none` for
|
|
1239
|
+
docs-only / no-UI repos. Enum:
|
|
1240
|
+
`amplify | ec2-tailscale | vercel | netlify | cloudflare-pages | none`.
|
|
1241
|
+
`/bind-subdomain` + `/launch-preview` refuse on `none` unless
|
|
1242
|
+
`BIND_SUBDOMAIN_OVERRIDE=1` is set. Closes bassclef#449.
|
|
1243
|
+
|
|
1244
|
+
If operator says "defer", scaffold with placeholders and `TODO:` markers
|
|
1245
|
+
so next session surfaces them.
|
|
1246
|
+
|
|
1247
|
+
##### 2.3.5 Write the bassclef-configs adopter-default seed (bassclef#1135 WU-4)
|
|
1248
|
+
|
|
1249
|
+
Write `.claude/bassclef-configs.jsonc` with the adopter-default seed.
|
|
1250
|
+
Every downstream hook + skill reads from this file; without it, defaults
|
|
1251
|
+
are invisible to the adopter. Phase 2.5 (security_scanning) appends to
|
|
1252
|
+
this file later in the same Procedure run, so the foundation MUST land
|
|
1253
|
+
first.
|
|
1254
|
+
|
|
1255
|
+
```bash
|
|
1256
|
+
cat > .claude/bassclef-configs.jsonc <<'JSONC'
|
|
1257
|
+
// bassclef-configs.jsonc — unified adopter configuration discovery surface
|
|
1258
|
+
//
|
|
1259
|
+
// Edit the values below. Each block has a comment explaining what it does,
|
|
1260
|
+
// what the default is, and which bassclef substrate component reads it.
|
|
1261
|
+
//
|
|
1262
|
+
// JSON Schema: standards/state-spine/schemas/bassclef-configs.schema.json
|
|
1263
|
+
// Full reference: standards/bassclef-configs-schema.md
|
|
1264
|
+
{
|
|
1265
|
+
// Whether bassclef's substrate syncs from upstream at SessionStart.
|
|
1266
|
+
// Adopter default: false (opt-in trust posture). Turn on when you want
|
|
1267
|
+
// bassclef to keep itself current automatically.
|
|
1268
|
+
// Pairs with: .claude/hooks/bassclef-sync.sh
|
|
1269
|
+
"sync": { "enabled": false },
|
|
1270
|
+
|
|
1271
|
+
// Per-agent LLM model routing. Most agents stay cheap-fast; Builder
|
|
1272
|
+
// gets balanced for code quality; Shaper gets capable for problem framing.
|
|
1273
|
+
// Pairs with: .claude/agents/<role>.md frontmatter
|
|
1274
|
+
"model_tier": {
|
|
1275
|
+
"Builder": "balanced",
|
|
1276
|
+
"Closer": "cheap-fast",
|
|
1277
|
+
"Deployer": "cheap-fast",
|
|
1278
|
+
"DocsWriter": "cheap-fast",
|
|
1279
|
+
"Explorer": "cheap-fast",
|
|
1280
|
+
"Orchestrator": "cheap-fast",
|
|
1281
|
+
"Reviewer": "cheap-fast",
|
|
1282
|
+
"Shaper": "capable"
|
|
1283
|
+
},
|
|
1284
|
+
|
|
1285
|
+
// Adopter plan tier. Caps parallel agent dispatch per Anthropic plan limits.
|
|
1286
|
+
// tier-1 = single-agent serial (safest); tier-2 = 2-3 parallel after upgrade;
|
|
1287
|
+
// tier-4 = 5+ parallel for operator-scale workflows.
|
|
1288
|
+
// Pairs with: .claude/skills/longrun/SKILL.md parallel-dispatch logic
|
|
1289
|
+
"plan_tier": "tier-1",
|
|
1290
|
+
|
|
1291
|
+
// Plain-English discipline for agent reply prose to operator. When true,
|
|
1292
|
+
// the Stop hook scans the agent's last message for bassclef-internal
|
|
1293
|
+
// jargon and surfaces findings to stderr (advisory). 'strict' makes the
|
|
1294
|
+
// hook block the stop until prose is clean. false disables the scan.
|
|
1295
|
+
//
|
|
1296
|
+
// Optional: kiss_words_strict_places escalates specific places to strict
|
|
1297
|
+
// even when the global toggle stays advisory. V1 places (bassclef#1171):
|
|
1298
|
+
// - closeout_blocks — matches a "## Closeout audit" heading
|
|
1299
|
+
// - summary_tables — matches a "## Status" heading
|
|
1300
|
+
// - next_pickup — matches a "## Next pickup" heading
|
|
1301
|
+
// Adopter default: advisory everywhere; no strict places. Flip to strict
|
|
1302
|
+
// or add places when you want the rule to actually catch you.
|
|
1303
|
+
//
|
|
1304
|
+
// Pairs with: .claude/rules/plain-english-discipline.md
|
|
1305
|
+
// .claude/hooks/turn-prose-kiss-check.sh
|
|
1306
|
+
"prose_discipline": {
|
|
1307
|
+
"kiss_words_turn_prose": true
|
|
1308
|
+
// "kiss_words_strict_places": ["closeout_blocks"] // uncomment when ready
|
|
1309
|
+
},
|
|
1310
|
+
|
|
1311
|
+
// Per-path testing-tier enforcement (4 tiers: 0 strict TDD / 1 test-with /
|
|
1312
|
+
// 2 smoke / 3 manual verify). global_floor applies when no path_matcher matches.
|
|
1313
|
+
// Adopter default: floor=1 (WARN on missing test); empty matchers (add your own).
|
|
1314
|
+
// Pairs with: .claude/rules/testing-tier-config.md
|
|
1315
|
+
"testing": {
|
|
1316
|
+
"global_floor": 1,
|
|
1317
|
+
"path_matchers": []
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
// Phase 2.5 below appends "security_scanning" here.
|
|
1321
|
+
// /onboard-repo Phase 2.x or future tickets append "tech_stack", "hook_chain",
|
|
1322
|
+
// "discipline_layers" as those vocabularies stabilize. Per bassclef#1135.
|
|
1323
|
+
}
|
|
1324
|
+
JSONC
|
|
1325
|
+
```
|
|
1326
|
+
|
|
1327
|
+
Validate the written file against the schema:
|
|
1328
|
+
|
|
1329
|
+
```bash
|
|
1330
|
+
perl -pe 's,^\s*//.*$,,; s,/\*.*?\*/,,g' .claude/bassclef-configs.jsonc \
|
|
1331
|
+
| ajv validate -s ~/src/sunj-labs/bassclef/standards/state-spine/schemas/bassclef-configs.schema.json \
|
|
1332
|
+
-d - --spec=draft2020 -c ajv-formats
|
|
1333
|
+
```
|
|
1334
|
+
|
|
1335
|
+
Expect a single `valid` line. The file is now the single source of truth for adopter configuration; subsequent Phase 2.x steps APPEND to it (Phase 2.3.6 tech_stack, Phase 2.5 security_scanning, future T-tickets for hook_chain etc.) rather than rewriting from empty.
|
|
1336
|
+
|
|
1337
|
+
If the adopter has a pre-existing `.claude/bassclef-configs.jsonc` from a prior SENTINEL_bassclef_version, prompt before overwriting; offer a `--merge` flow (operator-driven; not automated in V1).
|
|
1338
|
+
|
|
1339
|
+
##### 2.3.6 Discover and write the tech_stack block (bassclef#1127 WU-2)
|
|
1340
|
+
|
|
1341
|
+
Stack-bound rules + standards in bassclef (`api-conventions.md`, `sdlc-gates.md`, `schema-management.md`, `new-dependency-check.md`, `frontend-stack.md`, `deployment-topology.md`) read `tech_stack:` from `.claude/bassclef-configs.jsonc` to route per-stack discipline. Without the block, non-matching adopters see false-fires (Prisma rules at SQLAlchemy adopters, Next.js API conventions at FastAPI adopters, etc.). This step writes the block so the bleed stops at install time.
|
|
1342
|
+
|
|
1343
|
+
Schema reference: `standards/state-spine/schemas/tech-stack.schema.json`. Standard: `standards/tech-stack-config.md`.
|
|
1344
|
+
|
|
1345
|
+
**Auto-detection signals** (per `standards/tech-stack-config.md` § Auto-detection at `/onboard-repo`):
|
|
1346
|
+
|
|
1347
|
+
| Build file present | Suggests |
|
|
1348
|
+
|---|---|
|
|
1349
|
+
| `package.json` with `"next"` dep | `frontend: nextjs`, `lang: typescript` (if `tsconfig.json` also present) |
|
|
1350
|
+
| `package.json` with `"react"` dep (no `next`) | `frontend: react-vite` (if `vite.config.*` present) |
|
|
1351
|
+
| `package.json` with `"prisma"` dep | `orm: prisma` |
|
|
1352
|
+
| `package.json` with `"drizzle-orm"` dep | `orm: drizzle` |
|
|
1353
|
+
| `package.json` + `pnpm-lock.yaml` | `pkg_manager: pnpm` |
|
|
1354
|
+
| `package.json` + `yarn.lock` | `pkg_manager: yarn` |
|
|
1355
|
+
| `package.json` + `package-lock.json` | `pkg_manager: npm` |
|
|
1356
|
+
| `pyproject.toml` or `requirements.txt` | `lang: python` |
|
|
1357
|
+
| `pyproject.toml` with `sqlalchemy` | `orm: sqlalchemy` |
|
|
1358
|
+
| `pyproject.toml` with `alembic` | `orm: alembic` |
|
|
1359
|
+
| `pyproject.toml` with `fastapi` | `frontend: fastapi`, `backend: python` |
|
|
1360
|
+
| `pyproject.toml` with `django` | `frontend: django`, `backend: python` |
|
|
1361
|
+
| `Gemfile` | `lang: ruby`, `pkg_manager: gem` |
|
|
1362
|
+
| `Gemfile` with `rails` | `frontend: rails`, `backend: ruby` |
|
|
1363
|
+
| `go.mod` | `lang: go`, `pkg_manager: go-mod`, `backend: go` |
|
|
1364
|
+
| `Cargo.toml` | `lang: rust`, `pkg_manager: cargo`, `backend: rust` |
|
|
1365
|
+
| `vercel.json` or `.vercelrc` | `deploy: vercel` |
|
|
1366
|
+
| `fly.toml` | `deploy: fly` |
|
|
1367
|
+
| `netlify.toml` | `deploy: netlify` |
|
|
1368
|
+
| `render.yaml` | `deploy: render` |
|
|
1369
|
+
|
|
1370
|
+
No signal → field stays `none` and waits for operator override.
|
|
1371
|
+
|
|
1372
|
+
**Operator prompt** (surface this exact text):
|
|
1373
|
+
|
|
1374
|
+
```
|
|
1375
|
+
Detected stack signals:
|
|
1376
|
+
|
|
1377
|
+
lang: <detected or "(no signal)">
|
|
1378
|
+
frontend: <detected or "(no signal)">
|
|
1379
|
+
backend: <detected or "(no signal)">
|
|
1380
|
+
orm: <detected or "(no signal)">
|
|
1381
|
+
pkg_manager: <detected or "(no signal)">
|
|
1382
|
+
deploy: <detected or "(no signal)">
|
|
1383
|
+
|
|
1384
|
+
Stack-bound rules route per-adopter from this block. Without it,
|
|
1385
|
+
non-matching adopters see false-fires from rules written for the
|
|
1386
|
+
default stack (TypeScript + Next.js + Prisma + EC2-Tailscale).
|
|
1387
|
+
|
|
1388
|
+
Three paths:
|
|
1389
|
+
|
|
1390
|
+
[a] Accept detected values
|
|
1391
|
+
[b] Override per field (5-7 short prompts)
|
|
1392
|
+
[c] Skip — write declared_via: deferred and revisit later
|
|
1393
|
+
(rules fall back to operator defaults; expect some false-fires
|
|
1394
|
+
until you set the block)
|
|
1395
|
+
|
|
1396
|
+
Pick a/b/c:
|
|
1397
|
+
```
|
|
1398
|
+
|
|
1399
|
+
**On accept (a):** write the block with detected values + `declared_via: onboard-repo` + `declared_at: <ISO date>`.
|
|
1400
|
+
|
|
1401
|
+
**On per-field override (b):** prompt each field in order with detected value as the default. Field-level enums per `standards/tech-stack-config.md` § Fields.
|
|
1402
|
+
|
|
1403
|
+
**On skip (c):** write `tech_stack: { declared_via: "deferred", declared_at: <ISO date> }` only. The schema admits all fields as optional; rules fall back gracefully. Operator can revisit by re-running `/onboard-repo` or editing the block directly.
|
|
1404
|
+
|
|
1405
|
+
**Append to `.claude/bassclef-configs.jsonc`** using `jq` or in-place edit; the block goes immediately before the closing brace + after `testing`:
|
|
1406
|
+
|
|
1407
|
+
```bash
|
|
1408
|
+
# Pseudo-procedure; actual implementation reads the operator's path choice
|
|
1409
|
+
# and constructs the block from detected + override values.
|
|
1410
|
+
cat >> .claude/bassclef-configs.jsonc <<TECH_STACK
|
|
1411
|
+
,
|
|
1412
|
+
|
|
1413
|
+
// Tech stack vocabulary. Stack-bound rules read this to route per-
|
|
1414
|
+
// stack discipline (schema-management.md ORM routing, api-conventions
|
|
1415
|
+
// frontend routing, sdlc-gates lang routing, etc.).
|
|
1416
|
+
// Auto-detected at /onboard-repo Phase 2.3.6; operator confirms or
|
|
1417
|
+
// overrides per field.
|
|
1418
|
+
// Pairs with: standards/tech-stack-config.md (schema + semantics),
|
|
1419
|
+
// standards/state-spine/schemas/tech-stack.schema.json
|
|
1420
|
+
// Schema reference: bassclef#1127 tech_stack discovery
|
|
1421
|
+
"tech_stack": {
|
|
1422
|
+
"lang": "<value>",
|
|
1423
|
+
"frontend": "<value>",
|
|
1424
|
+
"backend": "<value>",
|
|
1425
|
+
"orm": "<value>",
|
|
1426
|
+
"pkg_manager": "<value>",
|
|
1427
|
+
"deploy": "<value>",
|
|
1428
|
+
"declared_at": "<ISO date>",
|
|
1429
|
+
"declared_via": "onboard-repo"
|
|
1430
|
+
}
|
|
1431
|
+
TECH_STACK
|
|
1432
|
+
```
|
|
1433
|
+
|
|
1434
|
+
(In practice the writer holds the file open as JSON-with-comments, mutates the parsed object, re-emits with comments preserved per the comment-aware JSONC editor pattern; the heredoc above is illustrative.)
|
|
1435
|
+
|
|
1436
|
+
**Validate after append:**
|
|
1437
|
+
|
|
1438
|
+
```bash
|
|
1439
|
+
perl -pe 's,^\s*//.*$,,; s,/\*.*?\*/,,g' .claude/bassclef-configs.jsonc \
|
|
1440
|
+
| ajv validate -s ~/src/sunj-labs/bassclef/standards/state-spine/schemas/bassclef-configs.schema.json \
|
|
1441
|
+
-d - --spec=draft2020 -c ajv-formats
|
|
1442
|
+
```
|
|
1443
|
+
|
|
1444
|
+
Expect `valid`. If the file fails to validate, revert the append, surface the schema error, and re-prompt.
|
|
1445
|
+
|
|
1446
|
+
**Acceptance:**
|
|
1447
|
+
|
|
1448
|
+
- After Phase 2.3.6 completes, `.claude/bassclef-configs.jsonc` contains a `tech_stack:` block.
|
|
1449
|
+
- Block validates against `tech-stack.schema.json`.
|
|
1450
|
+
- `declared_via` is one of `onboard-repo` (path a or b) or `deferred` (path c); `migrated` and `demo-prompt` reserved for other callers.
|
|
1451
|
+
- Operator can re-run `/onboard-repo` and the existing block is detected; prompt asks whether to keep, re-detect, or edit per-field.
|
|
1452
|
+
|
|
1453
|
+
**Composes with downstream Phase 2.x steps:**
|
|
1454
|
+
|
|
1455
|
+
- Phase 2.3.7 (testing-tier path_matchers seed) reads `tech_stack.lang` to pick the per-stack default tier matchers.
|
|
1456
|
+
- Phase 2.5 (security scanning) reads `tech_stack.lang` to seed the scanner adapter.
|
|
1457
|
+
- Future Phase 2.x for `hook_chain` reads stack signals to pick per-stack hook defaults.
|
|
1458
|
+
|
|
1459
|
+
##### 2.3.7 Seed testing.path_matchers per declared tech_stack (bassclef#1143 T10)
|
|
1460
|
+
|
|
1461
|
+
Phase 2.3.5 wrote the `testing` block with `global_floor: 1` + empty `path_matchers`. Phase 2.3.6 just discovered + wrote `tech_stack`. This step reads `tech_stack.lang` and seeds `testing.path_matchers` with sensible per-stack defaults so the Tier 0 / Tier 1 enforcer (Phase 2.2.8) fires on the right paths from day one.
|
|
1462
|
+
|
|
1463
|
+
**Per-stack default seeds** (operator-eyes design choices):
|
|
1464
|
+
|
|
1465
|
+
For `tech_stack.lang: typescript` + `tech_stack.frontend: nextjs` (bassclef default):
|
|
1466
|
+
|
|
1467
|
+
```jsonc
|
|
1468
|
+
"testing": {
|
|
1469
|
+
"global_floor": 1,
|
|
1470
|
+
"path_matchers": [
|
|
1471
|
+
{ "match": "lib/domain/**/*.ts", "tier": 0 },
|
|
1472
|
+
{ "match": "lib/infra/**/*.ts", "tier": 0 },
|
|
1473
|
+
{ "match": "app/api/**/*.ts", "tier": 1 },
|
|
1474
|
+
{ "match": "app/actions/**/*.ts", "tier": 1 },
|
|
1475
|
+
{ "match": "app/_components/**/*.tsx", "tier": 2 },
|
|
1476
|
+
{ "match": "app/**/*.tsx", "tier": 2 },
|
|
1477
|
+
{ "match": "docs/**/*.md", "tier": 3 }
|
|
1478
|
+
]
|
|
1479
|
+
}
|
|
1480
|
+
```
|
|
1481
|
+
|
|
1482
|
+
For `tech_stack.lang: python` + `tech_stack.frontend: fastapi`:
|
|
1483
|
+
|
|
1484
|
+
```jsonc
|
|
1485
|
+
"testing": {
|
|
1486
|
+
"global_floor": 1,
|
|
1487
|
+
"path_matchers": [
|
|
1488
|
+
{ "match": "domain/**/*.py", "tier": 0 },
|
|
1489
|
+
{ "match": "infrastructure/**/*.py", "tier": 0 },
|
|
1490
|
+
{ "match": "api/**/*.py", "tier": 1 },
|
|
1491
|
+
{ "match": "templates/**/*", "tier": 2 },
|
|
1492
|
+
{ "match": "docs/**/*.md", "tier": 3 }
|
|
1493
|
+
]
|
|
1494
|
+
}
|
|
1495
|
+
```
|
|
1496
|
+
|
|
1497
|
+
Other stacks (ruby-rails, go-gin, java-spring) ship default seeds as per-stack siblings under `standards/testing-tier-seeds/<stack>.md` — deferred to a follow-on ticket when an adopter on that stack files for them. The pattern is the same: Tier 0 on the essential pure-domain layer; Tier 1 on the API + actions layer; Tier 2 on UI + template surfaces; Tier 3 on docs.
|
|
1498
|
+
|
|
1499
|
+
**On `tech_stack.lang: none` or `deferred`** — skip the seed; leave `path_matchers: []` empty. The `global_floor: 1` keeps the WARN posture; adopter can hand-edit later.
|
|
1500
|
+
|
|
1501
|
+
**Operator prompt** (surface this exact text after Phase 2.3.6 completes):
|
|
1502
|
+
|
|
1503
|
+
```
|
|
1504
|
+
Tech stack: <detected lang + frontend>
|
|
1505
|
+
|
|
1506
|
+
Seed testing.path_matchers with the per-stack default for <stack>?
|
|
1507
|
+
|
|
1508
|
+
[a] Yes, accept default seed (recommended)
|
|
1509
|
+
[b] Show me the seed first
|
|
1510
|
+
[c] Skip — keep path_matchers empty (manual seed later)
|
|
1511
|
+
|
|
1512
|
+
Pick a/b/c:
|
|
1513
|
+
```
|
|
1514
|
+
|
|
1515
|
+
**On accept (a)** — write the matching per-stack seed into `testing.path_matchers` in `bassclef-configs.jsonc`. Use the comment-aware JSONC editor pattern so the existing comments + `global_floor` survive.
|
|
1516
|
+
|
|
1517
|
+
**On show first (b)** — print the seed inline; ask for accept/edit/skip.
|
|
1518
|
+
|
|
1519
|
+
**On skip (c)** — leave empty; print "Adopter chose to skip — `testing.path_matchers` stays empty. Set with `/onboard-repo` re-run or hand-edit later."
|
|
1520
|
+
|
|
1521
|
+
**Validate after write:**
|
|
1522
|
+
|
|
1523
|
+
```bash
|
|
1524
|
+
perl -pe 's,^\s*//.*$,,; s,/\*.*?\*/,,g' .claude/bassclef-configs.jsonc \
|
|
1525
|
+
| ajv validate -s ~/src/sunj-labs/bassclef/standards/state-spine/schemas/bassclef-configs.schema.json \
|
|
1526
|
+
-d - --spec=draft2020 -c ajv-formats
|
|
1527
|
+
```
|
|
1528
|
+
|
|
1529
|
+
Expect a single `valid` line. The `testing.path_matchers` items must validate against the bassclef-configs schema.
|
|
1530
|
+
|
|
1531
|
+
**Stack siblings deferred** — only typescript-nextjs + python-fastapi ship in this step. Adopters on ruby-rails / go-gin / java-spring / rust-axum etc. get `global_floor: 1` + empty matchers (the safe default WARN posture) and can hand-seed per their layout. A follow-on ticket adds per-stack seed siblings under `standards/testing-tier-seeds/<stack>.md` as adopters request them.
|
|
1532
|
+
|
|
1533
|
+
Source: bassclef#1143 (this seed step — T10, iteration-2 BLOCKER). Sister rule: `.claude/rules/testing-tier-config.md`. Pairs with Phase 2.2.8 (the enforcer that reads this seed).
|
|
1534
|
+
|
|
1535
|
+
##### 2.3.8 Surface the wiki_sync toggle (bassclef#1265 — Phase 1 stub)
|
|
1536
|
+
|
|
1537
|
+
The `wiki_sync` block in `.claude/bassclef-configs.jsonc` controls whether the adopter's repo gets the bassclef-docs-sync workflow vendored from `presence/install/bassclef-docs-sync.template.yml`. This step surfaces the toggle so adopters see the option exists.
|
|
1538
|
+
|
|
1539
|
+
**Phase 1 (this step — bet 2026-06-20b) — minimal stub:**
|
|
1540
|
+
|
|
1541
|
+
Read the adopter's `wiki_sync.enabled` value from the bassclef-configs file just written. Print one of the following:
|
|
1542
|
+
|
|
1543
|
+
- **`enabled: false`** (default — universal): print
|
|
1544
|
+
> Wiki-sync is OFF. The wiki-as-we-go discipline rule still loads at session-start, but no workflow fires. To enable later: edit `.claude/bassclef-configs.jsonc` `wiki_sync.enabled` to `true` and re-run `/onboard-repo` for the vendoring step (full vendoring lands in Phase 2 of bet 2026-06-20b — see `docs/iteration-bets/2026-06-20b-cold-adopter-mechanism-inheritance.md`).
|
|
1545
|
+
- **`enabled: true`**: print
|
|
1546
|
+
> Wiki-sync is ON. Full vendoring of `presence/install/bassclef-docs-sync.template.yml` → `.github/workflows/bassclef-docs-sync.yml` + `presence/install/docs-sync-allowlist.template.md` → `standards/docs-sync-allowlist.md` is **deferred to Phase 2 of bet 2026-06-20b**. For now, copy both templates manually if you need the wiki to update from main pushes today.
|
|
1547
|
+
|
|
1548
|
+
**Phase 2 (next session) — full vendoring (deferred):**
|
|
1549
|
+
|
|
1550
|
+
- Auto-copy both templates into the adopter's repo
|
|
1551
|
+
- Open a follow-up PR for GHA secrets if `target_wiki_repo` requires non-default auth
|
|
1552
|
+
- Walk the adopter through the three workflow-edit surfaces (the `on.push.paths` glob, the Filter step's case-glob, the Sync step's source-path → wiki-page mapping)
|
|
1553
|
+
- Smoke-test by pushing a docs change and verifying the wiki updates
|
|
1554
|
+
|
|
1555
|
+
The Phase 1 stub keeps this step minimal so the goal stays bounded. Adopters who want wiki-sync today can copy the templates by hand from bassclef's `presence/install/` (workflows + cold-adopter assets).
|
|
1556
|
+
|
|
1557
|
+
**Composes with:**
|
|
1558
|
+
|
|
1559
|
+
- `.claude/skills/docs-sync/SKILL.md` § Adopter mode (read at runtime; surfaces BLOCK if templates not vendored)
|
|
1560
|
+
- `.claude/rules/session-artifacts.md` § Wiki-as-we-go discipline (methodology rule that triggers the closeout question)
|
|
1561
|
+
|
|
1562
|
+
Source: bassclef#1265 (this surfacing step — wiki-sync mechanism adopter inheritance). Sister bet: `docs/iteration-bets/2026-06-20b-cold-adopter-mechanism-inheritance.md` WU-2.
|
|
1563
|
+
|
|
1564
|
+
##### 2.3.9 Write the onboard-repo state marker (bassclef-upstream#664)
|
|
1565
|
+
|
|
1566
|
+
Write `.claude/state/onboard-repo.state.json` after Phase 2.3 has
|
|
1567
|
+
populated the config file(s). The marker captures which mode was used,
|
|
1568
|
+
the ISO timestamp, and which files were written. The configs UI reads
|
|
1569
|
+
this marker to render the first-run overlay (per canvas 2026-07-08
|
|
1570
|
+
Appendix C). Future audits trace how a repo got wired.
|
|
1571
|
+
|
|
1572
|
+
Re-runs append a history entry — the marker keeps prior runs, not
|
|
1573
|
+
clobber them.
|
|
1574
|
+
|
|
1575
|
+
Invoke the helper script that ships alongside this SKILL:
|
|
1576
|
+
|
|
1577
|
+
```bash
|
|
1578
|
+
# ONBOARD_MODE is set earlier in Procedure —
|
|
1579
|
+
# default / --with-deploy-host / --with-secrets / --full / --greenfield-from-intent
|
|
1580
|
+
bash .claude/skills/onboard-repo/write-state-marker.sh "${ONBOARD_MODE:-default}"
|
|
1581
|
+
```
|
|
1582
|
+
|
|
1583
|
+
The script writes / updates `.claude/state/onboard-repo.state.json`
|
|
1584
|
+
with shape `{mode, timestamp_iso, paths_written[], history[], skill_version}`.
|
|
1585
|
+
Tier 0 tests at `.claude/skills/onboard-repo/tests/write-state-marker.test.sh`
|
|
1586
|
+
pin all behavior including re-run history append.
|
|
1587
|
+
|
|
1588
|
+
**Composes with:**
|
|
1589
|
+
|
|
1590
|
+
- Configs UI first-run overlay (`bassclef/configs/`, bet 2026-07-08a WU-7)
|
|
1591
|
+
- Future audits reading `.claude/state/onboard-repo.state.json`
|
|
1592
|
+
|
|
1593
|
+
Source: bassclef-upstream#664 (state marker for onboard-repo). Part of goal
|
|
1594
|
+
`docs/iteration-bets/2026-07-08a-configs-ui-iteration-1.md` WU-4.
|
|
1595
|
+
|
|
1596
|
+
##### 2.3.10 Wire the cron installer for auto-save (bassclef-upstream#941)
|
|
1597
|
+
|
|
1598
|
+
Goal 27b (PR #937) shipped `presence/install/schedule-auto-save.cron.sh` at
|
|
1599
|
+
`tier: lite`. The installer writes a cron entry that fires
|
|
1600
|
+
`save-state.sh --auto --quiet` on idle. Sam's magic demo per canvas
|
|
1601
|
+
`docs/canvases/2026-07-19-bassclef-lite.md` L67-71 promises session-state
|
|
1602
|
+
continuity as part of onboarding. This step wires the installer in.
|
|
1603
|
+
|
|
1604
|
+
Behavior per OS:
|
|
1605
|
+
|
|
1606
|
+
- **macOS + Linux + WSL** — run the installer directly. It appends one
|
|
1607
|
+
crontab entry and is idempotent (re-running is safe per PR #937 Test 4).
|
|
1608
|
+
- **Native Windows (no WSL)** — cron is not available. Point the adopter at
|
|
1609
|
+
`presence/install/schedule-auto-save.taskscheduler.md` for the manual
|
|
1610
|
+
Task Scheduler setup.
|
|
1611
|
+
|
|
1612
|
+
```bash
|
|
1613
|
+
OS_KIND="$(uname -s)"
|
|
1614
|
+
case "$OS_KIND" in
|
|
1615
|
+
Darwin|Linux)
|
|
1616
|
+
if [ -x "$BASSCLEF_DIR/presence/install/schedule-auto-save.cron.sh" ]; then
|
|
1617
|
+
bash "$BASSCLEF_DIR/presence/install/schedule-auto-save.cron.sh" \
|
|
1618
|
+
|| echo "WARNING: cron installer failed; run manually to enable auto-save" >&2
|
|
1619
|
+
else
|
|
1620
|
+
echo "WARNING: schedule-auto-save.cron.sh not found; skip auto-save wire" >&2
|
|
1621
|
+
fi
|
|
1622
|
+
;;
|
|
1623
|
+
MINGW*|CYGWIN*|MSYS*)
|
|
1624
|
+
echo "Native Windows detected. Cron is not available."
|
|
1625
|
+
echo "See presence/install/schedule-auto-save.taskscheduler.md for the"
|
|
1626
|
+
echo "manual Task Scheduler setup that wires auto-save on Windows."
|
|
1627
|
+
;;
|
|
1628
|
+
*)
|
|
1629
|
+
echo "Unknown OS '$OS_KIND'; skip auto-save wire"
|
|
1630
|
+
;;
|
|
1631
|
+
esac
|
|
1632
|
+
```
|
|
1633
|
+
|
|
1634
|
+
The installer prints its own progress. Failures do not stop onboarding —
|
|
1635
|
+
auto-save is a nice-to-have on first install, and adopters can re-run
|
|
1636
|
+
the installer any time via `bash $BASSCLEF_DIR/presence/install/schedule-auto-save.cron.sh`.
|
|
1637
|
+
|
|
1638
|
+
**Composes with:**
|
|
1639
|
+
|
|
1640
|
+
- `presence/install/schedule-auto-save.cron.sh` (PR #937 — the cron installer)
|
|
1641
|
+
- `presence/install/schedule-auto-save.taskscheduler.md` (PR #937 — Windows guide)
|
|
1642
|
+
- ADR-046 cross-OS scheduling (accepted; Option C picked)
|
|
1643
|
+
|
|
1644
|
+
Source: bassclef-upstream#941 (wire cron into onboard for Sam's magic demo).
|
|
1645
|
+
|
|
1646
|
+
##### 2.4 Point at bassclef's source (URL + auth)
|
|
1647
|
+
|
|
1648
|
+
The thin-pointer dispatcher installed in 2.1 reads `.bassclef-source.json`
|
|
1649
|
+
for the upstream tarball URL + auth metadata when the curl fallback
|
|
1650
|
+
fires (Cloud Env / mobile / fresh sandboxes / CI). Schema codified at
|
|
1651
|
+
`standards/bassclef-source-config.md`; validation hook at
|
|
1652
|
+
`.claude/hooks/bassclef-source-config-validate.sh`.
|
|
1653
|
+
|
|
1654
|
+
Before writing the file, prompt the adopter explicitly (per bassclef#939):
|
|
1655
|
+
|
|
1656
|
+
```
|
|
1657
|
+
Bassclef can auto-pull the latest skills, rules, and agents into this
|
|
1658
|
+
repo at every Claude Code session start. Keeps you current with bassclef's
|
|
1659
|
+
evolution; you never manually update.
|
|
1660
|
+
|
|
1661
|
+
Off by default. Turn on now? [y/N]
|
|
1662
|
+
```
|
|
1663
|
+
|
|
1664
|
+
Persist the adopter's answer to the `auto_sync` field. Default is `false` when in doubt.
|
|
1665
|
+
|
|
1666
|
+
```bash
|
|
1667
|
+
cat > .bassclef-source.json <<JSON
|
|
1668
|
+
{
|
|
1669
|
+
"source_url": "https://api.github.com/repos/sunj-labs/bassclef/tarball/main",
|
|
1670
|
+
"auth_method": "Bearer",
|
|
1671
|
+
"token_env": "BASSCLEF_TOKEN",
|
|
1672
|
+
"auto_sync": false,
|
|
1673
|
+
"comment": "Provider: GitHub repo tarball API. Auth: fine-grained PAT 'bassclef-substrate-read' (Contents:read on sunj-labs/bassclef), stored as BASSCLEF_TOKEN in the consumer's Cloud Code environment. Schema: standards/bassclef-source-config.md. auto_sync controls whether SessionStart fetches bassclef's latest substrate; off by default per bassclef#939."
|
|
1674
|
+
}
|
|
1675
|
+
JSON
|
|
1676
|
+
```
|
|
1677
|
+
|
|
1678
|
+
**Operator action — out of band, one-time per consumer:**
|
|
1679
|
+
|
|
1680
|
+
1. Create fine-grained GitHub PAT scoped to `sunj-labs/bassclef`
|
|
1681
|
+
- Permission: Contents: read
|
|
1682
|
+
- Expiry: 90 days
|
|
1683
|
+
- Name: `bassclef-read-{REPO_SHORT}-{YYYYMMDD}`
|
|
1684
|
+
2. Paste into the consumer's Cloud Code environment as `BASSCLEF_TOKEN`.
|
|
1685
|
+
3. Track the PAT lifecycle in operator's `substrate.secrets.md` per
|
|
1686
|
+
`standards/secrets-lifecycle.md`.
|
|
1687
|
+
|
|
1688
|
+
Provider alternatives (see `standards/bassclef-source-config.md` for
|
|
1689
|
+
GitHub public, GitHub private release, ADO Repos, GitLab examples).
|
|
1690
|
+
|
|
1691
|
+
After writing the file, run the hook once locally to validate via
|
|
1692
|
+
sibling fast-path (no PAT needed for desktop):
|
|
1693
|
+
|
|
1694
|
+
```bash
|
|
1695
|
+
echo "{\"cwd\":\"$PWD\"}" | bash .claude/hooks/bassclef-sync.sh
|
|
1696
|
+
```
|
|
1697
|
+
|
|
1698
|
+
Expect: `### BASSCLEF SUBSTRATE — SYNCED` banner; skills/rules/hooks/
|
|
1699
|
+
luminaries symlinked into `.claude/`; no errors surfaced.
|
|
1700
|
+
|
|
1701
|
+
##### 2.5 Configure security scanning (per bassclef goal 2026-06-17a Phase Y)
|
|
1702
|
+
|
|
1703
|
+
Auto-detect the adopter's stack from build files, confirm with the operator, and write the `security_scanning` block to `.claude/bassclef-configs.jsonc`. Adopter copies the matching reference workflow next.
|
|
1704
|
+
|
|
1705
|
+
Auto-detection signals (per `standards/security-scanner-adapter.md` § Stack auto-detection):
|
|
1706
|
+
|
|
1707
|
+
```bash
|
|
1708
|
+
# Detect stack by build-file signal.
|
|
1709
|
+
STACK_GUESS="custom"
|
|
1710
|
+
if [ -f "package.json" ]; then
|
|
1711
|
+
if grep -q '"typescript"\|"@types/' package.json; then
|
|
1712
|
+
STACK_GUESS="node-typescript"
|
|
1713
|
+
else
|
|
1714
|
+
STACK_GUESS="node-typescript" # TS opt-out can happen later
|
|
1715
|
+
fi
|
|
1716
|
+
elif [ -f "requirements.txt" ] || [ -f "pyproject.toml" ] || [ -f "setup.py" ]; then
|
|
1717
|
+
STACK_GUESS="python"
|
|
1718
|
+
elif [ -f "Gemfile" ] || ls *.gemspec >/dev/null 2>&1; then
|
|
1719
|
+
STACK_GUESS="ruby"
|
|
1720
|
+
elif [ -f "pom.xml" ] || [ -f "build.gradle" ] || ls **/*.java >/dev/null 2>&1; then
|
|
1721
|
+
STACK_GUESS="java"
|
|
1722
|
+
elif [ -f "go.mod" ] || ls **/*.go >/dev/null 2>&1; then
|
|
1723
|
+
STACK_GUESS="go"
|
|
1724
|
+
fi
|
|
1725
|
+
```
|
|
1726
|
+
|
|
1727
|
+
Prompt the operator (sample):
|
|
1728
|
+
|
|
1729
|
+
```
|
|
1730
|
+
Bassclef security scanner adapter detected stack: <STACK_GUESS>
|
|
1731
|
+
|
|
1732
|
+
Default scanners for this stack (per standards/security-scanner-adapter.md):
|
|
1733
|
+
- node-typescript: eslint-plugin-security + audit-ci + gitleaks
|
|
1734
|
+
- python: bandit + safety + gitleaks
|
|
1735
|
+
- ruby: brakeman + bundler-audit + gitleaks
|
|
1736
|
+
- java: spotbugs-security + dependency-check + gitleaks
|
|
1737
|
+
- go: gosec + govulncheck + gitleaks
|
|
1738
|
+
- custom: adopter-supplied list
|
|
1739
|
+
|
|
1740
|
+
Activate scanners with stack=<STACK_GUESS>? [Y/n/custom/skip]
|
|
1741
|
+
```
|
|
1742
|
+
|
|
1743
|
+
Persist the answer to the bassclef-configs block. Phase 2.3.5 (bassclef#1135 WU-4) wrote the seed file; this step adds the `security_scanning` block to it via a Python merge to keep the JSONC valid.
|
|
1744
|
+
|
|
1745
|
+
```bash
|
|
1746
|
+
# Add security_scanning block to .claude/bassclef-configs.jsonc (bassclef#1135).
|
|
1747
|
+
# Phase 2.3.5 already wrote the seed; we add a block via JSONC-aware Python merge.
|
|
1748
|
+
python3 - "$STACK_GUESS" <<'PY'
|
|
1749
|
+
import json, re, sys, pathlib
|
|
1750
|
+
path = pathlib.Path('.claude/bassclef-configs.jsonc')
|
|
1751
|
+
raw = path.read_text()
|
|
1752
|
+
# Strip line + block comments before parsing
|
|
1753
|
+
stripped = re.sub(r'/\*.*?\*/', '', re.sub(r'//.*$', '', raw, flags=re.M), flags=re.S)
|
|
1754
|
+
data = json.loads(stripped)
|
|
1755
|
+
stack = sys.argv[1]
|
|
1756
|
+
data['security_scanning'] = {
|
|
1757
|
+
'stack': None if stack in ('null', 'skip', '') else stack,
|
|
1758
|
+
'additional_scanners': [],
|
|
1759
|
+
'block_on_high_severity': True,
|
|
1760
|
+
}
|
|
1761
|
+
path.write_text(json.dumps(data, indent=2) + '\n')
|
|
1762
|
+
PY
|
|
1763
|
+
```
|
|
1764
|
+
|
|
1765
|
+
Note: the Python merge re-serializes the file without comments. Operators
|
|
1766
|
+
keep the schema-doc reference (`standards/bassclef-configs-schema.md`)
|
|
1767
|
+
as the bassclef comment-bearing source; the on-disk file is the values.
|
|
1768
|
+
A follow-on (bassclef#1135 V2) may add a comment-preserving JSONC writer.
|
|
1769
|
+
|
|
1770
|
+
INSTEAD of asking the operator to know the scanner names: the standard supplies the per-stack defaults; the operator confirms the stack choice only.
|
|
1771
|
+
|
|
1772
|
+
If the operator answers `skip`, write `"stack": null` — scanners stay off until the next session re-runs the question.
|
|
1773
|
+
|
|
1774
|
+
**Operator follow-up — copy the reference workflow:**
|
|
1775
|
+
|
|
1776
|
+
After the bassclef-configs block is written, point the operator at the matching reference workflow:
|
|
1777
|
+
|
|
1778
|
+
```
|
|
1779
|
+
Next step: copy the per-stack reference workflow into your repo's CI.
|
|
1780
|
+
|
|
1781
|
+
cp ~/src/sunj-labs/bassclef/config/security-scanners/<STACK_GUESS>.yml \
|
|
1782
|
+
.github/workflows/security-scan.yml
|
|
1783
|
+
|
|
1784
|
+
The workflow auto-skips when the bassclef-configs stack value is null,
|
|
1785
|
+
so copying is harmless if you're not ready to activate yet.
|
|
1786
|
+
```
|
|
1787
|
+
|
|
1788
|
+
For stacks beyond node-typescript + python (which ship reference workflows), the operator authors their own workflow guided by `standards/security-scanner-adapter.md` § Stack reference matrix.
|
|
1789
|
+
|
|
1790
|
+
##### 2.6 Set up .gitignore for bassclef's runtime files
|
|
1791
|
+
|
|
1792
|
+
If `.gitignore` doesn't exist, create with bassclef runtime markers:
|
|
1793
|
+
|
|
1794
|
+
```bash
|
|
1795
|
+
[ -f .gitignore ] || cat > .gitignore <<'EOF'
|
|
1796
|
+
# Bassclef runtime artifacts (per-machine, not content)
|
|
1797
|
+
.claude/LAST_SAVE
|
|
1798
|
+
.claude/SESSION_LOCK
|
|
1799
|
+
.claude/CHECKPOINT.md
|
|
1800
|
+
.claude/settings.local.json
|
|
1801
|
+
state/markers/verify/
|
|
1802
|
+
EOF
|
|
1803
|
+
```
|
|
1804
|
+
|
|
1805
|
+
The sync hook will add its managed symlink block on next run; this
|
|
1806
|
+
seeds the file with everything else.
|
|
1807
|
+
|
|
1808
|
+
#### Phase 3 — Create starter docs (CLAUDE.md, whereami)
|
|
1809
|
+
|
|
1810
|
+
**Working directory: target repo.**
|
|
1811
|
+
|
|
1812
|
+
##### 3.1 Create the starter CLAUDE.md
|
|
1813
|
+
|
|
1814
|
+
Minimal starter. Operator owns this file going forward.
|
|
1815
|
+
|
|
1816
|
+
The first non-title line reads `@.claude/bassclef-orientation.md` (per
|
|
1817
|
+
ADR-032 + Anthropic's `@filename` convention). This import loads bassclef's
|
|
1818
|
+
orientation into every session. The orientation names the gates, the
|
|
1819
|
+
luminaries, the tier ladder, and the rules the agent should not break.
|
|
1820
|
+
|
|
1821
|
+
The orientation file lands in the adopter repo as a symlink into bassclef.
|
|
1822
|
+
The sync template Section 3d-bis writes the symlink (per bassclef-upstream#925).
|
|
1823
|
+
Without this symlink, the import resolves only through `additionalDirectories`.
|
|
1824
|
+
If that path breaks — rename, delete, corporate laptop with no sibling — the
|
|
1825
|
+
import silently no-ops. The symlink closes that gap.
|
|
1826
|
+
|
|
1827
|
+
Bassclef updates the file via release. The adopter's CLAUDE.md stays
|
|
1828
|
+
adopter-owned outside the import line.
|
|
1829
|
+
|
|
1830
|
+
```markdown
|
|
1831
|
+
# [repo-name]
|
|
1832
|
+
|
|
1833
|
+
@.claude/bassclef-orientation.md
|
|
1834
|
+
|
|
1835
|
+
## What this repo is
|
|
1836
|
+
|
|
1837
|
+
[One-paragraph description]
|
|
1838
|
+
|
|
1839
|
+
## Substrate
|
|
1840
|
+
|
|
1841
|
+
Inherits bassclef substrate via `--add-dir ../bassclef` (desktop) and
|
|
1842
|
+
S3 tarball sync (mobile). The `@.claude/bassclef-orientation.md` import
|
|
1843
|
+
above auto-loads the substrate discipline (gates, luminaries, tier ladder,
|
|
1844
|
+
what-NOT-to-do). For the upstream role declaration, see
|
|
1845
|
+
`~/src/sunj-labs/bassclef/CLAUDE.md`.
|
|
1846
|
+
|
|
1847
|
+
## Stack
|
|
1848
|
+
|
|
1849
|
+
[Framework, language, key dependencies — or "documentation only"]
|
|
1850
|
+
|
|
1851
|
+
## Current state
|
|
1852
|
+
|
|
1853
|
+
[Phase, active iteration, any context a fresh session needs]
|
|
1854
|
+
```
|
|
1855
|
+
|
|
1856
|
+
##### 3.2 Create the starter docs/whereami.md
|
|
1857
|
+
|
|
1858
|
+
```bash
|
|
1859
|
+
mkdir -p docs
|
|
1860
|
+
cp ~/src/sunj-labs/bassclef/templates/whereami-template.md \
|
|
1861
|
+
docs/whereami.md
|
|
1862
|
+
```
|
|
1863
|
+
|
|
1864
|
+
Operator fills in phase + iteration section. If repo is fresh,
|
|
1865
|
+
`phase: Inception` with `iteration_bet: none — new repo` is fine.
|
|
1866
|
+
|
|
1867
|
+
#### Phase 3.5 — Optional: provision the deploy host + bootstrap secrets (bassclef#522)
|
|
1868
|
+
|
|
1869
|
+
**Working directory: target repo.**
|
|
1870
|
+
|
|
1871
|
+
After Phase 3 (docs stubs) lands the substrate config files, three opt-in
|
|
1872
|
+
flags chain follow-on skills inline so every new sunj-labs repo bootstraps
|
|
1873
|
+
in one dispatch instead of four manual steps. Without these flags, the
|
|
1874
|
+
operator runs the follow-on skills separately afterward (older pattern,
|
|
1875
|
+
still supported).
|
|
1876
|
+
|
|
1877
|
+
##### Flag reference
|
|
1878
|
+
|
|
1879
|
+
| Invocation | Behavior |
|
|
1880
|
+
|---|---|
|
|
1881
|
+
| `/onboard-repo` (no flag) | Phases 1–3 only. Interactive prompt at end: "Run /provision-deploy-host now? [Y/n/skip]" + same for /secrets-bootstrap. Skip-state persists per below. |
|
|
1882
|
+
| `/onboard-repo --with-deploy-host` | Phases 1–3, then dispatches `/provision-deploy-host` inline (operator-confirms before AWS calls). Skips the interactive prompt for deploy-host. |
|
|
1883
|
+
| `/onboard-repo --with-secrets` | Phases 1–3, then dispatches `/secrets-bootstrap` inline (audits + bootstraps env per substrate.secrets.md). Skips the interactive prompt for secrets. |
|
|
1884
|
+
| `/onboard-repo --full` | All of the above — substrate scaffolded + Amplify host provisioned + secrets audited in one dispatch. Recommended for new sunj-labs repos. |
|
|
1885
|
+
| `/onboard-repo --greenfield-from-intent "<intent>"` | Cold-adopter demo path. Skips Path A entirely and runs Path B — empty directory in, full artifact chain on disk plus localhost gallery URL out. No GitHub repo gets created. No vendor accounts needed. Uses the LLM your Claude Code session provides. For the extended path (deploy mocks to a configured subdomain plus chain to `/build` plus `/launch-preview`), finish Path A first to wire vendor bindings, then dispatch `/launch --voice-input` without `--local`. See `docs/iteration-bets/2026-05-12-phase-v-hey-alexa-mvd.md` and bassclef#923 / PR #924 for the local-mode building block. |
|
|
1886
|
+
|
|
1887
|
+
##### Tier gate (bassclef#740 Task 1.2)
|
|
1888
|
+
|
|
1889
|
+
Three of the flags above dispatch skills that ship at `tier: standard`.
|
|
1890
|
+
Lite adopters running the bassclef-lite tarball do not carry
|
|
1891
|
+
`/provision-deploy-host` or `/secrets-bootstrap`. Before dispatching
|
|
1892
|
+
`--with-deploy-host`, `--with-secrets`, or `--full`, resolve the
|
|
1893
|
+
adopter tier through `lib/tier-check.sh`:
|
|
1894
|
+
|
|
1895
|
+
```bash
|
|
1896
|
+
source lib/tier-check.sh
|
|
1897
|
+
tier=$(resolve_adopter_tier)
|
|
1898
|
+
```
|
|
1899
|
+
|
|
1900
|
+
The check reads `.claude/bassclef-configs.jsonc` `adopter.tier` field
|
|
1901
|
+
first. Falls back to skill probe — presence of `/provision-deploy-host`
|
|
1902
|
+
skill returns `standard`. Safe default is `standard`.
|
|
1903
|
+
|
|
1904
|
+
Behavior per tier:
|
|
1905
|
+
|
|
1906
|
+
| Invocation | tier=lite | tier=standard or pro |
|
|
1907
|
+
|---|---|---|
|
|
1908
|
+
| `/onboard-repo` (no flag) | Phases 1–3 run. Skip interactive prompts for /provision-deploy-host and /secrets-bootstrap. | Phases 1–3 + interactive prompts. |
|
|
1909
|
+
| `/onboard-repo --with-deploy-host` (lite adopter) | Print graceful upgrade message: "--with-deploy-host requires bassclef standard. Reason: /provision-deploy-host is a standard-tier skill. Upgrade path: install bassclef standard, or run Phases 1–3 without the flag and provision the deploy host manually later." Continue Phases 1–3 as if no flag was passed. | Phases 1–3 + `/provision-deploy-host` dispatched inline. |
|
|
1910
|
+
| `/onboard-repo --with-secrets` (lite adopter) | Print graceful upgrade message: "--with-secrets requires bassclef standard. Reason: /secrets-bootstrap is a standard-tier skill. Upgrade path: install bassclef standard, or run Phases 1–3 without the flag and manage secrets manually via substrate.secrets.md." Continue Phases 1–3. | Phases 1–3 + `/secrets-bootstrap` dispatched inline. |
|
|
1911
|
+
| `/onboard-repo --full` (lite adopter) | Print graceful upgrade message: "--full requires bassclef standard. Reason: /provision-deploy-host and /secrets-bootstrap are both standard-tier skills. Upgrade path: install bassclef standard." Continue Phases 1–3. | Phases 1–3 + both skills dispatched inline. |
|
|
1912
|
+
| `/onboard-repo --greenfield-from-intent` | Runs Path B via `/launch --local`. Lite-safe by design. | Same. |
|
|
1913
|
+
|
|
1914
|
+
Per canvas 2026-07-19-bassclef-lite Q6 Option E — one SKILL.md file,
|
|
1915
|
+
same name in both tiers, body carries runtime tier awareness with
|
|
1916
|
+
graceful degrade.
|
|
1917
|
+
|
|
1918
|
+
##### Interactive prompt (when no flag)
|
|
1919
|
+
|
|
1920
|
+
After Phase 3 completes, agent surfaces follow-on prompts. Prompts are
|
|
1921
|
+
gated on artifact existence (bassclef#961) and the deploy-host prompt
|
|
1922
|
+
uses honest platform framing (bassclef#962):
|
|
1923
|
+
|
|
1924
|
+
```bash
|
|
1925
|
+
# Check for substrate.secrets.md (bassclef#961) — silently skip the
|
|
1926
|
+
# secrets prompt if the manifest doesn't exist. No point prompting an
|
|
1927
|
+
# adopter to bootstrap secrets when they haven't declared any.
|
|
1928
|
+
HAS_SECRETS_MANIFEST=0
|
|
1929
|
+
if [ -f substrate.secrets.md ]; then
|
|
1930
|
+
HAS_SECRETS_MANIFEST=1
|
|
1931
|
+
fi
|
|
1932
|
+
```
|
|
1933
|
+
|
|
1934
|
+
Then surface (deploy-host always; secrets only when manifest present):
|
|
1935
|
+
|
|
1936
|
+
```
|
|
1937
|
+
Substrate scaffolded. One or two follow-on steps remain before /launch
|
|
1938
|
+
runs cleanly end-to-end:
|
|
1939
|
+
|
|
1940
|
+
1. /provision-deploy-host — provisions a preview deploy host. Tested
|
|
1941
|
+
today with AWS Amplify; other platforms (Vercel, Netlify, Cloudflare
|
|
1942
|
+
Pages, GCP, Azure) ship via adapter PRs. If you want a platform that
|
|
1943
|
+
doesn't ship yet, file a ticket — or build the adapter (it's a single
|
|
1944
|
+
shell script following `scripts/adapters/deploy/amplify.sh` as the
|
|
1945
|
+
reference). The provision takes ~1-2h, mostly waiting. Required
|
|
1946
|
+
before /launch can bind a subdomain on the chosen host. Skip if this
|
|
1947
|
+
repo opts out of preview deployment (hosting_platform: none) or if
|
|
1948
|
+
you want to provision later.
|
|
1949
|
+
|
|
1950
|
+
2. /secrets-bootstrap — audits + bootstraps env credentials per
|
|
1951
|
+
substrate.secrets.md. Required before /launch, /deploy-prod, or
|
|
1952
|
+
/journal push. Skip if creds are already known-good.
|
|
1953
|
+
|
|
1954
|
+
Run /provision-deploy-host now? [Y/n/skip]
|
|
1955
|
+
Run /secrets-bootstrap now? [Y/n/skip]
|
|
1956
|
+
```
|
|
1957
|
+
|
|
1958
|
+
When `HAS_SECRETS_MANIFEST=0` (no substrate.secrets.md on disk), the
|
|
1959
|
+
agent OMITS step 2 entirely + the second prompt — no "(likely no-op)"
|
|
1960
|
+
hedge, no prompt asking the adopter to bootstrap secrets they haven't
|
|
1961
|
+
declared. The numbered list collapses to "One follow-on step remains".
|
|
1962
|
+
|
|
1963
|
+
Each `Y` triggers the inline dispatch (operator-confirms any destructive
|
|
1964
|
+
substep — AWS calls, env writes). Each `n` defers to a later session.
|
|
1965
|
+
Each `skip` persists in `substrate.config.md` so re-running /onboard-repo
|
|
1966
|
+
on this repo doesn't re-prompt for that step.
|
|
1967
|
+
|
|
1968
|
+
The deploy-host prompt wording matters: cold adopters reading it should
|
|
1969
|
+
understand bassclef's posture on multi-platform support without
|
|
1970
|
+
operator coaching. "Tested with AWS Amplify; other platforms via
|
|
1971
|
+
adapter PRs" reads as honest (we have one tested path + a clear
|
|
1972
|
+
contribution route) instead of overpromising. Filing a ticket for an
|
|
1973
|
+
unsupported platform is invited, not gated.
|
|
1974
|
+
|
|
1975
|
+
##### Skip-state persistence
|
|
1976
|
+
|
|
1977
|
+
When operator chooses `skip` (not `n`) for a follow-on step, the agent
|
|
1978
|
+
writes one of these fields to `substrate.config.md`:
|
|
1979
|
+
|
|
1980
|
+
```yaml
|
|
1981
|
+
# === Onboarding follow-on skip state (bassclef#522) ===
|
|
1982
|
+
#
|
|
1983
|
+
# Set by /onboard-repo when the operator explicitly skips a follow-on
|
|
1984
|
+
# step. Re-runs of /onboard-repo do NOT re-prompt for skipped steps;
|
|
1985
|
+
# operator can dispatch the underlying skill directly when ready, or
|
|
1986
|
+
# unset the field to re-enable the prompt.
|
|
1987
|
+
onboarding_deploy_host_skipped: true # set when operator answers 'skip'
|
|
1988
|
+
onboarding_secrets_skipped: true # set when operator answers 'skip'
|
|
1989
|
+
```
|
|
1990
|
+
|
|
1991
|
+
`n` (decline-this-time) does NOT persist — re-runs re-prompt because the
|
|
1992
|
+
operator didn't say skip-permanently.
|
|
1993
|
+
|
|
1994
|
+
`Y` sets the per-platform provisioning indicator (e.g., `amplify_app_id`) on success. Chain pre-flight Check 6 (bassclef#523) reads that indicator when /launch dispatches. Without it, /launch BLOCKs with three-path remediation. The two flags pair well. `--with-deploy-host` pre-empts the BLOCK. The default flow lets the BLOCK surface the missing setup.
|
|
1995
|
+
|
|
1996
|
+
##### Composes with
|
|
1997
|
+
|
|
1998
|
+
| Skill | Role |
|
|
1999
|
+
|---|---|
|
|
2000
|
+
| `/provision-deploy-host` | Inline dispatch when `--with-deploy-host` or interactive `Y`. Populates `amplify_app_id` (or platform-equivalent indicator). |
|
|
2001
|
+
| `/secrets-bootstrap` | Inline dispatch when `--with-secrets` or interactive `Y`. Audits creds against `substrate.secrets.md`. |
|
|
2002
|
+
| `/bind-subdomain <subdomain>` | Separate dispatch — not chained from /onboard-repo (subdomain choice is repo-specific; no good default). Run after deploy host provisioned. |
|
|
2003
|
+
| `/launch` | First substantive dispatch after onboarding completes. Chain pre-flight Check 6 verifies the provisioning indicator before variant generation runs. |
|
|
2004
|
+
|
|
2005
|
+
#### Phase 3.6 — Closing handoff (bassclef#964)
|
|
2006
|
+
|
|
2007
|
+
After Phase 3 (and Phase 3.5 if any flag fired), Path A surfaces a
|
|
2008
|
+
closing handoff. Two templates — adopter-mode and operator-mode —
|
|
2009
|
+
mirror the same split documented at `.claude/skills/launch/SKILL.md`
|
|
2010
|
+
Phase 14b.
|
|
2011
|
+
|
|
2012
|
+
##### Adopter-mode auto-chain (default when context signals cold-adopter)
|
|
2013
|
+
|
|
2014
|
+
Cold-adopter signals: `/onboard-repo` was invoked from a fresh
|
|
2015
|
+
directory (no prior chronicle / no whereami / no iteration-goal), OR
|
|
2016
|
+
the `--greenfield-from-intent "<paragraph>"` flag was used. In either
|
|
2017
|
+
shape, the agent has a paragraph in hand (either from the flag or
|
|
2018
|
+
asked at Phase 1 as part of the "what is this repo for?" prompt).
|
|
2019
|
+
|
|
2020
|
+
The adopter-mode handoff template re-uses that paragraph and offers
|
|
2021
|
+
the build:
|
|
2022
|
+
|
|
2023
|
+
```
|
|
2024
|
+
Your repo is wired. Two things you can do next — pick one:
|
|
2025
|
+
|
|
2026
|
+
1. Build something now. I'll take the paragraph you gave me earlier
|
|
2027
|
+
("<INTENT_PARAGRAPH>") and run /launch --local. About 4 minutes
|
|
2028
|
+
later you'll have four clickable mock directions to pick from on
|
|
2029
|
+
your phone or laptop. After you pick a direction, I'll write the
|
|
2030
|
+
formal artifacts + the app code for that direction only (~15 min
|
|
2031
|
+
total). You'll have a working local version to play with.
|
|
2032
|
+
|
|
2033
|
+
2. Stop here and come back later. I'll save your spot. When you come
|
|
2034
|
+
back, just open Claude Code in this repo and say "/launch --local"
|
|
2035
|
+
to pick up where we left off.
|
|
2036
|
+
|
|
2037
|
+
Build now? [Y/n]
|
|
2038
|
+
```
|
|
2039
|
+
|
|
2040
|
+
`Y` triggers `/launch --local "<INTENT_PARAGRAPH>"` inline in the
|
|
2041
|
+
same session — no context switch, no second dispatch. `n` writes
|
|
2042
|
+
the paragraph to `docs/whereami.md` under "Pending intent" so the
|
|
2043
|
+
next session's `/sprint` surfaces it.
|
|
2044
|
+
|
|
2045
|
+
Substitute `<INTENT_PARAGRAPH>` with the actual paragraph (from
|
|
2046
|
+
`--greenfield-from-intent` flag value OR from the Phase 1 "what is
|
|
2047
|
+
this repo for?" prompt — operator confirms it's still accurate before
|
|
2048
|
+
the auto-chain fires).
|
|
2049
|
+
|
|
2050
|
+
The adopter-mode handoff **skips entirely**:
|
|
2051
|
+
- "Run /sprint to orient" — adopter has no work in flight to orient
|
|
2052
|
+
to
|
|
2053
|
+
- "Run /whereami" — same
|
|
2054
|
+
- Bare command names (`/launch`) without the plain-English explanation
|
|
2055
|
+
of what /launch produces
|
|
2056
|
+
|
|
2057
|
+
##### Operator-mode handoff (non-cold-adopter context)
|
|
2058
|
+
|
|
2059
|
+
This mode fires when cold-adopter signals are absent. Signals absent means: the repo has prior chronicles, whereami is populated, OR the operator passed `--no-auto-chain`. The agent uses this wording:
|
|
2060
|
+
|
|
2061
|
+
```
|
|
2062
|
+
Your bassclef is ready.
|
|
2063
|
+
|
|
2064
|
+
What landed this session:
|
|
2065
|
+
- Skills, rules, and hooks synced from bassclef
|
|
2066
|
+
- GitHub labels + branch protection set up
|
|
2067
|
+
- Session-start wiring wired
|
|
2068
|
+
|
|
2069
|
+
Best single next move — try /sprint in a fresh session. It reads
|
|
2070
|
+
your current state and proposes the next work.
|
|
2071
|
+
|
|
2072
|
+
Other useful next moves:
|
|
2073
|
+
- /whereami — current state in one glance
|
|
2074
|
+
- /launch <tier> "<paragraph>" — start a new feature; see /launch
|
|
2075
|
+
SKILL.md for sizes + flags
|
|
2076
|
+
- /launch --local "<paragraph>" — same as above but writes artifacts
|
|
2077
|
+
to disk + serves the gallery on localhost (no vendor accounts
|
|
2078
|
+
needed)
|
|
2079
|
+
|
|
2080
|
+
Run any of those in a fresh session.
|
|
2081
|
+
```
|
|
2082
|
+
|
|
2083
|
+
Operator-mode keeps a hard break between onboard and build. Operators often onboard a repo as one task. They pick up build work later in a fresh session.
|
|
2084
|
+
|
|
2085
|
+
##### Composes with
|
|
2086
|
+
|
|
2087
|
+
| Skill | How it composes |
|
|
2088
|
+
|---|---|
|
|
2089
|
+
| `/launch --local` | Adopter-mode auto-chain dispatch. Inline in same session. |
|
|
2090
|
+
| `/sprint` | Operator-mode orientation skill. Surfaces "Pending intent" if `n` was chosen for the build-now prompt. |
|
|
2091
|
+
| `/whereami` | Same. |
|
|
2092
|
+
|
|
2093
|
+
#### Phase 4 — Bassclef-side matrix PRs
|
|
2094
|
+
|
|
2095
|
+
**Phase 4 retired under bassclef goal 2026-05-03-consumer-bassclef-sync-rollout WU-7.**
|
|
2096
|
+
|
|
2097
|
+
Under thin-pointer architecture (Phase 2.1 + 2.4 above), the `bassclef-template-sync.yml` workflow is deleted (no more auto-PRs). The `bassclef-s3-publish.yml` URL-refresh matrix is unused by thin-pointer (no `.bassclef-url`); a follow-on issue tracks its retirement separately.
|
|
2098
|
+
|
|
2099
|
+
**No matrix-PR step is needed for new consumers.** The thin-pointer dispatcher reads from `.bassclef-source.json` directly; bassclef's matrix list is no longer the source of truth for which consumers receive substrate updates — every consumer fetches HEAD on every session-start.
|
|
2100
|
+
|
|
2101
|
+
Skip this phase entirely. Phase 5 (Verify) below validates that the consumer is correctly wired without any bassclef-side matrix change.
|
|
2102
|
+
|
|
2103
|
+
#### Historical note
|
|
2104
|
+
|
|
2105
|
+
Pre-2026-05-03, this phase added the new consumer to BOTH `bassclef-s3-publish.yml` and `bassclef-template-sync.yml` matrices. The 2026-04-18 lesson (#166 caught a one-of-two miss after #158) drove the bassclef-workflow-parity rule. With both workflows retired/unused under thin-pointer, the parity rule no longer applies to this surface.
|
|
2106
|
+
|
|
2107
|
+
## Verify (acceptance)
|
|
2108
|
+
|
|
2109
|
+
Acceptance differs per path.
|
|
2110
|
+
|
|
2111
|
+
### Path A — verify everything's wired correctly (after Phases 1–3 + 3.5 if flagged)
|
|
2112
|
+
|
|
2113
|
+
- [ ] `gh label list --repo "$REPO"` shows all 11 standard labels
|
|
2114
|
+
- [ ] `gh api repos/$REPO/branches/$DEFAULT_BRANCH/protection` returns 200
|
|
2115
|
+
- [ ] (If applicable) `gh api repos/$REPO/environments/prod` shows required_reviewers
|
|
2116
|
+
- [ ] `.claude/hooks/bassclef-sync.sh` is executable
|
|
2117
|
+
- [ ] `.claude/settings.json` validates as JSON
|
|
2118
|
+
- [ ] `substrate.config.md` has no `TODO:` markers (or operator approved deferring them)
|
|
2119
|
+
- [ ] `.bassclef-source.json` exists, validates against bassclef schema (`standards/bassclef-source/schemas/bassclef-source.schema.json`), references a valid `BASSCLEF_TOKEN` env var, and has an explicit `auto_sync` value (true OR false) set by the adopter at Phase 2.4 — silence is not deferral here
|
|
2120
|
+
- [ ] Running the sync hook produces no errors; banner shows `### BASSCLEF SUBSTRATE — SYNCED`; symlinks resolve
|
|
2121
|
+
- [ ] `CLAUDE.md` exists with at least a one-paragraph description
|
|
2122
|
+
- [ ] `docs/whereami.md` exists with phase + iteration filled in
|
|
2123
|
+
- [ ] **(Retired)** ~~Bassclef PR opened with both workflow matrices updated~~ — Phase 4 retired under bassclef goal 2026-05-03 WU-7
|
|
2124
|
+
- [ ] Test session in target repo: `claude` → SessionStart hook fires → no BLOCKED items from missing substrate
|
|
2125
|
+
|
|
2126
|
+
### Path B acceptance — after Phases B.1–B.5
|
|
2127
|
+
|
|
2128
|
+
- [ ] Generated task list had at most 5 tasks (Phase B.1–B.5); no Path A phases queued
|
|
2129
|
+
- [ ] No `gh` calls fired during the dispatch
|
|
2130
|
+
- [ ] No `gh repo create`, no `gh api .../environments/prod`, no branch-protection writes
|
|
2131
|
+
- [ ] `.git/` exists with at least one commit
|
|
2132
|
+
- [ ] `.claude/hooks/bassclef-sync.sh` is executable
|
|
2133
|
+
- [ ] `.claude/settings.json` validates as JSON
|
|
2134
|
+
- [ ] `substrate.config.md` exists with `hosting_platform: none`
|
|
2135
|
+
- [ ] `CLAUDE.md` exists
|
|
2136
|
+
- [ ] `/launch --local --voice-input` was the final dispatch (not extended `/launch` and not `/build` and not `/launch-preview`)
|
|
2137
|
+
- [ ] `/launch` emitted a `localhost:80xx` URL pointing at the generated gallery
|
|
2138
|
+
- [ ] Operator opened the URL and saw the variants gallery
|
|
2139
|
+
|
|
2140
|
+
## What NOT to do
|
|
2141
|
+
|
|
2142
|
+
- **Do not** restore Phase 4 — the bassclef-template-sync.yml workflow is retired. Adding new consumers to a deleted matrix produces nothing.
|
|
2143
|
+
- **Do not** vendor `bassclef-sync.template.sh` directly — that was the pre-2026-05-03 pattern. The thin-pointer dispatcher reads `.bassclef-source.json` and fetches bassclef's CURRENT logic at fire-time. Vendoring reproduces the drift problem WU-7 retires.
|
|
2144
|
+
- **Do not** auto-merge the bassclef-side PR. Bassclef is the trust
|
|
2145
|
+
root; every matrix change needs operator review.
|
|
2146
|
+
- **Do not** hardcode the presigned URL anywhere — read from
|
|
2147
|
+
`.bassclef-url` at runtime.
|
|
2148
|
+
- **Do not** copy `.claude/settings.local.json` from another repo — it's
|
|
2149
|
+
machine-local per-operator permission grants.
|
|
2150
|
+
- **Do not** commit secrets. The sync URL expires in ~7 days; don't
|
|
2151
|
+
treat it as a secret, but don't commit long-lived tokens either.
|
|
2152
|
+
- **Do not** mix Path A and Path B in one dispatch. If the operator picked
|
|
2153
|
+
Path B at the prereqs-missing menu, skip every Path A phase — don't
|
|
2154
|
+
generate a task list that walks Phase 1.1 / 1.2 / 1.3 / 2.4 / 3.5 /
|
|
2155
|
+
Phase 4. The whole point of Path B is the ≤5-step task list ending
|
|
2156
|
+
in `/launch --local --voice-input`. Any Path A task that leaks into a
|
|
2157
|
+
Path B run is the bassclef#934 regression.
|
|
2158
|
+
- **Do not** chain `/build` or `/launch-preview` after Path B's final
|
|
2159
|
+
dispatch. Path B's deliverable is the localhost URL `/launch --local`
|
|
2160
|
+
emits; the extended chain belongs to operators with vendor bindings.
|
|
2161
|
+
|
|
2162
|
+
## Relationship to other skills
|
|
2163
|
+
|
|
2164
|
+
- **`/autonomous start`** — runs AFTER `/onboard-repo` to set up iteration
|
|
2165
|
+
scaffolding. `/onboard-repo` does the one-time wiring; `/autonomous
|
|
2166
|
+
start` does the per-iteration setup.
|
|
2167
|
+
- **`/substrate-check`** — validates substrate wiring AFTER onboarding.
|
|
2168
|
+
Run it as a post-flight check.
|
|
2169
|
+
- **`/promote`** — once onboarded, the new repo can file
|
|
2170
|
+
`bassclef-evolution` issues. The labels bootstrapped in Phase 1.1
|
|
2171
|
+
make them scannable.
|
|
2172
|
+
|
|
2173
|
+
## Source
|
|
2174
|
+
|
|
2175
|
+
Ritual observed 4x on 2026-04-17: four consumer repos (refresh). Each took ~15 minutes by hand.
|
|
2176
|
+
Codified here so the fifth, sixth, and nth take ~3 minutes.
|
|
2177
|
+
|
|
2178
|
+
Closes bassclef #164.
|
|
2179
|
+
|
|
2180
|
+
Path B added 2026-06-13. Cold-adopter smoke test on `new-recipe-test`
|
|
2181
|
+
2026-06-13 01:30am showed PR #931's edit to the flag-row description
|
|
2182
|
+
was cosmetic — `--greenfield-from-intent` still queued 12 standard
|
|
2183
|
+
onboarding tasks because the Procedure was monolithic. Refactor splits
|
|
2184
|
+
Procedure into Path A (standard, unchanged) and Path B (≤5 steps, no
|
|
2185
|
+
GitHub-remote operations, ends in `/launch --local --voice-input`).
|
|
2186
|
+
Closes bassclef#934.
|
|
2187
|
+
|
|
2188
|
+
## Output discipline
|
|
2189
|
+
|
|
2190
|
+
Dispatch `/kiss words --rewrite` on your skill output before you return it. See `standards/skill-output-discipline.md` for the contract.
|