@thebassclef/lite 0.1.0 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +40 -6
- package/dist/cli.js +40 -6
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/substrate/.bassclef/lite-manifest.json +999 -92
- package/substrate/.claude/hooks/longrun-prep-compounding-axis-check.sh +492 -0
- package/substrate/.claude/hooks/longrun-prep-compounding-sequence-check.sh +492 -0
- package/substrate/.claude/hooks/pre-commit-gate.sh +1 -2
- package/substrate/.claude/hooks/turn-prose-kiss-check.sh +30 -1
- package/substrate/.claude/luminaries/glenford-myers.md +230 -0
- package/substrate/.claude/luminaries/hunt-thomas.md +115 -0
- package/substrate/.claude/luminaries/hyrum-wright.md +94 -0
- package/substrate/.claude/luminaries/michael-feathers.md +2 -2
- package/substrate/.claude/luminaries/tony-hoare.md +170 -0
- package/substrate/.claude/luminaries/vaughn-vernon.md +50 -0
- package/substrate/.claude/luminaries/w-edwards-deming.md +158 -0
- package/substrate/.claude/rules/accessor-library-discipline.md +138 -0
- package/substrate/.claude/rules/adr-discipline.md +120 -0
- package/substrate/.claude/rules/api-conventions.md +125 -0
- package/substrate/.claude/rules/bootstrap-pair-discipline.md +141 -0
- package/substrate/.claude/rules/cold-adopter-harness-discipline.md +129 -0
- package/substrate/.claude/rules/compounding-axis-fresh-analysis.md +188 -0
- package/substrate/.claude/rules/compounding-sequence-fresh-analysis.md +188 -0
- package/substrate/.claude/rules/defensive-bash.md +68 -0
- package/substrate/.claude/rules/deferred-actions.md +233 -0
- package/substrate/.claude/rules/github-issue-flash-tweet.md +156 -0
- package/substrate/.claude/rules/hook-wire-on-author.md +103 -0
- package/substrate/.claude/rules/iteration-bet-brief-completeness.md +54 -0
- package/substrate/.claude/rules/lite-manifest-schema-change-discipline.md +3 -3
- package/substrate/.claude/rules/longrun-prep-plan-doc-compression.md +89 -0
- package/substrate/.claude/rules/loop-discipline.md +81 -0
- package/substrate/.claude/rules/manual-prod-approval.md +100 -0
- package/substrate/.claude/rules/marker-enrichment-discipline.md +99 -0
- package/substrate/.claude/rules/mobile-ephemeral-session.md +109 -0
- package/substrate/.claude/rules/new-dependency-check.md +51 -0
- package/substrate/.claude/rules/option-label-discipline.md +108 -0
- package/substrate/.claude/rules/pattern-annotation.md +100 -0
- package/substrate/.claude/rules/plain-english-discipline.md +11 -9
- package/substrate/.claude/rules/plan-enumeration-needs-value-props.md +211 -0
- package/substrate/.claude/rules/pr-title-shape.md +161 -0
- package/substrate/.claude/rules/prototype-workflow.md +65 -0
- package/substrate/.claude/rules/reserved-skill-names.md +123 -0
- package/substrate/.claude/rules/schema-management.md +49 -0
- package/substrate/.claude/rules/security.md +37 -0
- package/substrate/.claude/rules/skill-composition-declarations.md +124 -0
- package/substrate/.claude/rules/skill-description-clarity.md +247 -0
- package/substrate/.claude/rules/skill-procedure-step-list.md +137 -0
- package/substrate/.claude/rules/stuck-signal-diagnostic.md +140 -0
- package/substrate/.claude/rules/substrate-config-schema.md +98 -0
- package/substrate/.claude/rules/test-list-discipline.md +175 -0
- package/substrate/.claude/rules/test-sufficiency.md +210 -0
- package/substrate/.claude/rules/testing-tier-config.md +145 -0
- package/substrate/.claude/rules/testing.md +38 -0
- package/substrate/.claude/rules/turn-estimate-grounding.md +134 -0
- package/substrate/.claude/rules/visual-hierarchy.md +437 -0
- package/substrate/.claude/rules/we-dont-break-adopters.md +126 -0
- package/substrate/.claude/rules/wu-sequencing-compounds.md +145 -0
- package/substrate/.claude/skills/build/SKILL.md +1 -1
- package/substrate/.claude/skills/chronicle/SKILL.md +55 -0
- package/substrate/.claude/skills/clean-artifacts/SKILL.md +249 -0
- package/substrate/.claude/skills/decompose/SKILL.md +1 -1
- package/substrate/.claude/skills/diagnose/SKILL.md +1 -1
- package/substrate/.claude/skills/feynman/SKILL.md +90 -0
- package/substrate/.claude/skills/howdoi/SKILL.md +1 -1
- package/substrate/.claude/skills/ia-model/SKILL.md +1 -1
- package/substrate/.claude/skills/interaction-design/SKILL.md +1 -1
- package/substrate/.claude/skills/interpret-input/SKILL.md +8 -8
- package/substrate/.claude/skills/journal/SKILL.md +209 -0
- package/substrate/.claude/skills/kiss/SKILL.md +1 -1
- package/substrate/.claude/skills/launch/SKILL.md +14 -23
- package/substrate/.claude/skills/lean-canvas/SKILL.md +1 -1
- package/substrate/.claude/skills/longrun/SKILL.md +45 -8
- package/substrate/.claude/skills/luminary/SKILL.md +1 -1
- package/substrate/.claude/skills/ogilvy-writing-audit/SKILL.md +1 -1
- package/substrate/.claude/skills/onboard-repo/SKILL.md +143 -709
- package/substrate/.claude/skills/pattern-review/SKILL.md +1 -1
- package/substrate/.claude/skills/personas/SKILL.md +5 -5
- package/substrate/.claude/skills/promote/SKILL.md +1 -1
- package/substrate/.claude/skills/requirement/SKILL.md +1 -1
- package/substrate/.claude/skills/retro/SKILL.md +1 -1
- package/substrate/.claude/skills/riff/SKILL.md +1 -1
- package/substrate/.claude/skills/roadmap-reconcile/SKILL.md +1 -1
- package/substrate/.claude/skills/session-end/SKILL.md +1 -1
- package/substrate/.claude/skills/session-log/SKILL.md +3 -3
- package/substrate/.claude/skills/skills/SKILL.md +1 -1
- package/substrate/.claude/skills/spec/SKILL.md +1 -1
- package/substrate/.claude/skills/sprint/SKILL.md +1 -1
- package/substrate/.claude/skills/stage/SKILL.md +1 -1
- package/substrate/.claude/skills/state-a-problem/SKILL.md +1 -1
- package/substrate/.claude/skills/temperance/SKILL.md +1 -1
- package/substrate/.claude/skills/use-case/SKILL.md +1 -1
- package/substrate/.claude/skills/user-stories/SKILL.md +1 -1
- package/substrate/.claude/skills/value-prop/SKILL.md +1 -1
- package/substrate/.claude/skills/verify/SKILL.md +1 -1
- package/substrate/.claude/skills/visual-review/SKILL.md +503 -0
- package/substrate/.claude/skills/whats-the-plan/SKILL.md +202 -0
- package/substrate/.claude/skills/whereami/SKILL.md +2 -2
- package/substrate/CONTRIBUTING.md +1 -1
- package/substrate/README.md +5 -5
- package/substrate/lib/prose-scan-boundary.sh +171 -0
- package/substrate/lib/tier-check.sh +50 -1
- package/substrate/lib/tier-dependency-audit.sh +159 -4
- package/substrate/presence/install/bassclef-sync.template.sh +1 -1
- package/substrate/scripts/generate-lite-manifest.sh +21 -5
- package/substrate/standards/adr-template.md +86 -0
- package/substrate/standards/api-conventions/nextjs.md +84 -0
- package/substrate/standards/artifact-composition.md +209 -0
- package/substrate/standards/bash-hook-safety.md +246 -0
- package/substrate/standards/branch-stacking.md +408 -0
- package/substrate/standards/code-safety-principles.md +176 -0
- package/substrate/standards/composer-prerequisites.md +155 -0
- package/substrate/standards/dependency-discipline/cargo.md +39 -0
- package/substrate/standards/dependency-discipline/gem.md +43 -0
- package/substrate/standards/dependency-discipline/go-mod.md +41 -0
- package/substrate/standards/dependency-discipline/npm.md +42 -0
- package/substrate/standards/dependency-discipline/pip.md +42 -0
- package/substrate/standards/deployment-topology/ec2-tailscale.md +225 -0
- package/substrate/standards/deployment-topology.md +69 -0
- package/substrate/standards/docs-sync-allowlist.md +4 -4
- package/substrate/standards/domain-and-dns.md +145 -0
- package/substrate/standards/frontend-stack.md +67 -0
- package/substrate/standards/frontmatter-schema.md +154 -0
- package/substrate/standards/hook-injection-discipline.md +202 -0
- package/substrate/standards/hook-install-class.md +215 -0
- package/substrate/standards/input-handler-interface.md +152 -0
- package/substrate/standards/lite-manifest-schema-changes.md +60 -0
- package/substrate/standards/luminary-matching.md +105 -0
- package/substrate/standards/migration-discipline/active-record.md +50 -0
- package/substrate/standards/migration-discipline/alembic.md +43 -0
- package/substrate/standards/migration-discipline/gorm.md +50 -0
- package/substrate/standards/migration-discipline/prisma.md +53 -0
- package/substrate/standards/migration-discipline/sqlalchemy.md +51 -0
- package/substrate/standards/mobile-ephemeral-session.md +167 -0
- package/substrate/standards/model-routing-discipline.md +160 -0
- package/substrate/standards/persona-schema.md +229 -0
- package/substrate/standards/pluggable-luminaries.md +323 -0
- package/substrate/standards/pr-body-discipline.md +115 -0
- package/substrate/standards/preview-state-schema.md +189 -0
- package/substrate/standards/reserved-skill-names.md +120 -0
- package/substrate/standards/scannable-multi-option-output.md +261 -0
- package/substrate/standards/sdlc-gates/typescript.md +57 -0
- package/substrate/standards/session-board.md +256 -0
- package/substrate/standards/state-spine-contract.md +255 -0
- package/substrate/standards/steering-hints/kiss-words.md +11 -0
- package/substrate/standards/substrate-config-schema.md +267 -0
- package/substrate/standards/tier-dependency-analysis.md +1 -1
- package/substrate/standards/tier-tag-schema.md +1 -1
- package/substrate/standards/two-layer-config.md +99 -0
- package/substrate/standards/use-case-format.md +292 -0
- package/substrate/standards/user-story-invest.md +268 -0
- package/substrate/standards/velocity-and-appetite.md +229 -0
- package/substrate/standards/voice-input-pattern.md +119 -0
- package/substrate/standards/worktree-management.md +211 -0
- package/substrate/templates/chronicle-template.md +75 -0
- package/substrate/templates/memory-proposal-template.md +77 -0
- package/substrate/templates/persona-template.md +200 -0
- package/substrate/templates/pr-faq.md +45 -0
- package/substrate/templates/secret-rotation-template.md +162 -0
- package/substrate/templates/spec-template.md +131 -0
- package/substrate/templates/use-case-template.md +194 -0
- package/substrate/templates/user-story-template.md +107 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
---
|
|
2
|
+
tier: lite
|
|
3
|
+
description: "The interface contract for lib/state.sh — the typed accessor library over bassclef's state spine."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# State Spine Accessor Contract
|
|
7
|
+
|
|
8
|
+
The interface contract for `lib/state.sh` — the typed accessor library
|
|
9
|
+
over bassclef's state spine. Repository pattern (Fowler PoEAA): hides
|
|
10
|
+
storage backend (JSON-on-git for v0; cxdb for opt-in adopters per
|
|
11
|
+
bassclef#300) from consumers via a stable accessor surface.
|
|
12
|
+
|
|
13
|
+
This standard is the contract. The implementation is `lib/state.sh`.
|
|
14
|
+
Adapter discipline (per canvas Stage 3 §"Adapter discipline") preserves
|
|
15
|
+
the swap surface — when cxdb adoption ships, the implementation changes
|
|
16
|
+
but this contract does not.
|
|
17
|
+
|
|
18
|
+
## Sources read
|
|
19
|
+
|
|
20
|
+
- `docs/iteration-bets/2026-04-27-state-spine-v0.md` §"WU-2 Typed accessor library" — procedure + acceptance
|
|
21
|
+
- `strategy/canvases/2026-04-26-state-spine.md` v4 — accessor discipline (replay-determinism); pattern-set (Repository/Facade); cxdb-adapter exit criteria
|
|
22
|
+
- `architecture/decisions/ADR-015-state-schema-format.md` — JSON Schema only for v0 (no Zod runtime)
|
|
23
|
+
- `standards/state-spine.md` — entity catalog (17 v0 + Persona); storage shapes (3 — A/B/C); replay-determinism rule; write-once-immutability rule
|
|
24
|
+
- All 18 v0 entity schemas in `standards/state-spine/schemas/` — define what each accessor returns
|
|
25
|
+
- `.claude/luminaries/martin-fowler.md` — Repository pattern + Strangler Fig
|
|
26
|
+
- `.claude/rules/diagnosis.md` — error-shape conventions (return non-zero + stderr message; never silent failure)
|
|
27
|
+
|
|
28
|
+
## What I'm NOT reading (with reason)
|
|
29
|
+
|
|
30
|
+
- cxdb / Dolt vendor docs — out of v0 scope (Track B)
|
|
31
|
+
- `extract-session-telemetry.sh` line-by-line — WU-5's pre-read; this contract defines the surface telemetry will consume, not telemetry itself
|
|
32
|
+
|
|
33
|
+
## Naming convention
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
state_<entity>_<verb>[_<modifier>]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- **entity** — kebab-case entity name from state-spine.md (e.g., `whereami`, `iteration_bet`, `deferred_action`, `chronicle`, `marker`)
|
|
40
|
+
- **verb** — operation (bassclef set below)
|
|
41
|
+
- **modifier** — optional qualifier for special-purpose accessors
|
|
42
|
+
|
|
43
|
+
Examples:
|
|
44
|
+
- `state_whereami_get` — get the singleton
|
|
45
|
+
- `state_iteration_bet_list` — list all bets
|
|
46
|
+
- `state_marker_count` — count markers (with optional filter args)
|
|
47
|
+
- `state_chronicle_gate_evidence` — special-purpose: extract gates_fired from a chronicle
|
|
48
|
+
- `state_deferred_action_pending` — special-purpose: list pending actions matching capabilities
|
|
49
|
+
|
|
50
|
+
## Verb vocabulary
|
|
51
|
+
|
|
52
|
+
Stable set. Extend via this contract (with a paired schema-side rationale).
|
|
53
|
+
|
|
54
|
+
| Verb | Semantics | Allowed for |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `get` | Single entity by id; returns JSON to stdout, 1 to stderr if not found | All entities |
|
|
57
|
+
| `list` | Collection (optional filter args); returns JSON array to stdout (empty array `[]` if none) | All entities |
|
|
58
|
+
| `count` | Counter (optional filter args); returns integer to stdout | All entities |
|
|
59
|
+
| `add` | Append a new entity; takes JSON on stdin, writes file, returns id to stdout | Write-many entities (Marker, Chronicle, Decomposition, InputArtifact, Session, Gate, InboxWorkItem, TemplateInstance, DeferredAction) |
|
|
60
|
+
| `set` | Update field on singleton; takes field + value, validates against schema, writes | Singletons only (Whereami, SubstrateConfig, PreviewState, BassclefPlatform) |
|
|
61
|
+
| `update_status` | Change status field; validates new value against entity's status enum | Mutable-status entities (IterationBet, Step, InboxWorkItem, DeferredAction) |
|
|
62
|
+
| `render` | Generate operator-readable markdown from JSON authoritative source | Singletons (Whereami, SubstrateConfig, PreviewState) |
|
|
63
|
+
|
|
64
|
+
Forbidden verbs: any mutation on Markers, Chronicles, Discoveries,
|
|
65
|
+
Sessions (write-once-immutable per Helland — see canvas §"Accessor
|
|
66
|
+
discipline"). Updates produce NEW records; readers consider the latest
|
|
67
|
+
non-superseded record per business-key.
|
|
68
|
+
|
|
69
|
+
## Return semantics
|
|
70
|
+
|
|
71
|
+
| Outcome | Stdout | Stderr | Exit code |
|
|
72
|
+
|---|---|---|---|
|
|
73
|
+
| Success (single entity) | JSON object | (empty) | 0 |
|
|
74
|
+
| Success (list) | JSON array (possibly empty) | (empty) | 0 |
|
|
75
|
+
| Success (count) | integer | (empty) | 0 |
|
|
76
|
+
| Success (add) | new id | (empty) | 0 |
|
|
77
|
+
| Not found | (empty) | (empty) | 1 |
|
|
78
|
+
| Invalid input | (empty) | error message | 2 |
|
|
79
|
+
| Schema validation failed | (empty) | jq/ajv error | 3 |
|
|
80
|
+
| File system error | (empty) | error message | 4 |
|
|
81
|
+
| Unsupported entity / verb | (empty) | error message | 5 |
|
|
82
|
+
|
|
83
|
+
Consumers check exit code; non-zero is failure. Never silent — error
|
|
84
|
+
messages always go to stderr per `.claude/rules/diagnosis.md`
|
|
85
|
+
("Step 0 — Reproduce" requires the failure to surface).
|
|
86
|
+
|
|
87
|
+
## Replay-determinism rule (canvas v4 SWF mining)
|
|
88
|
+
|
|
89
|
+
Counters and computed state derived from history on every call. **Never
|
|
90
|
+
cache derived values as side state.**
|
|
91
|
+
|
|
92
|
+
Concretely:
|
|
93
|
+
- `state_marker_count [category] [since-N-sessions]` reads the Marker
|
|
94
|
+
collection + computes; no `latest_count` field stored anywhere
|
|
95
|
+
- `state_chronicle_gate_evidence <id>` derives from session's marker
|
|
96
|
+
history at chronicle write time, not maintained out-of-band
|
|
97
|
+
- `state_whereami_recompute` regenerates the Whereami singleton from
|
|
98
|
+
collection state; the JSON sidecar at `docs/whereami.json` is a
|
|
99
|
+
cache, not authority
|
|
100
|
+
|
|
101
|
+
This is the bassclef analog of SWF's "decider must be idempotent on
|
|
102
|
+
replay." Same input history → same output, deterministically. Failing
|
|
103
|
+
at this is what produced bassclef#296 (compliance counter floor-lift).
|
|
104
|
+
|
|
105
|
+
## Write-once-immutability rule (Helland)
|
|
106
|
+
|
|
107
|
+
Markers, Chronicles, Discoveries, Sessions are write-once. Once
|
|
108
|
+
committed:
|
|
109
|
+
- Filename never changes (UUID for Markers; ID-based for others)
|
|
110
|
+
- Body content never changes
|
|
111
|
+
- Updates produce NEW records that supersede the prior (via
|
|
112
|
+
`supersedes` field per the Marker schema)
|
|
113
|
+
|
|
114
|
+
Accessors that mutate (`add`) write atomic — file lands or fails. No
|
|
115
|
+
partial writes. Use temp-file + rename pattern.
|
|
116
|
+
|
|
117
|
+
Accessors that don't mutate (`get`, `list`, `count`, `update_status`)
|
|
118
|
+
operate on whatever's on disk; consumers responsible for any
|
|
119
|
+
serializability they need (out of v0 scope; cxdb upgrade addresses for
|
|
120
|
+
concurrent fanout).
|
|
121
|
+
|
|
122
|
+
## Storage-shape extraction (per state-spine.md §"Storage format")
|
|
123
|
+
|
|
124
|
+
The accessor handles all three on-disk shapes via entity-type-aware
|
|
125
|
+
extraction:
|
|
126
|
+
|
|
127
|
+
### Shape A entities (singleton JSON authoritative + markdown rendered)
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
state_whereami_get() { cat docs/whereami.json; }
|
|
131
|
+
state_whereami_set <field> <value> # jq update + atomic rename
|
|
132
|
+
state_whereami_render # JSON → markdown (with auto-generated banner)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Shape B entities (markdown + YAML frontmatter + body)
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
state_iteration_bet_get <slug> {
|
|
139
|
+
# Extract YAML frontmatter, convert to JSON
|
|
140
|
+
awk '/^---$/{c++; next} c==1' docs/iteration-bets/$slug.md | yq2json
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`yq2json` is implemented as `python3 -c "import sys,yaml,json; print(json.dumps(yaml.safe_load(sys.stdin), default=str))"` (yaml + json stdlib, available everywhere).
|
|
145
|
+
|
|
146
|
+
### Shape C entities (pure JSON)
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
state_marker_get <uuid> {
|
|
150
|
+
# Find by UUID across category dirs
|
|
151
|
+
find state/markers -name "${uuid}.json" -type f -print -quit | xargs cat
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Shape D entities (pure YAML at sibling-repo root) — added bassclef#462
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
state_platform_get [dot.path.key] {
|
|
159
|
+
# Resolves file via BASSCLEF_PLATFORM_CONFIG_PATH env or default
|
|
160
|
+
# ~/src/sunj-labs/platform-config/bassclef-platform.yml; converts
|
|
161
|
+
# YAML to JSON; if a key is given, jq-walks the path.
|
|
162
|
+
python3 -c "import yaml,json; print(json.dumps(yaml.safe_load(open('$path'))))" \
|
|
163
|
+
| jq -r --arg key "$key" 'getpath($key | split("."))'
|
|
164
|
+
}
|
|
165
|
+
state_platform_path # returns the resolved file path (no read)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Special-purpose accessors (v0 hot-path consumers)
|
|
169
|
+
|
|
170
|
+
Beyond generic verbs, the following exist for known v0 consumers:
|
|
171
|
+
|
|
172
|
+
| Accessor | Returns | Consumer |
|
|
173
|
+
|---|---|---|
|
|
174
|
+
| `state_marker_count [category] [since-days]` | int | `extract-session-telemetry.sh` (WU-5); `40-gate-compliance.sh` (WU-6). v0 since-days = mtime-based; v1 will redefine as session-boundary-mapped per canvas v4 SWF mining. Accepts both `.json` (spine) and `.marker` (legacy verify-markers) extensions. |
|
|
175
|
+
| `state_chronicle_list [--recent N]` | newline-separated chronicle IDs (lex order) | `extract-session-telemetry.sh` (WU-5); `/sprint`, `/chronicle` (WU-7). Tries `chronicle/` then `docs/chronicle/`. |
|
|
176
|
+
| `state_chronicle_gate_evidence <id>` | JSON of `gates_fired` frontmatter array | `extract-session-telemetry.sh` (WU-5) — frontmatter-only. Legacy chronicles with body-table Gate Evidence return `[]`; consumers may parse markdown body directly per `accessor-library-discipline.md` §"What this rule allows". |
|
|
177
|
+
| `state_session_recent <N>` | JSON array of recent N sessions | `/sprint`, `/chronicle` (WU-7) |
|
|
178
|
+
| `state_step_for_branch <branch-name>` | JSON of Step | status checks; `/longrun checkpoint` |
|
|
179
|
+
| `state_deferred_action_pending [requires-capability...]` | JSON array of resolvable entries (capability-filtered) | `60-deferred-actions.sh` (WU-6) |
|
|
180
|
+
| `state_deferred_action_list [pending\|completed\|all]` | JSON array of all entries in scope (no capability filter) | `/state` skill (WU-8); ad-hoc inspection |
|
|
181
|
+
| `state_iteration_bet_active` | JSON of currently-active bet (or empty if none) | `/sprint` (WU-7) |
|
|
182
|
+
| `state_iteration_bet_list` | JSON array of all bets (lexicographic order) | `/state` skill (WU-8) |
|
|
183
|
+
| `state_reference_get <type> <id>` | JSON of referenced entity (resolves typed cross-refs) | validator (WU-3); render (WU-7) |
|
|
184
|
+
| `state_reference_check <type> <id>` | exit 0 if exists, 1 if dangling | validator (WU-3) |
|
|
185
|
+
| `state_validate <path>` | empty stdout on success; BLOCK message on stderr if schema violation | `/state` skill (WU-8); ad-hoc operator validation |
|
|
186
|
+
| `state_roadmap_get <slug>` | JSON of roadmap frontmatter | `/sprint`, `/longrun` first-turn orientation (bassclef#337 WU-4) |
|
|
187
|
+
| `state_roadmap_next_pending <slug>` | JSON of next claimable WU (depends_on satisfied + status=pending) | claim_wu's default arg path |
|
|
188
|
+
| `state_platform_get [dot.path]` | JSON object (whole file) or scalar (when dot-path given) | `/provision-deploy-host` (bassclef#457), `/secrets-bootstrap` (bassclef#461), `/scaffold-stack` (bassclef#460), `/bind-subdomain` (existing — V2 reads zone via this rather than per-repo substrate.config); reads sibling-repo `bassclef-platform.yml` |
|
|
189
|
+
| `state_platform_path` | filesystem path string | tooling that displays where state_platform_get reads from without reading |
|
|
190
|
+
| `state_roadmap_claim_wu <slug> [wu-slug] [session-id]` | JSON of claimed WU; advances `last_event` with `task-claimed` (bassclef#349 tail-1) | distributed agent coordination — atomic claim via flock |
|
|
191
|
+
| `state_roadmap_release_wu <slug> <wu-slug> [event] [refs-json]` | JSON of new `last_event`; advances `last_event` with completion | distributed agent coordination — completion |
|
|
192
|
+
| `state_roadmap_abandon_wu <slug> <wu-slug> [reason] [--terminal]` | JSON of new `last_event`; advances `last_event` with `task-abandoned` (bassclef#349 tail-2) | scope-change exit path; default returns to pending, `--terminal` marks abandoned |
|
|
193
|
+
| `state_roadmap_add_task <slug> <wu-slug> <issue> <wu-id> <title> [depends_on-json]` | JSON of new task; advances `last_event` with `task-added` (bassclef#349 tail-3) | proper accessor entry point for populating roadmap task list — replaces hand-edits per `accessor-library-discipline.md` |
|
|
194
|
+
|
|
195
|
+
## Adapter discipline — preserved swap surface
|
|
196
|
+
|
|
197
|
+
Per canvas Stage 3 §"Adapter discipline" (7 exit criteria):
|
|
198
|
+
|
|
199
|
+
1. **Accessor library is the ONLY consumer of storage backend APIs.**
|
|
200
|
+
No skill/hook/rule/script invokes `cat docs/whereami.json` directly
|
|
201
|
+
when an accessor exists. Enforced via `accessor-library-discipline.md`
|
|
202
|
+
rule (paired with this contract; ships in same PR as `lib/state.sh`).
|
|
203
|
+
2. **Accessor surface exposes only operations expressible in BOTH
|
|
204
|
+
backends** (JSON-on-git AND cxdb). No JSON-on-git-specific features
|
|
205
|
+
(e.g., `find`-based glob queries, jq paths in public surface) leak
|
|
206
|
+
into the contract. cxdb-adopter would re-implement these accessors
|
|
207
|
+
against Turn DAG + Blob CAS; surface stays unchanged.
|
|
208
|
+
3. **JSON spine remains writable from cxdb.** When cxdb adoption ships,
|
|
209
|
+
`state_marker_add` etc. continue to write JSON files (cxdb is a
|
|
210
|
+
read-side projection); JSON files in git are still authoritative.
|
|
211
|
+
4. **Contract is schema-first, not implementation-first.** Entity
|
|
212
|
+
definitions live in `standards/state-spine/schemas/`. cxdb's Type
|
|
213
|
+
Registry would be loaded FROM bassclef's schema, not derived.
|
|
214
|
+
5. **A working cxdb fork is maintained** when adopted. (Out of v0 scope.)
|
|
215
|
+
6. **Migration tooling (cxdb → Dolt) is designable from the entity
|
|
216
|
+
model.** (Out of v0 scope; ADR bassclef#300 placeholder.)
|
|
217
|
+
7. **Dolt-as-fallback design is sketched in the cxdb ADR.** (Out of
|
|
218
|
+
v0 scope.)
|
|
219
|
+
|
|
220
|
+
For v0: criteria 1-4 apply now. They constrain `lib/state.sh` design
|
|
221
|
+
even though only criterion 1 is enforceable in v0 (the rule).
|
|
222
|
+
|
|
223
|
+
## Test coverage requirement
|
|
224
|
+
|
|
225
|
+
≥95% test coverage on `lib/state.sh` per step-2 acceptance. Test scaffold
|
|
226
|
+
at `scripts/tests/state.test.sh` per existing bassclef bash-test
|
|
227
|
+
pattern (`scripts/tests/bind-subdomain.test.sh`).
|
|
228
|
+
|
|
229
|
+
Each accessor has at minimum:
|
|
230
|
+
- Happy path (successful return)
|
|
231
|
+
- Not-found path (exit 1, empty stdout)
|
|
232
|
+
- Invalid-input path (exit 2, stderr message)
|
|
233
|
+
- Schema-validation path where applicable (exit 3)
|
|
234
|
+
|
|
235
|
+
## Versioning
|
|
236
|
+
|
|
237
|
+
Accessor surface versioned via this standard. Breaking changes
|
|
238
|
+
(removing accessors, changing return shapes, changing exit codes) are
|
|
239
|
+
v1 changes — ship as `state_v1.sh` alongside `state.sh` during
|
|
240
|
+
deprecation window per Fowler expand-and-contract.
|
|
241
|
+
|
|
242
|
+
Adding new accessors is non-breaking; can land in v0 patches.
|
|
243
|
+
|
|
244
|
+
## Relationship to other standards
|
|
245
|
+
|
|
246
|
+
- `standards/state-spine.md` — entity catalog + storage shapes; this
|
|
247
|
+
contract operates on
|
|
248
|
+
- `standards/branch-stacking.md` — Stack entity uses retarget protocol;
|
|
249
|
+
WU-2 PR will need this on merge
|
|
250
|
+
- `.claude/rules/state-schema-validation.md` (paired with WU-3) — write-time
|
|
251
|
+
validation; this contract's `add`/`set`/`update_status` accessors invoke
|
|
252
|
+
the validator before writing
|
|
253
|
+
- `.claude/rules/accessor-library-discipline.md` (paired with this
|
|
254
|
+
standard) — enforces "no skill/hook/rule reads state directly; all
|
|
255
|
+
access goes through `lib/state.sh`"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
tier: lite
|
|
3
|
+
hint_id: kiss-words
|
|
4
|
+
priority: high
|
|
5
|
+
boundaries: [prompt-submit, skill-end, state-write]
|
|
6
|
+
token_budget: 50
|
|
7
|
+
refresh_interval_turns: 5
|
|
8
|
+
description: Write at grade 8. Cap grade 10. Short sentences. Subject-verb-object. Drop bassclef-only words per standards/bassclef-internal-jargon.md. Keep standard tech words (API, hook, PR, schema).
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Write at grade 8. Cap grade 10. Short sentences. Subject-verb-object. Drop bassclef-only words per standards/bassclef-internal-jargon.md. Keep standard tech words (API, hook, PR, schema).
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
---
|
|
2
|
+
tier: lite
|
|
3
|
+
description: substrate.config.md is the single source of truth for external resource references in any bassclef-substrate repo.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Substrate Config Schema
|
|
7
|
+
|
|
8
|
+
`substrate.config.md` is the **single source of truth** for external
|
|
9
|
+
resource references in any bassclef-substrate repo. Functionally, it
|
|
10
|
+
is to bassclef what `.env` is to runtime code — the one place agents
|
|
11
|
+
look for "where is X" without guessing, without consulting memory, and
|
|
12
|
+
without hardcoding.
|
|
13
|
+
|
|
14
|
+
## Why this exists
|
|
15
|
+
|
|
16
|
+
Before this standard: resource references lived in three places —
|
|
17
|
+
`substrate.config.md`, project memory, and hardcoded in skills. Agents
|
|
18
|
+
had to check all three, confused them routinely, and produced wrong
|
|
19
|
+
pushes (drafts pushed to corpus doc, etc.).
|
|
20
|
+
|
|
21
|
+
Single-source-of-truth eliminates the ambiguity. Skills read only from
|
|
22
|
+
`substrate.config.md`. Memory references resources only by field name,
|
|
23
|
+
never by raw ID.
|
|
24
|
+
|
|
25
|
+
## Typed suffix convention
|
|
26
|
+
|
|
27
|
+
Every resource field in `substrate.config.md` uses a typed suffix so
|
|
28
|
+
the agent never has to interpret the value:
|
|
29
|
+
|
|
30
|
+
| Suffix | Meaning | Examples |
|
|
31
|
+
|--------|---------|----------|
|
|
32
|
+
| `*_doc_id` | Google Doc ID (a string that goes after `/document/d/`) | `journal_doc_id`, `changelog_doc_id`, `brand_corpus_doc_id` |
|
|
33
|
+
| `*_sheet_id` | Google Sheets ID | `metrics_sheet_id` |
|
|
34
|
+
| `*_url` | Full URL (http/https) | `staging_url`, `grafana_url`, `langfuse_url` |
|
|
35
|
+
| `*_host` | Host without scheme | `db_host`, `redis_host` |
|
|
36
|
+
| `*_repo` | GitHub repo reference (`owner/name`) | `parent_repo`, `deploy_repo` |
|
|
37
|
+
| `*_repos` | Comma-separated list of GitHub repo references | `bassclef_evolution_child_repos` |
|
|
38
|
+
| `*_path` | Filesystem path (absolute preferred) | `shared_artifacts_path` |
|
|
39
|
+
| `*_channel_id` | Slack/Discord channel | `alerts_channel_id` |
|
|
40
|
+
| `*_project_id` | Linear/Jira/Asana project | `linear_project_id` |
|
|
41
|
+
| `*_token_name` | **Name of env var** holding a secret (never the secret itself) | `gh_token_name`, `anthropic_key_name` |
|
|
42
|
+
| `*_reviewers` | GitHub usernames (comma-separated) for approval gates | `prod_approval_reviewers` |
|
|
43
|
+
| `*_topology` | Architectural choice per `deployment-topology.md` | `staging_topology` |
|
|
44
|
+
|
|
45
|
+
**Secrets NEVER in `substrate.config.md`.** That file is committed to
|
|
46
|
+
git. Secrets live in `.env` (gitignored) or a secret manager. The
|
|
47
|
+
convention stores the *name* of the env var (e.g.,
|
|
48
|
+
`gh_token_name: GH_TOKEN`) so agents know which variable to read at
|
|
49
|
+
runtime.
|
|
50
|
+
|
|
51
|
+
**Secret lifecycle is tracked separately** in `substrate.secrets.md`
|
|
52
|
+
(schema: `standards/state-spine/schemas/substrate-secrets.schema.json`;
|
|
53
|
+
purpose: `standards/secrets-lifecycle.md`). That file holds rotation
|
|
54
|
+
cadence, expiry dates, runbook paths, and health-check commands —
|
|
55
|
+
references and metadata only, never values. The two files cross-reference
|
|
56
|
+
each other; `substrate.config.md` answers *where* a resource lives,
|
|
57
|
+
`substrate.secrets.md` answers *when* a credential expires and *how* to
|
|
58
|
+
rotate it.
|
|
59
|
+
|
|
60
|
+
## Single-source-of-truth rule
|
|
61
|
+
|
|
62
|
+
1. **External resource IDs live ONLY in `substrate.config.md`.** Any
|
|
63
|
+
Google Doc ID, URL, repo reference, or similar pointer is a field
|
|
64
|
+
in this file.
|
|
65
|
+
2. **Project memory references resources ONLY by field name**, never
|
|
66
|
+
by raw value. `brand_corpus_doc_id` is a valid memory reference;
|
|
67
|
+
`1gps7mmEYBCud...` is not.
|
|
68
|
+
3. **Skills and hooks read ONLY from `substrate.config.md`** for
|
|
69
|
+
resource lookups. No hardcoded IDs in skill files, hook scripts,
|
|
70
|
+
or agent definitions.
|
|
71
|
+
4. **If an agent needs a resource that isn't in
|
|
72
|
+
`substrate.config.md`**, the agent prompts the operator to add it
|
|
73
|
+
with a typed field name. Do not hardcode. Do not cache in memory.
|
|
74
|
+
5. **Scaffolding on new repos prompts for each known field** during
|
|
75
|
+
`/autonomous start` setup.
|
|
76
|
+
|
|
77
|
+
## Known fields (extensible)
|
|
78
|
+
|
|
79
|
+
These fields are recognized by bassclef skills. Not all are required;
|
|
80
|
+
uncommented defaults shown.
|
|
81
|
+
|
|
82
|
+
```yaml
|
|
83
|
+
# === Artifacts ===
|
|
84
|
+
journal_doc_id: [per-repo draft Google Doc — pushes from /journal + session-end]
|
|
85
|
+
changelog_doc_id: [per-repo weekly changelog Google Doc — pushes from /release-notes]
|
|
86
|
+
brand_corpus_doc_id: [cross-repo brand team corpus — pushes from /journal-export]
|
|
87
|
+
|
|
88
|
+
# === Environments ===
|
|
89
|
+
deploy_targets:
|
|
90
|
+
local: true # always available
|
|
91
|
+
staging: false # URL when configured
|
|
92
|
+
prod: false # URL when configured (hard gate — human-approved)
|
|
93
|
+
|
|
94
|
+
# === Integrations (optional, per-project) ===
|
|
95
|
+
langfuse_url: [tracing dashboard URL, if used]
|
|
96
|
+
grafana_url: [metrics dashboard URL, if used]
|
|
97
|
+
linear_project_id: [Linear project for ticket sync, if used]
|
|
98
|
+
|
|
99
|
+
# === Bassclef substrate (bassclef repo only) ===
|
|
100
|
+
# Consumer repos scanned by session-reflection for bassclef-evolution
|
|
101
|
+
# issues. Keep in sync with .github/workflows/bassclef-template-sync.yml
|
|
102
|
+
# matrix. Fallback when field absent: poa only.
|
|
103
|
+
bassclef_evolution_child_repos: [comma-separated list of owner/name]
|
|
104
|
+
|
|
105
|
+
# === Budget ===
|
|
106
|
+
budget:
|
|
107
|
+
session_ceiling: [API burst $/session]
|
|
108
|
+
iteration_ceiling: [API burst $/iteration]
|
|
109
|
+
warning_threshold: 75%
|
|
110
|
+
|
|
111
|
+
# === Secret references (names only, not values) ===
|
|
112
|
+
anthropic_key_name: ANTHROPIC_API_KEY # env var name, not the key
|
|
113
|
+
gh_token_name: GH_TOKEN # env var name, not the token
|
|
114
|
+
|
|
115
|
+
# === Hosting platform (REQUIRED in any consumer that ships a UI) ===
|
|
116
|
+
#
|
|
117
|
+
# Drives which deploy_* fields apply AND which adapter `/bind-subdomain`
|
|
118
|
+
# loads. Every consumer running `/preview-build` or shipping production
|
|
119
|
+
# UI MUST set this — the bassclef default is `amplify` per ADR-008
|
|
120
|
+
# (frontend-stack) + POA + quorum precedent (single-provider portfolio
|
|
121
|
+
# reduces operator cognitive load; AWS-native; cheap port to existing
|
|
122
|
+
# AWS auth).
|
|
123
|
+
#
|
|
124
|
+
# Enum values (all five are valid; not all have shipped adapters yet):
|
|
125
|
+
# - amplify: use amplify_app_id + amplify_region + amplify_branch
|
|
126
|
+
# (bassclef-recommended default; adapters live)
|
|
127
|
+
# - ec2-tailscale: use deploy_host + deploy_user + deploy_path (POA pattern)
|
|
128
|
+
# - vercel: adapter not yet shipped — schema reserves the slot
|
|
129
|
+
# - netlify: adapter not yet shipped — schema reserves the slot
|
|
130
|
+
# - cloudflare-pages: adapter not yet shipped — schema reserves the slot
|
|
131
|
+
# - none: consumer explicitly opts out of preview/prototype
|
|
132
|
+
# deployment. `/bind-subdomain` and `/launch-preview`
|
|
133
|
+
# refuse on `none` unless --override given. Use for
|
|
134
|
+
# docs-only repos (e.g., bassclef itself) or repos
|
|
135
|
+
# that ship no UI surface.
|
|
136
|
+
hosting_platform: amplify # bassclef default; pick from enum above
|
|
137
|
+
|
|
138
|
+
# --- Production tier (optional; declare when production differs from prototype) ---
|
|
139
|
+
#
|
|
140
|
+
# Per-tier hosting was added in bassclef#458 to resolve the POA case:
|
|
141
|
+
# POA's prototypes deploy to amplify, but POA's production runs on
|
|
142
|
+
# ec2-tailscale (per ADR-009) and predates the prototype pipeline.
|
|
143
|
+
# Without `production_hosting_platform`, hosting_platform=amplify
|
|
144
|
+
# implies POA's whole app is on amplify — wrong.
|
|
145
|
+
#
|
|
146
|
+
# `production_hosting_platform` is DECLARATIVE. /launch-preview and
|
|
147
|
+
# /bind-subdomain operate on the prototype tier only and do not read
|
|
148
|
+
# this field. It documents the production tier so operators (and
|
|
149
|
+
# future tooling) know the production deploy is a separate concern.
|
|
150
|
+
#
|
|
151
|
+
# When unset, production tier inherits hosting_platform.
|
|
152
|
+
production_hosting_platform: ec2-tailscale # only set when production differs from prototype
|
|
153
|
+
|
|
154
|
+
# --- Amplify-specific (when hosting_platform=amplify) ---
|
|
155
|
+
#
|
|
156
|
+
# `amplify_app_id` is the provisioning-indicator field (bassclef#523).
|
|
157
|
+
# Set by `/provision-deploy-host` on success when the Amplify app is
|
|
158
|
+
# created. Composer pre-flight (lib/composer-preflight.sh Check 6) reads
|
|
159
|
+
# this field to verify provisioning happened — when missing or set to a
|
|
160
|
+
# bracketed placeholder shape, /preview-build is BLOCKED with three-path
|
|
161
|
+
# remediation (run /provision-deploy-host inline / re-dispatch with
|
|
162
|
+
# --no-launch / explicit defer). Replaces the silent-defer-bind class
|
|
163
|
+
# that caused POA's 2026-05-05 mobile session to merge variants but
|
|
164
|
+
# never bind a subdomain.
|
|
165
|
+
amplify_app_id: [e.g., d154l5usyc80nk]
|
|
166
|
+
amplify_region: [e.g., eu-north-1]
|
|
167
|
+
amplify_branch: [branch bound to domain, default: main]
|
|
168
|
+
|
|
169
|
+
# --- EC2 + Tailscale-specific (when hosting_platform=ec2-tailscale) ---
|
|
170
|
+
# See standards/deployment-topology.md for the SENTINEL_bassclef_pattern.
|
|
171
|
+
#
|
|
172
|
+
# `deploy_host` is the provisioning-indicator field (bassclef#523) —
|
|
173
|
+
# Composer pre-flight Check 6 reads it to verify provisioning happened
|
|
174
|
+
# (parallel to amplify_app_id for amplify). Same three-path remediation
|
|
175
|
+
# applies when missing.
|
|
176
|
+
deploy_host: [Tailscale hostname of prod box]
|
|
177
|
+
deploy_user: [SSH user on prod box]
|
|
178
|
+
deploy_path: [absolute path on box, e.g., /opt/poa]
|
|
179
|
+
staging_deploy_host: [same or different Tailscale host]
|
|
180
|
+
staging_deploy_path: [absolute path, e.g., /opt/poa-staging]
|
|
181
|
+
staging_topology: same-box | separate-box # Option A or B
|
|
182
|
+
tailscale_tag: [CI tag, e.g., tag:ci]
|
|
183
|
+
backup_path: [absolute path, e.g., /opt/backups]
|
|
184
|
+
prod_approval_reviewers: [GitHub usernames comma-separated]
|
|
185
|
+
|
|
186
|
+
# === Domain + DNS (when app binds a friendly URL) ===
|
|
187
|
+
# See standards/domain-and-dns.md. Optional — skip if using platform
|
|
188
|
+
# default URL (e.g., *.amplifyapp.com). Binding is automated via the
|
|
189
|
+
# /bind-subdomain skill.
|
|
190
|
+
subdomain: [e.g., myapp.apps.example.com]
|
|
191
|
+
dns_provider: [cloudflare | route53] # v1 supports cloudflare
|
|
192
|
+
dns_zone: [e.g., example.com]
|
|
193
|
+
cloudflare_token_env: [env var name holding CF API token, e.g., CLOUDFLARE_API_TOKEN]
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Migration from anti-patterns
|
|
197
|
+
|
|
198
|
+
### Anti-pattern 1: raw IDs in project memory
|
|
199
|
+
|
|
200
|
+
**Before**:
|
|
201
|
+
```markdown
|
|
202
|
+
# memory/reference_brand_corpus.md
|
|
203
|
+
The brand team corpus lives at 1gps7mmEYBCud462Pn_DNf2mXa5Bj58NMjXUUDqWGIh0.
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**After**:
|
|
207
|
+
```markdown
|
|
208
|
+
# memory/reference_brand_corpus.md
|
|
209
|
+
The brand team corpus is configured as `brand_corpus_doc_id` in
|
|
210
|
+
substrate.config.md. Agents should always read from there, never
|
|
211
|
+
reference the raw ID directly.
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Anti-pattern 2: hardcoded IDs in skills
|
|
215
|
+
|
|
216
|
+
**Before** (in `.claude/skills/journal-export/SKILL.md`):
|
|
217
|
+
```bash
|
|
218
|
+
npx tsx scripts/push-to-gdoc.ts \
|
|
219
|
+
--file docs/journal-corpus/corpus.md \
|
|
220
|
+
--doc-id 1gps7mmEYBCud462Pn_DNf2mXa5Bj58NMjXUUDqWGIh0
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**After**:
|
|
224
|
+
```bash
|
|
225
|
+
DOC_ID=$(grep "^brand_corpus_doc_id:" substrate.config.md | awk '{print $2}')
|
|
226
|
+
npx tsx scripts/push-to-gdoc.ts \
|
|
227
|
+
--file docs/journal-corpus/corpus.md \
|
|
228
|
+
--doc-id "$DOC_ID"
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Or equivalently, skills document the field name in prose and require
|
|
232
|
+
the agent to look it up at runtime:
|
|
233
|
+
|
|
234
|
+
```markdown
|
|
235
|
+
## Step 7: Push to Google Docs
|
|
236
|
+
|
|
237
|
+
Push to `brand_corpus_doc_id` from `substrate.config.md`:
|
|
238
|
+
|
|
239
|
+
\`\`\`bash
|
|
240
|
+
npx tsx scripts/push-to-gdoc.ts --file docs/journal-corpus/corpus.md \
|
|
241
|
+
--doc-id $(grep "^brand_corpus_doc_id:" substrate.config.md | awk '{print $2}')
|
|
242
|
+
\`\`\`
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Anti-pattern 3: scattered fields
|
|
246
|
+
|
|
247
|
+
If an app repo adds a new resource (e.g., a Retool dashboard URL)
|
|
248
|
+
and stores it in a README comment, an inline skill comment, or a
|
|
249
|
+
hook variable — that's the anti-pattern. All such values go in
|
|
250
|
+
`substrate.config.md` with the typed suffix.
|
|
251
|
+
|
|
252
|
+
## Related rules
|
|
253
|
+
|
|
254
|
+
- `.claude/rules/substrate-config-schema.md` — enforces this standard
|
|
255
|
+
at agent behavior level (read from config, don't hardcode, don't
|
|
256
|
+
cache in memory)
|
|
257
|
+
- `.claude/skills/autonomous/SKILL.md` — prompts for each known field
|
|
258
|
+
during scaffold
|
|
259
|
+
- `.claude/skills/substrate-check/SKILL.md` — validates substrate.config.md
|
|
260
|
+
has required fields for the repo type
|
|
261
|
+
|
|
262
|
+
## Versioning
|
|
263
|
+
|
|
264
|
+
When bassclef adds a new known field type, the schema here is the
|
|
265
|
+
authoritative list. App repos update their `substrate.config.md` to
|
|
266
|
+
add the new field during next `/autonomous start` scaffold pass (the
|
|
267
|
+
skill surfaces missing fields and prompts for values).
|
|
@@ -144,7 +144,7 @@ Bucket 2 outputs from single-tier and combined-tier agree for that tier's legit
|
|
|
144
144
|
- Consumed by `.claude/skills/tier-dependency-audit/SKILL.md` — user-invocable wrapper
|
|
145
145
|
- Composed with `scripts/release-to-bassclef.sh` — the ACTUAL set comes from `--dry-run`
|
|
146
146
|
- Composed with `.claude/rules/we-dont-break-adopters.md` (ADR-031) — the audit surfaces adopter-observable classes for careful handling
|
|
147
|
-
- Composed with `.claude/rules/mechanism-fidelity.md` — the subsystem IS the mechanism paired with tier-tag-schema
|
|
147
|
+
- Composed with `.claude/rules/mechanism-fidelity.md` (standard tier) — the subsystem IS the mechanism paired with tier-tag-schema
|
|
148
148
|
|
|
149
149
|
## Anchor luminaries
|
|
150
150
|
|
|
@@ -117,7 +117,7 @@ Prior default-ship rule for non-md/sh files (superseded by this table 2026-08-20
|
|
|
117
117
|
|
|
118
118
|
Files whose path matches an entry in `standards/path-exclusion-registry.json` are excluded from every release regardless of any inner `tier:` value. The path wins. Consumers (release script, audit tools) read the registry as the single source of truth for path-based exclusion classes: `session-runtime`, `operator-private`, `test-only`, `operator-internal-rd`, `prototype`, `config-per-repo`, `ephemeral-transient`.
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
The path exclusion registry (operator-only) tracks classes + rationale per entry + how to add a new exclusion.
|
|
121
121
|
|
|
122
122
|
The path-exclusion registry supersedes the prior inline `case` statement in `scripts/release-to-bassclef.sh` L415-460. The refactor to read the registry ships as a separate PR after this schema amendment.
|
|
123
123
|
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
tier: lite
|
|
3
|
+
description: "Bassclef's config splits into two layers."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Two-Layer Config — Shared vs Operator
|
|
7
|
+
|
|
8
|
+
Bassclef's config splits into two layers. The **shared layer** is committed and identity-agnostic — every adopter sees it. The **operator layer** is gitignored and per-person — never committed, never shipped to adopters.
|
|
9
|
+
|
|
10
|
+
This standard exists because the line between "what bassclef needs" and "what one operator has set up" was blurry before the 2026-05-23 cold-adopter run. Operator-specific paths, allow lists, and preferences leaked into shared config. Adopters would have inherited the operator's identity on first clone.
|
|
11
|
+
|
|
12
|
+
## The two layers
|
|
13
|
+
|
|
14
|
+
### Shared layer (committed)
|
|
15
|
+
|
|
16
|
+
- `.claude/settings.json`
|
|
17
|
+
- `CLAUDE.md`
|
|
18
|
+
- `README.md`
|
|
19
|
+
- All skills, hooks, rules, standards under `.claude/` and root dirs
|
|
20
|
+
- All ADRs, decompositions, design docs
|
|
21
|
+
|
|
22
|
+
**Rules for the shared layer:**
|
|
23
|
+
|
|
24
|
+
1. Identity-agnostic. No absolute paths to specific home directories.
|
|
25
|
+
2. No personal allow-list entries (broad npm grants, macOS process control, etc.).
|
|
26
|
+
3. No "sunj-labs" references except in attribution.
|
|
27
|
+
4. Tested against cold-adopter personas (`strategy/personas/`) before publication.
|
|
28
|
+
|
|
29
|
+
### Operator layer (gitignored)
|
|
30
|
+
|
|
31
|
+
- `.claude/settings.local.json`
|
|
32
|
+
- `CLAUDE.local.md` (optional, if operator wants per-machine context)
|
|
33
|
+
- `.env.local`
|
|
34
|
+
- Anything ending in `.local`
|
|
35
|
+
|
|
36
|
+
**Rules for the operator layer:**
|
|
37
|
+
|
|
38
|
+
1. Holds operator-specific allow lists, machine paths, personal aliases.
|
|
39
|
+
2. Never committed. `.gitignore` enforces this by listing the files explicitly.
|
|
40
|
+
3. Each contributor creates their own. The shared `.gitignore` block tells them which files to create.
|
|
41
|
+
4. Where convenience accumulates — broad `Bash(rm *)` grants, machine-specific paths, etc.
|
|
42
|
+
|
|
43
|
+
## Mechanical enforcement
|
|
44
|
+
|
|
45
|
+
`.claude/hooks/pre-commit-gate.sh` includes a CCF-3 absolute-path guard. It runs on every `git commit` and BLOCKs when any staged non-exempt file contains `/Users/<name>` or `/home/<name>` patterns.
|
|
46
|
+
|
|
47
|
+
**Exempt paths** (where these patterns are legitimate — documentation, audits, test fixtures):
|
|
48
|
+
|
|
49
|
+
- `.claude/rules/`, `.claude/hooks/`, `.claude/skills/`, `.claude/luminaries/`, `.claude/agents/`
|
|
50
|
+
- `standards/`, `architecture/`, `design/`
|
|
51
|
+
- `docs/` (all subdirs)
|
|
52
|
+
- `chronicle/`, `strategy/`
|
|
53
|
+
- `scripts/tests/`
|
|
54
|
+
|
|
55
|
+
**Non-exempt** (where the guard fires): everything else. Most importantly `.claude/settings.json`, top-level configs, source code under `src/` or `lib/`.
|
|
56
|
+
|
|
57
|
+
When the guard fires, it points to `.claude/settings.local.json` as the right home for the offending entry.
|
|
58
|
+
|
|
59
|
+
## Override
|
|
60
|
+
|
|
61
|
+
`SKIP_OPERATOR_PATHS=1 git commit` — logged via `trace-helper.sh`. Use only for genuine cases where the absolute path must live in the shared layer (rare). Document the rationale in the commit message.
|
|
62
|
+
|
|
63
|
+
## What this fixes
|
|
64
|
+
|
|
65
|
+
Before this standard:
|
|
66
|
+
|
|
67
|
+
- `.claude/settings.json` shipped `Bash(rm /Users/<operator>/src/<org>/bassclef/*)` — F70 in the 2026-05-23 cold-adopter run. Every public clone would have inherited that operator-identity line.
|
|
68
|
+
- Allow-list entries reflected operator workflow rather than adopter safety — F73 in the same run.
|
|
69
|
+
|
|
70
|
+
After this standard:
|
|
71
|
+
|
|
72
|
+
- F70 is removed from the committed `.claude/settings.json`.
|
|
73
|
+
- The hook prevents the class from recurring.
|
|
74
|
+
- New operator-convenience entries go to `.claude/settings.local.json`.
|
|
75
|
+
|
|
76
|
+
## When to add a new field to the operator layer
|
|
77
|
+
|
|
78
|
+
Any of these signals mean it belongs in `settings.local.json`, not `settings.json`:
|
|
79
|
+
|
|
80
|
+
- Contains an absolute path matching `/Users/<name>` or `/home/<name>`.
|
|
81
|
+
- Grants a broad permission (`Bash(rm *)`, `Bash(npm*)`, `Bash(pkill*)`) that an adopter on a first session would not be expected to want.
|
|
82
|
+
- References operator-specific tools or paths (`/Applications/Tailscale.app/...`).
|
|
83
|
+
- Encodes machine-specific config (hostnames, ports tied to a specific dev setup).
|
|
84
|
+
|
|
85
|
+
When in doubt, default to the operator layer. It's cheaper to promote a setting from `settings.local.json` to `settings.json` later than to ship operator identity to every adopter.
|
|
86
|
+
|
|
87
|
+
## Sources read
|
|
88
|
+
|
|
89
|
+
- `docs/adoption-runs/2026-05-23-cold-adopter-1.md` — the cold-adopter run record (PR #735)
|
|
90
|
+
- Operator handoff CCF-3 + Step 5 Issue 4 — the policy specification
|
|
91
|
+
- F70 (operator path leak in committed `.claude/settings.json`)
|
|
92
|
+
- F73 (allow-list grants reflect operator workflow rather than adopter safety)
|
|
93
|
+
- `.claude/hooks/pre-commit-gate.sh` — the mechanical enforcement
|
|
94
|
+
- `.claude/rules/blocked-items.md` — the resolve-or-defer protocol the BLOCKED signal slots into
|
|
95
|
+
|
|
96
|
+
## Closes
|
|
97
|
+
|
|
98
|
+
- bassclef#740 (CCF-3 — two-layer config policy)
|
|
99
|
+
- bassclef#738 (Pre-launch cleanup — F70 instance)
|