create-substrat 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +7 -3
- package/package.json +2 -2
- package/template/.substrat/playbook.md +105 -29
- package/template/AGENTS.md +3 -2
package/index.js
CHANGED
|
@@ -25,7 +25,7 @@ const SUBSTRAT = '^0.29.0';
|
|
|
25
25
|
const ENGINES = '^0.3.27';
|
|
26
26
|
const BOUNDARY_LINT = '^0.0.5';
|
|
27
27
|
|
|
28
|
-
const DOCS = 'https://substrat.
|
|
28
|
+
const DOCS = 'https://substrat.net';
|
|
29
29
|
|
|
30
30
|
function fail(message) {
|
|
31
31
|
process.stderr.write(`\n create-substrat: ${message}\n\n`);
|
|
@@ -167,9 +167,13 @@ pnpm typecheck
|
|
|
167
167
|
|
|
168
168
|
function main() {
|
|
169
169
|
const target = process.argv[2];
|
|
170
|
-
if (
|
|
170
|
+
if (target === '-h' || target === '--help') {
|
|
171
171
|
usage();
|
|
172
|
-
process.exit(
|
|
172
|
+
process.exit(0);
|
|
173
|
+
}
|
|
174
|
+
if (!target) {
|
|
175
|
+
usage();
|
|
176
|
+
fail('a target directory is required — e.g. `npm create substrat my-app` (or `.` for here).');
|
|
173
177
|
}
|
|
174
178
|
|
|
175
179
|
const dest = resolve(target);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-substrat",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Scaffold a Substrat vertical — `npm create substrat
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Scaffold a Substrat vertical — `npm create substrat <dir>`.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -1,21 +1,33 @@
|
|
|
1
1
|
# Playbook — build a vertical on Substrat
|
|
2
2
|
|
|
3
3
|
The always-on rules live in [`AGENTS.md`](../AGENTS.md); read them first. This playbook is
|
|
4
|
-
the **flow**: interview the user, tell them honestly how much of their app already exists,
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
the **flow**: interview the user, tell them honestly how much of their app already exists, and
|
|
5
|
+
**land a design document they can review and approve before a line of code is written** — then
|
|
6
|
+
reshape the reference into it. Read the whole thing before starting — both the design gate
|
|
7
|
+
(Step 4) and the checkpoints (Step 7) are hard stops.
|
|
8
|
+
|
|
9
|
+
**The target is a reviewed design, not running code.** Steps 1–2 learn the domain and map it
|
|
10
|
+
onto what already exists; Step 3 writes a checked-in `DESIGN.md` in the user's own vocabulary;
|
|
11
|
+
Step 4 is a **hard stop** where the user reads and approves it. Only then does Step 5 reshape
|
|
12
|
+
the reference into their domain. The design gate (Step 4) is *upstream* of the two code
|
|
13
|
+
checkpoints (Step 7) — a user with zero Substrat knowledge gets to say "yes, that's the app I
|
|
14
|
+
want" before implementation, not after.
|
|
7
15
|
|
|
8
16
|
This project ships with a small **working reference vertical** — a bike-repair shop on
|
|
9
|
-
`engine-workorder` + `engine-invoicing`, green out of the box (`npm test`). It is your
|
|
10
|
-
|
|
11
|
-
than building from an empty directory. Work in the project root.
|
|
17
|
+
`engine-workorder` + `engine-invoicing`, green out of the box (`npm test`). It is your worked
|
|
18
|
+
example and your build starting point: once the design is approved you **reshape** it into the
|
|
19
|
+
user's domain rather than building from an empty directory. Work in the project root.
|
|
12
20
|
|
|
13
21
|
---
|
|
14
22
|
|
|
15
23
|
## Step 1 — Interview
|
|
16
24
|
|
|
17
|
-
Ask, don't assume. **Three to five questions, conversational, one message.** You are
|
|
18
|
-
|
|
25
|
+
Ask, don't assume. **Three to five questions, conversational, one message.** You are learning
|
|
26
|
+
the *shape* of the domain — the answers become the design document (Step 3), so listen for
|
|
27
|
+
vocabulary, the cast, and who must be denied what, not just features. **Adapt depth to the
|
|
28
|
+
user**: someone who already knows their domain cold needs fewer, sharper questions; someone
|
|
29
|
+
thinking out loud needs you to draw the shape out. One flow, not branching tracks — read the
|
|
30
|
+
room and dial the teaching up or down.
|
|
19
31
|
|
|
20
32
|
1. **What are you building, and who uses it?** (the firm, the cast)
|
|
21
33
|
2. **What's the thing that moves through the system?** A job, a repair, an inspection, an
|
|
@@ -34,8 +46,10 @@ detail, skip to Step 2 and confirm your reading of it instead of re-asking.
|
|
|
34
46
|
## Step 2 — The coverage map
|
|
35
47
|
|
|
36
48
|
**This is the most valuable thing you do, and the easiest to get wrong by being
|
|
37
|
-
flattering.**
|
|
38
|
-
|
|
49
|
+
flattering.** Work out what already exists and what the user is actually signing up to build.
|
|
50
|
+
Be specific and honest. This analysis is the analytical core of the design document (Step 3) —
|
|
51
|
+
sections 3 ("what already exists vs. what's yours") and 4 ("who is denied what") are this map,
|
|
52
|
+
written down.
|
|
39
53
|
|
|
40
54
|
**First, list the engines that actually exist. Do not trust any hard-coded list:**
|
|
41
55
|
|
|
@@ -128,25 +142,86 @@ stop. Do not scaffold.
|
|
|
128
142
|
|
|
129
143
|
---
|
|
130
144
|
|
|
131
|
-
## Step 3 —
|
|
145
|
+
## Step 3 — Write the design document
|
|
132
146
|
|
|
133
|
-
|
|
147
|
+
**This is the deliverable.** Everything before now was learning; this is where it lands
|
|
148
|
+
somewhere the user can hold. Write a **checked-in `DESIGN.md`** in the project root, in the
|
|
149
|
+
user's own vocabulary — no Substrat internals, no decision refs, no cross-references to
|
|
150
|
+
platform docs. Someone who has never heard of Substrat must be able to read it and recognise
|
|
151
|
+
their own business.
|
|
134
152
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
153
|
+
**Top line, verbatim** — the house marker for a pre-code design:
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
Status: draft v0.1 · Last updated: <date> · For review before any code
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**The template** — the coverage map (Step 2) is sections 3–4, already done; the rest is the
|
|
160
|
+
interview written down. Two sections deliberately *preview* the code checkpoints of Step 7 in
|
|
161
|
+
plain language, so nothing there is a surprise:
|
|
162
|
+
|
|
163
|
+
1. **What we're building & who uses it** — the firm and the cast, one paragraph.
|
|
164
|
+
2. **The thing that moves through the system** — the core noun and its lifecycle
|
|
165
|
+
(the states it passes through, and which transitions must not be skippable).
|
|
166
|
+
3. **What already exists vs. what's yours** — the coverage map as tiers: the kernel
|
|
167
|
+
(free), the engines you compose, the connectors, and the Tier-3 vocabulary/pricing/
|
|
168
|
+
screens that are yours. If it's a **bad fit, this is where the honest no lands** — say
|
|
169
|
+
so and stop; do not write the rest.
|
|
170
|
+
4. **Who is denied what** — the load-bearing section, and a plain-language *preview of the
|
|
171
|
+
permission diff*: each role and what it can and cannot see. Make two answers impossible
|
|
172
|
+
to miss — **who can see the money, and who can see other customers' data.**
|
|
173
|
+
5. **Money & sign-off** — invoice / quote / receipt / none; anything gated on a signature
|
|
174
|
+
or a check before a step can happen.
|
|
175
|
+
6. **The cast, roles, and tenancy** — the named roles per persona (roles are the user's
|
|
176
|
+
vocabulary — name them for the persona: `workshop-admin`, not `role_1`). **Two tenants,
|
|
177
|
+
always** — the second exists to be attacked, which is how isolation gets proven rather
|
|
178
|
+
than claimed.
|
|
179
|
+
7. **The data we'll store** — the vertical's own tables and fields in plain terms. This
|
|
180
|
+
*previews the migration diff*; migrations are **append-only forever after first ship**,
|
|
181
|
+
so this is the cheap moment to get the shape right.
|
|
182
|
+
8. **The scenario the test will replay** — the happy path plus the denials that prove
|
|
183
|
+
isolation (wrong role denied, customer A sees theirs and customer B sees nothing, a
|
|
184
|
+
cross-tenant attacker gets nothing).
|
|
185
|
+
9. **Open decisions** — each with a **recommended default**, so the user chooses rather
|
|
186
|
+
than specifies:
|
|
187
|
+
- **Auth.** Local dev uses an `x-principal` header — a dev seam, not a login. Default to
|
|
188
|
+
it and note it must be replaced before anything real; offer to wire a real login (the
|
|
189
|
+
bike-shop reference shows the Better Auth pattern) now if they want it. Real auth gates
|
|
190
|
+
*exposing* the app, not *building* it.
|
|
191
|
+
- **Deploy or stay local.** Local-first is a legitimate endpoint; default to it.
|
|
192
|
+
10. **Out of scope / deferred** — what you are deliberately not building, so the review is
|
|
193
|
+
about a bounded thing.
|
|
194
|
+
|
|
195
|
+
End with a short **"Review questions for the human"** block (2–3 questions) — the things the
|
|
196
|
+
user must actively confirm, not rubber-stamp.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Step 4 — The design gate. STOP HERE.
|
|
201
|
+
|
|
202
|
+
**Present the design document and wait for approval. Do not reshape the reference, do not
|
|
203
|
+
write code.**
|
|
204
|
+
|
|
205
|
+
This is a *human* gate, and it is the whole point: it happens **before** any code, upstream of
|
|
206
|
+
the two implementation checkpoints in Step 7. Walk the user through section 4 ("who is denied
|
|
207
|
+
what") in **their own vocabulary** until they can answer, without your help: *who can see the
|
|
208
|
+
money, and who can see other customers' data?*
|
|
209
|
+
|
|
210
|
+
**A gate assumes a competent reviewer.** If the user cannot evaluate the permission preview,
|
|
211
|
+
say so rather than letting them wave it through — a design nobody understands is theater, and
|
|
212
|
+
reproduces exactly the failure Substrat exists to prevent. Iterate the document until they
|
|
213
|
+
can, and only then take explicit approval.
|
|
214
|
+
|
|
215
|
+
Approval of the design is what unlocks Step 5. Until you have it, you are still in design.
|
|
142
216
|
|
|
143
217
|
---
|
|
144
218
|
|
|
145
|
-
## Step
|
|
219
|
+
## Step 5 — Reshape the reference
|
|
146
220
|
|
|
147
|
-
The scaffold already contains a working vertical in `src/` + `test/` —
|
|
148
|
-
**Read it first** (it's your Callout: the real, green implementation of
|
|
149
|
-
step describes), then reshape it into the user's domain from the
|
|
221
|
+
The design is approved. The scaffold already contains a working vertical in `src/` + `test/` —
|
|
222
|
+
the bike-repair shop. **Read it first** (it's your Callout: the real, green implementation of
|
|
223
|
+
every pattern this step describes), then reshape it into the user's domain from the approved
|
|
224
|
+
`DESIGN.md`:
|
|
150
225
|
|
|
151
226
|
- **Rename the vocabulary** — `shop_customers`/`shop_bikes` → the user's nouns, the `shop/*`
|
|
152
227
|
operation names, the roles, the price-list shape. If the user's core noun maps onto a work
|
|
@@ -159,7 +234,7 @@ step describes), then reshape it into the user's domain from the interview:
|
|
|
159
234
|
- **Drop what the domain doesn't need, add its own tables** for anything the engines don't
|
|
160
235
|
own. If the user's core noun *isn't* work-order-shaped, you may replace more of `src/` —
|
|
161
236
|
but the seed/server/test scaffolding and the layout still hold.
|
|
162
|
-
- **Re-run the gates as you go** (Step
|
|
237
|
+
- **Re-run the gates as you go** (Step 6) — the reference is green, so any red is something
|
|
163
238
|
you just changed.
|
|
164
239
|
|
|
165
240
|
The dependencies are already wired in `package.json` (the `@substrat-run/*` packages, `hono`,
|
|
@@ -233,7 +308,7 @@ closed-door assertion with a control proving a neighbouring door is still open.
|
|
|
233
308
|
|
|
234
309
|
---
|
|
235
310
|
|
|
236
|
-
## Step
|
|
311
|
+
## Step 6 — Run it
|
|
237
312
|
|
|
238
313
|
Build confidence in this order, and **show the user the output of each**:
|
|
239
314
|
|
|
@@ -255,9 +330,10 @@ and typed wrappers over the routes. Ask first — it roughly doubles the work.
|
|
|
255
330
|
|
|
256
331
|
---
|
|
257
332
|
|
|
258
|
-
## Step
|
|
333
|
+
## Step 7 — The two checkpoints. STOP HERE.
|
|
259
334
|
|
|
260
|
-
**You may never self-approve these. Present them and wait.**
|
|
335
|
+
**You may never self-approve these. Present them and wait.** The design gate (Step 4) already
|
|
336
|
+
took the user's approval of *what* to build; these confirm that the code matches it.
|
|
261
337
|
|
|
262
338
|
1. **Migration diff** — every new `SqlMigration`, verbatim. Append-only forever once
|
|
263
339
|
shipped, so this is the last cheap moment to change your mind.
|
|
@@ -275,7 +351,7 @@ and who can see other tenants' data?* A permission diff nobody understands is th
|
|
|
275
351
|
|
|
276
352
|
---
|
|
277
353
|
|
|
278
|
-
## Step
|
|
354
|
+
## Step 8 — Deploy (optional)
|
|
279
355
|
|
|
280
356
|
Only if the user asks. Local-first is a legitimate stopping point.
|
|
281
357
|
|
|
@@ -301,7 +377,7 @@ cross-tenant hole with a UI.
|
|
|
301
377
|
|
|
302
378
|
---
|
|
303
379
|
|
|
304
|
-
## Step
|
|
380
|
+
## Step 9 — Leave the project competent
|
|
305
381
|
|
|
306
382
|
The next session — in any tool — starts cold. The scaffold already ships `AGENTS.md`,
|
|
307
383
|
`CLAUDE.md`, and the Cursor/opencode command stubs, so the rules and this flow survive. Your
|
package/template/AGENTS.md
CHANGED
|
@@ -5,8 +5,9 @@ Substrat kernel and its engines. This file is the always-on constitution — the
|
|
|
5
5
|
that hold no matter what you touch. It is read by every AI tool (Claude Code, Cursor,
|
|
6
6
|
opencode); do not duplicate it into tool-specific config.
|
|
7
7
|
|
|
8
|
-
The full build flow — interview, coverage map,
|
|
9
|
-
|
|
8
|
+
The full build flow — interview, coverage map, a reviewed design document you approve
|
|
9
|
+
before any code, then reshape, run, and the checkpoints — is a **playbook**, not always-on
|
|
10
|
+
context. Invoke it when you start or extend a vertical:
|
|
10
11
|
|
|
11
12
|
- **Claude Code**: `/substrat`
|
|
12
13
|
- **Cursor / opencode**: the `new-vertical` command, or read [`.substrat/playbook.md`](.substrat/playbook.md)
|