@pikku/skills 0.12.57 → 0.12.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dist/skills.gen.js +2 -2
  2. package/package.json +10 -7
  3. package/skills/pikku-a11y/SKILL.md +5 -3
  4. package/skills/pikku-addon/SKILL.md +8 -9
  5. package/skills/pikku-agent/SKILL.md +16 -13
  6. package/skills/pikku-agent/references/runner-vercel.md +1 -1
  7. package/skills/pikku-architect/SKILL.md +7 -10
  8. package/skills/pikku-auth/SKILL.md +16 -20
  9. package/skills/pikku-auth/references/better-auth.md +74 -37
  10. package/skills/pikku-auth/references/sessions.md +8 -0
  11. package/skills/pikku-build/SKILL.md +36 -68
  12. package/skills/pikku-build/references/app.md +27 -828
  13. package/skills/pikku-build/references/feature.md +3 -5
  14. package/skills/pikku-build/references/phase-1-what-it-is.md +167 -0
  15. package/skills/pikku-build/references/phase-2-people-and-apps.md +133 -0
  16. package/skills/pikku-build/references/phase-3-plan.md +62 -0
  17. package/skills/pikku-build/references/phase-4-build.md +249 -0
  18. package/skills/pikku-build/references/phase-5-prove-and-ship.md +148 -0
  19. package/skills/pikku-build/references/platform.md +5 -6
  20. package/skills/pikku-build/references/quick.md +2 -2
  21. package/skills/pikku-build/references/ship.md +4 -5
  22. package/skills/pikku-changes/SKILL.md +13 -7
  23. package/skills/pikku-concepts/SKILL.md +19 -258
  24. package/skills/pikku-concepts/references/cli.md +63 -0
  25. package/skills/pikku-concepts/references/functions.md +117 -0
  26. package/skills/pikku-concepts/references/language.md +5 -5
  27. package/skills/pikku-concepts/references/packages.md +6 -4
  28. package/skills/pikku-concepts/references/testing.md +14 -0
  29. package/skills/pikku-convert-screen/SKILL.md +7 -1
  30. package/skills/pikku-deploy/SKILL.md +29 -30
  31. package/skills/pikku-deploy/references/ws.md +1 -1
  32. package/skills/pikku-emails/SKILL.md +7 -3
  33. package/skills/pikku-guide/SKILL.md +7 -10
  34. package/skills/pikku-i18n/SKILL.md +18 -19
  35. package/skills/pikku-i18n/references/messages.md +6 -8
  36. package/skills/pikku-knowledge/SKILL.md +42 -45
  37. package/skills/pikku-kysely/SKILL.md +50 -13
  38. package/skills/pikku-list-query/SKILL.md +27 -14
  39. package/skills/pikku-meta/SKILL.md +13 -16
  40. package/skills/pikku-meta/references/audit.md +1 -45
  41. package/skills/pikku-meta/references/versioning.md +1 -1
  42. package/skills/pikku-middleware/SKILL.md +23 -13
  43. package/skills/pikku-middleware/references/middleware-patterns.md +2 -2
  44. package/skills/pikku-n8n-import/SKILL.md +7 -8
  45. package/skills/pikku-permissions/SKILL.md +14 -8
  46. package/skills/pikku-react/SKILL.md +7 -6
  47. package/skills/pikku-realtime/SKILL.md +16 -11
  48. package/skills/pikku-report/SKILL.md +8 -10
  49. package/skills/pikku-scenario/SKILL.md +15 -18
  50. package/skills/pikku-scenario/references/steps.md +3 -4
  51. package/skills/pikku-scenario/references/virtual-users.md +1 -1
  52. package/skills/pikku-seo/SKILL.md +16 -8
  53. package/skills/pikku-service-backends/SKILL.md +26 -28
  54. package/skills/pikku-services/SKILL.md +8 -9
  55. package/skills/pikku-services/references/services.md +1 -1
  56. package/skills/pikku-software-archaeology/README.md +12 -1
  57. package/skills/pikku-software-archaeology/SKILL.md +9 -2
  58. package/skills/pikku-software-archaeology/references/pikku-mapping.md +1 -1
  59. package/skills/pikku-software-archaeology/references/sources/express.md +75 -0
  60. package/skills/pikku-software-archaeology/references/sources/laravel.md +77 -0
  61. package/skills/pikku-software-archaeology/references/sources/nestjs.md +73 -0
  62. package/skills/pikku-software-archaeology/references/sources/rails.md +72 -0
  63. package/skills/pikku-software-archaeology/references/tests-to-scenarios.md +72 -0
  64. package/skills/pikku-tailwind/SKILL.md +15 -16
  65. package/skills/pikku-theme/SKILL.md +15 -17
  66. package/skills/pikku-ui/SKILL.md +4 -3
  67. package/skills/pikku-webhook/SKILL.md +10 -11
  68. package/skills/pikku-wiring/SKILL.md +45 -47
  69. package/skills/pikku-wiring/references/channel.md +1 -1
  70. package/skills/pikku-wiring/references/scheduler.md +1 -1
  71. package/skills/pikku-wiring/references/trigger.md +24 -1
  72. package/skills/pikku-workflow/SKILL.md +7 -8
@@ -1,45 +1,24 @@
1
1
  # Build a product on open-source Pikku
2
2
 
3
- ## Contents
4
-
5
- - Agent Operating Procedure
6
- - 0. Bootstrap, before anything else
7
- - 1. One more round of questions — then stop asking
8
- - 1a. Three languages, and you must not collapse them
9
- - PHASE 1 — What the app is
10
- - 2. Write the knowledge graph — before any code
11
- - PHASE 2 — Who it is for, and what it is made of
12
- - 3. Declare the people — virtual users and roles
13
- - 4. Declare the apps — one API, several frontends
14
- - PHASE 3 — The plan
15
- - 5. File the work as changes
16
- - 5a. The technical plan — per changeset, before you build it
17
- - PHASE 4 — Build
18
- - 6. Build each changeset
19
- - 6a. Close the changeset against its plan, not against your memory
20
- - 7. Prove it — scenarios
21
- - 8. Make it look like someone designed it
22
- - 9. Ship it, and stay deploy-ready
23
- - Reference
24
-
25
3
  You have a scaffolded project with skills installed. This skill owns everything
26
4
  from here: no hosted account, no card, no hosted build — while keeping the
27
5
  project shaped so a hosting platform can adopt it later with zero rework.
28
6
 
29
- **Four phases, and you do not skip ahead:**
7
+ **Five phases, and you do not skip ahead.** This file is the index: read it,
8
+ then open each phase file when you reach that phase, not before. Section
9
+ numbers (§0-§9) are stable and are cited elsewhere as "app §N".
30
10
 
31
- 1. Write the knowledge graph — what the app IS, before any code
32
- 2. Declare the people and the apps — virtual users, roles, frontends
33
- 3. File the work as changes — grouped into changesets, in dependency order
34
- 4. Build them one changeset at a time — each proven by a scenario before it merges
11
+ | Phase | Sections | Read |
12
+ | -------------------------------------------------------------- | ------------------------------------------------------------------- | --------------------------------------- |
13
+ | 1. What the app is — write the knowledge graph before any code | §0 bootstrap, §1 questions, §1a three languages, §2 knowledge graph | `references/phase-1-what-it-is.md` |
14
+ | 2. Who it is for — virtual users, roles, frontends | §3 people, §4 apps | `references/phase-2-people-and-apps.md` |
15
+ | 3. The plan — file the work as changes, in dependency order | §5 changes, §5a technical plan | `references/phase-3-plan.md` |
16
+ | 4. Build, one changeset at a time | §6 build, §6a close against the plan, §6b feed back | `references/phase-4-build.md` |
17
+ | 5. Prove it, make it look designed, ship it | §7 scenarios, §7a coverage, §8 look and theme (§8a, §8b), §9 ship | `references/phase-5-prove-and-ship.md` |
35
18
 
36
- The sections below follow those phases in order, and are meant to be worked
37
- through rather than searched: §0 bootstrap, §1-§1a the last questions and the
38
- three languages, §2 the knowledge graph, §3-§4 virtual users and apps, §5-§5a the
39
- changes and each changeset's technical plan, §6-§6a building and closing one,
40
- §7-§7a proving it, §8 design, §9 ship. Four of them have their own reference —
41
- scenarios, design, multi-app and ship, each listed in SKILL.md — to read at the
42
- point you need it.
19
+ Work through them in order rather than searching. Each changeset repeats §5a,
20
+ §6-§7a; §8 and §9 run when the queue is empty (design is read before the first
21
+ screen, though — see below).
43
22
 
44
23
  ## Agent Operating Procedure
45
24
 
@@ -54,800 +33,20 @@ point you need it.
54
33
  wirings, schemas or generated clients may have changed.
55
34
  4. If validation fails, fix the source cause and rerun. Do not paper over
56
35
  generated errors by editing generated files.
57
- 5. Report at the person's level (SKILL.md, "Who you are talking to"): a console
58
- link for everything the console can show, and no code unless they are a
59
- developer.
60
-
61
- ## 0. Bootstrap, before anything else
62
-
63
- ```sh
64
- bunx --bun pikku bootstrap
65
- ```
66
-
67
- One command, run once, now — not later when you start building. It wires the
68
- `#pikku` import alias the generated code depends on. On a fresh scaffold `.pikku/`
69
- is empty, so **every command that touches codegen fails until this has run** —
70
- including ones you would reasonably reach for while still planning, like
71
- `pikku virtual-user list`. Those failures look alarming (`Cannot find module
72
- '#pikku/workflow/pikku-workflow-types.gen.js'`, `Schema generation failed for 16
73
- schemas`) and they are nothing but this missing step.
74
-
75
- `pikku knowledge index` and `knowledge validate` work without it — which is why
76
- the planning phases below are safe either way.
77
-
78
- ## 1. One more round of questions — then stop asking
79
-
80
- Ask **three to five** questions that actually change the schema or the screens,
81
- in one message. Then stop; do not interview the user.
82
-
83
- - **Who uses it — who are the distinct kinds of people?** Ask this however small
84
- the app is. The answer becomes §3's virtual users and roles, and you build only the
85
- roles they name.
86
- - **What are the two or three core objects?**
87
- - **What is the main thing someone does on their first visit?** This answer
88
- becomes the second changeset, not the tenth.
89
- - **One app or several?** Separate apps on separate hosts, or one app with paths.
90
- Cheap to answer now, expensive after the routes exist.
91
- - **What should it look like?** The template ships one theme — "Neutral", a
92
- deliberately unopinionated monochrome scaffold — and **nothing in the
93
- open-source toolchain will ever replace it for you.** Accept any of: keep
94
- Neutral (fine for an internal tool, but say so out loud); a direction in words;
95
- a reference (brand guide, screenshots, a site whose register they want); or
96
- their own design agent/prompt, whose output you take as the direction.
97
- - **Do they want to see the screens before you build them?** Offer it here, in
98
- this same round, with yes marked recommended, in the words of
99
- the design reference (see SKILL.md) — "a picture of the main screens so you can say 'yes,
100
- like that' or 'no, move this'", never "mock" or "wireframe". Behind it is one
101
- HTML page mocking the main screens, a few minutes of work. On a yes, or no
102
- answer at all, the design reference (see SKILL.md) owns what to make and what it then binds — the
103
- approved page becomes source of truth for the screens, and the theme is written
104
- before it so what they approve is what ships. Only an explicit no skips it.
105
- - **May I write test records into the system it talks to?** Ask only when the
106
- app reads a live system through an addon (an ERP, a CRM) and a changeset needs
107
- data that isn't there yet: an unpaid invoice, a closed ticket. Say what you
108
- would create and that it will be marked "Test". A no means building those
109
- screens against their empty states.
110
- - **What language should the app speak, and what language does the team work
111
- in?** Two answers, not one — see §1a, which is where they go. Ask only if the
112
- request is not obviously English; a brief written in English about an English
113
- product answers both.
114
-
115
- Skip anything you can decide yourself. If nobody answers, assume one app with
116
- paths, the roles implied by the request, Neutral, English — and say so.
117
-
118
- ## 1a. Three languages, and you must not collapse them
119
-
120
- A brief saying "the entire UI is German" is about **one** of these. Getting this
121
- wrong has already shipped a project that can never add a second language, so
122
- settle all three explicitly before you write code.
123
-
124
- | Axis | What it covers | Where it goes |
125
- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
126
- | **Identifiers** | Function, component, type, variable and file names. Database tables and columns. Commit messages. | Nowhere — **always English**, no setting, not negotiable |
127
- | **Meta** | `description` on functions and steps, `name`/`title` on features and scenarios, step `template`, role and virtual user descriptions | `metaLocale` in `pikku.config.json`, default `en` |
128
- | **Product UI** | Every string the app shows a user | `messages/<locale>.json`, and `defaultLocale` for what a first-time visitor opens in |
129
-
130
- **Identifiers are English.** The product's market does not change this and
131
- neither does `metaLocale`. Identifiers are the surface the generated `#pikku/*`
132
- clients, `pikku info`, the typed RPC map and the Kysely types all bind to, and
133
- unlike a string an identifier cannot be translated later — renaming one is a
134
- migration. A German practice management tool gets `getWorklist`, `case`,
135
- `event`, not `getUebersicht`, `vorgang`, `ereignis`.
136
-
137
- **Meta follows `metaLocale`.** Write the team's answer into `pikku.config.json` in
138
- this phase, before there is any meta to be wrong:
139
-
140
- ```json
141
- { "metaLocale": "de" }
142
- ```
143
-
144
- It exists for the Pikku Console. Meta is the one part of a project the Console
145
- renders back to a human, so a team working in German reads their own functions,
146
- features and scenario reports in German. Default `en` and do not ask when the
147
- project is obviously English. **On every later run, read this field first and
148
- author descriptions, titles and step templates in it** — a project whose
149
- `metaLocale` you ignored reports half in one language and half in another.
150
-
151
- **Product UI is the message catalogue.** `messages/<locale>.json` via
152
- `pikku-i18n`, with `defaultLocale` deciding what a visitor opens in.
153
- `baseLocale` in `project.inlang/settings.json` **stays `en`**: it names the
154
- message source, the catalogue every other language is cloned from, so a project
155
- that repoints it has nothing to translate from and `--add-locale` is broken
156
- forever.
157
-
158
- A German medical portal, correctly:
159
-
160
- ```jsonc
161
- // project.inlang/settings.json
162
- { "baseLocale": "en", "locales": ["en", "de"] }
163
- // apps/app/src/i18n/active.json
164
- { "defaultLocale": "de" }
165
- // pikku.config.json
166
- { "metaLocale": "de" }
167
- ```
168
-
169
- Record the two non-obvious answers as a `decisions/` note in §2 — neither is
170
- discoverable from code, and the next agent will otherwise re-derive them wrong.
171
-
172
- ---
173
-
174
- ## PHASE 1 — What the app is
175
-
176
- ## 2. Write the knowledge graph — before any code
177
-
178
- `knowledge/` is not documentation you write at the end. It is the record of what
179
- the app IS, in the words its users use, and it is the one part of the project
180
- another agent picks up and continues from. **Nothing gets built until there is a
181
- note saying what it is.**
182
-
183
- Read `knowledge/index.md` and the `pikku-knowledge` skill, then write the notes
184
- for what the user just told you. A project whose `knowledge/` is still only the
185
- shipped index is a project nobody can resume.
186
-
187
- Four sections, each answering exactly one question:
188
-
189
- - `entities/` — what a thing IS, in the words users use for it
190
- - `decisions/` — what was chosen and what that rules out.
191
- `decisions/security/` for who may reach what, `decisions/design/` for how it
192
- looks and behaves
193
- - `questions/` — what you asked and never got an answer to
194
- - `wishlist/` — what someone wants that nobody has asked you to build
195
-
196
- Rules that make it a graph rather than a pile of files:
197
-
198
- - **A note's path is its identity.** Markdown, YAML frontmatter, `type` required.
199
- Cross-link notes with plain markdown links — that is what makes it a graph.
200
- - **Create a section the turn you have a note for it**, with its own `index.md`
201
- written in the same turn. Never scaffold empty directories, and never leave
202
- notes flat at the root: a `product.md` and a `glossary.md` at `knowledge/` is
203
- not a knowledge base, and it leaves the project unbuildable.
204
- - **A note says what the app is, never what is left to do.** No status, no
205
- todo list: the work is filed as changes in §5, and `pikku knowledge gaps`
206
- says which notes no change builds yet.
207
- - **Record only what pikku cannot tell you.** Tables, columns, function
208
- signatures, routes, wirings, permissions and roles are all discoverable with
209
- `pikku info` / `pikku meta`. Copying them into a note gives you a second copy
210
- that goes stale. Knowledge is the why: decisions, constraints, what a thing
211
- means.
212
-
213
- Three decisions belong here on day one, because every later choice leans on them
214
- and none is discoverable from code:
215
-
216
- - **How the product is split into apps**, and why (§4) — `decisions/`
217
- - **What each kind of person may reach**, in domain language — `decisions/security/`
218
- - **What the app should look like** — the direction from §1 (§8) — `decisions/design/`
219
-
220
- Then keep it honest — both must pass, and `validate` is a real gate:
221
-
222
- ```sh
223
- bunx --bun pikku knowledge index
224
- bunx --bun pikku knowledge validate
225
- ```
226
-
227
- ---
228
-
229
- ## PHASE 2 — Who it is for, and what it is made of
230
-
231
- ## 3. Declare the people — virtual users and roles
232
-
233
- The answer to "who uses it" becomes code, in one file:
234
- `packages/functions/src/virtual-users.ts`. It ships with a single `visitor`; add the
235
- people the user named, and the roles they imply.
236
-
237
- ```typescript
238
- import { defineVirtualUsers } from '#pikku/scopes/pikku-virtual-users.gen.js'
239
- import { defineSystemRole } from '#pikku/scopes'
240
-
241
- defineSystemRole({
242
- owner: {
243
- displayName: 'Owner',
244
- description: 'Runs their own properties — sees only what they own',
245
- scopes: [],
246
- },
247
- tenant: {
248
- displayName: 'Tenant',
249
- description: 'Lives in one unit — sees only their own tenancy',
250
- scopes: [],
251
- },
252
- })
253
-
254
- defineVirtualUsers({
255
- visitor: {
256
- name: 'Visitor',
257
- jobTitle: 'Synthetic health-check user',
258
- account: {},
259
- },
260
- amina: {
261
- name: 'Amina',
262
- jobTitle: 'Property owner',
263
- personality: 'Checks arrears first, every single time',
264
- roles: ['owner'],
265
- account: {},
266
- },
267
- bilal: {
268
- name: 'Bilal',
269
- jobTitle: 'Property owner',
270
- personality:
271
- 'A second owner — exists so "you see yours, not theirs" is testable',
272
- roles: ['owner'],
273
- account: {},
274
- },
275
- chidi: {
276
- name: 'Chidi',
277
- jobTitle: 'Tenant',
278
- personality: 'Reports the boiler, wants to know it was seen',
279
- roles: ['tenant'],
280
- account: {},
281
- },
282
- })
283
- ```
284
-
285
- - **Keep `visitor`.** The shipped scenarios name `virtualUsers.visitor`, and PKU677
286
- requires a browser step's virtual user to be a literal `virtualUsers.<name>`. Removing it
287
- stops `virtualUsers.visitor` type-checking, which fails `pikku all`, and nothing you
288
- write after that registers.
289
- - **One `defineVirtualUsers` call for the whole project.** Codegen builds the
290
- `VirtualUserId` union from it, materialises one scenario virtual user per person, and
291
- seeds a user row each. A second call site is a second answer to "who uses this
292
- app".
293
- - **`roles` is typechecked against `defineSystemRole`.** An undeclared role is a
294
- build error rather than a runtime surprise.
295
- - **Never write an email address.** Each is derived from the virtual user id and
296
- `scenarios.emailDomain` in `pikku.config.json` — `visitor@virtualuser.local`.
297
- Hand-writing one is how a run signs in as somebody who was never created.
298
- - **Declare a second person of the same kind** whenever the rule is ownership
299
- (`bilal` above). "You see yours, not theirs" is not testable with one owner,
300
- and §7 is where it gets caught.
301
- - **Build the roles the user's answer produces, no more.** An invented role
302
- becomes invented screens and invented rules, and it is the user who has to
303
- live with them.
304
- - **Roles are what a permission check reads, not where it lives.** The check goes
305
- in the function's `permissions` field (§6), never in the body. Read the
306
- `pikku-auth` skill.
307
-
308
- `pikku virtual-user list` shows who is declared and `pikku roles audit` reports roles
309
- the database still holds that code no longer declares — both need §0's bootstrap
310
- to have run, and both are worth a look once it has.
311
-
312
- **One warning about the scaffold's own notes:** `knowledge/index.md` may claim
313
- the people live in `pikku.config.json`, put there by a virtual user command.
314
- That is stale. In this template they live in `virtual-users.ts` as above, and
315
- `pikku.config.json` carries no virtual users key at all — only `scenarios.emailDomain`.
316
- Trust the file you can read over the note describing it.
317
-
318
- ## 4. Declare the apps — one API, several frontends
319
-
320
- ### The rule that makes it cheap
321
-
322
- **One backend, many frontends. Never fork `packages/functions`.**
323
-
324
- Every app imports the same generated SDK (`@project/functions-sdk`) and calls the
325
- same RPCs. What differs is which screens exist and what the shell looks like.
326
- What must NOT differ is the data layer: two copies of a `listProperties` function
327
- is two places for the permission check to be wrong.
328
-
329
- **The app split is presentation, not security.** A tenant app that simply never
330
- renders the arrears screen is not access control — it is a hidden button.
331
- Security is the `permissions` field on the function, and it holds whether the
332
- caller arrived from the admin origin, the tenant origin, curl, or the generated
333
- client. Build the split for clarity, and prove the boundary with a refusal
334
- scenario in §7.
335
-
336
- ### Choosing the split
337
-
338
- - **Separate apps on separate hosts** (`admin.example.com`, `portal.example.com`)
339
- — when the two audiences share almost no screens, when they should not see each
340
- other's brand register, or when one may later ship independently.
341
- - **One app with paths** (`/app/admin/*`, `/app/portal/*`) — when they share most
342
- of the shell and the difference is a handful of screens. Cheaper, and honest:
343
- two nav trees in one app is still two apps to a user.
344
-
345
- Either way, write the choice and its reason into `knowledge/decisions/`.
346
-
347
- ### Adding a second frontend — later, not now
348
-
349
- Recording the decision is Phase 2 work. **Creating the directory is not.**
350
- Cloning `apps/app` materialises a folder of copied screens, so it belongs to the
351
- changeset that first needs the second app, not to planning.
352
-
353
- When you get there, read the multi-app reference (see SKILL.md). It carries the clone, the
354
- `package.json` edits, the `frontends` map in `pikku.config.json`, the dev-runner
355
- change that otherwise silently never starts your second app, the per-frontend
356
- scenario environments, and how sessions behave across two origins.
357
-
358
- What Phase 2 owes you now is only this: the split, its reason, and who each app
359
- serves, written into `knowledge/decisions/`.
360
-
361
- ---
362
-
363
- ## PHASE 3 — The plan
364
-
365
- ## 5. File the work as changes
366
-
367
- Turn the app into changes on the project's queue — the pikku-changes skill.
368
- `pikku knowledge gaps` lists every note no change builds yet; file each as one
369
- or more changes with `pikku changes file`, one commit's worth each, and
370
- end every body with the gap's `Knowledge:` line so it is not filed twice.
371
-
372
- What a change is:
373
-
374
- - **One commit's worth.** "The owner sees this month's arrears" is a change,
375
- or a few. "Add the database schema" is not; it is a step inside one.
376
- - **It ends in something a person can do**, in a browser, signed in as a named
377
- virtual user. If you cannot say what they see when it is done, that is a
378
- `questions/` note, not a change.
379
-
380
- If §1's screen mock was made — approved, or drawn because nobody answered —
381
- every screen on that page belongs to some change, and a screen no change builds
382
- is a hole in this plan.
383
-
384
- How they group into changesets, in order:
385
-
386
- 1. **The spine first.** The one object everything else hangs off, and the screen
387
- that proves the app exists at all.
388
- 2. **Then the loop the user named as "the main thing someone does on their first
389
- visit."** That answer from §1 is the second changeset, not the tenth.
390
- 3. **Then each audience's own surface**, one at a time. With two apps, finish one
391
- app's spine before starting the other's — a half-built app in each is worse
392
- than one working app.
393
- 4. **Refusals ride along with the changeset that creates the thing being
394
- refused**, never as a "permissions" changeset at the end.
395
-
396
- **One approval, then build to the end.** Show the picture of the screens and
397
- the changes together, in one message, as the plan. That is the only approval you
398
- ask for. Once they approve it, or don't answer, run `pikku changes next --exec <harness>
399
- --loop`: it hands each changeset to a fresh agent, merges it, and moves on. Post
400
- one line as each changeset merges, and carry on. Stop only for what is theirs to
401
- decide: a credential you don't have, spending money, posting in public, deleting
402
- or overwriting their data, or a finding that changes the plan.
403
-
404
- ## 5a. The technical plan — per changeset, before you build it
405
-
406
- `pikku changes claim` decides whether a changeset needs a **plan**: one
407
- that creates or alters a table always does, and the judge decides the rest. The
408
- plan says what has to exist: the tables, functions, wires, roles, scopes,
409
- screens and scenarios, split into passes. It is JSON at
410
- `knowledge/plans/<changeset>.plan.json`, committed on the changeset's branch,
411
- and `pikku knowledge plan progress` measures the finished build against it.
412
-
413
- **Read `pikku-architect` and follow it.** The plan is written in its own turn,
414
- before any of the code it measures exists, and is never edited afterwards to
415
- match what you ended up building. An item that will not land is deferred with
416
- its reason — `plan defer` — not quietly rewritten:
417
-
418
- ```sh
419
- pikku knowledge plan schema # the only spec there is
420
- pikku knowledge plan set <changeset> /tmp/plan.json
421
- pikku knowledge plan show <changeset> --for-build # what you then build
422
- ```
423
-
424
- ---
425
-
426
- ## PHASE 4 — Build
427
-
428
- ## 6. Build each changeset
429
-
430
- **Per changeset** — claim it, plan it if the claim says so (§5a), do the six
431
- steps one commit per change, close it out (§6a), mark its changes done. Do not
432
- mark the last one done until §6a passes, §7 is green for it _and §7a shows its
433
- functions covered_; `changes done` refuses a planned changeset's last change
434
- while the plan is short.
435
-
436
- 1. **Migration.** SQL in `db/sqlite/` at the project root (`db/postgres/` or
437
- `db/mysql/` when `createConfig` sets `postgresUrl` or `mysqlUrl`), numbered on from the
438
- ones already there. Apply with `bunx --bun pikku db migrate`, which also
439
- regenerates the Kysely types your functions import. **Neither `pikku all` nor
440
- restarting `pikku dev` applies a migration** — so a new column reads as
441
- `TS2353 … does not exist in type 'InsertExpression<DB, "…">'` on the function
442
- that writes it. An unapplied migration is the usual cause and the cheapest to
443
- rule out, so run `db migrate` first — but the same error is what a misspelt
444
- column or a stale generated type looks like, so if it survives the migration,
445
- go and read the SQL.
446
- 2. **Seed.** Demo rows in `db/sqlite-dev-seed.sql`. **There is no seed command** —
447
- `bunx --bun pikku db reset` is the only thing that applies the file, and it
448
- wipes, migrates and seeds in one go (`--no-seed` stops after the migration,
449
- for working on an empty state the test data would hide). Because reset always
450
- arrives at a database it just wiped, **the seed file is plain `INSERT`s** — no
451
- `ON CONFLICT DO NOTHING`, no `INSERT OR IGNORE`. Nothing ever applies it
452
- twice, so it never has to defend itself. It is also **local only** — no deploy
453
- applies it, so anything the app would be broken without in production is
454
- configuration and belongs in a migration, not here.
455
- Do this generously and do it now: an empty app demos badly and critiques
456
- badly, and you cannot judge a screen's hierarchy, overflow, or truncation
457
- against zero rows. Seed rows each virtual user sees differently — with an ownership
458
- rule that means seeding rows for the _second_ owner too.
459
- 3. **Functions.** One `pikkuFunc` per `*.function.ts`. Mark it `expose: true` and
460
- Pikku generates the typed RPC client and the React Query hooks the UI calls;
461
- you do NOT write an HTTP route for it. Add `wireHTTP` only for a real REST
462
- shape (a third-party webhook).
463
- 4. **Regenerate:** `bunx --bun pikku all`
464
- 5. **UI.** Pages in `<app>/src/pages/`, one route file each in `<app>/src/routes/`,
465
- calling functions through the generated `usePikkuQuery` / `usePikkuMutation`
466
- hooks from `@project/functions-sdk/pikku/api.gen`. One component per `.tsx`
467
- file. Compose the kit from `@/components/<Name>` rather than hand-rolling
468
- controls — and **add to that kit**: the component that draws the thing this
469
- product is actually about, and the furniture you would otherwise copy-paste
470
- into eight pages. The kit is where you start, not where you stop; an app whose
471
- every screen is `Card` + `Stack` + `Text` composed the inventory rather than a
472
- design. Register the screen in `useNavItems()` — that one file feeds both the
473
- desktop sidebar and the phone navigation.
474
- **Read the design reference (see SKILL.md) before you write the first screen.** You commit
475
- to a design direction there and are then accountable to it — it hands you no
476
- layouts, because the design is yours to make. What you build here is then
477
- judged at step 7, at the end of this changeset rather than once at §8, where
478
- the only affordable fix is a repaint of eight screens.
479
- 6. **Scenario** (§7).
480
- 7. **Look at it.** Screenshot every screen this changeset touched, at both
481
- widths, with the seed in place, and look at the images. This is a gate, the
482
- same as the scenario: a changeset whose screens nobody has seen is not built,
483
- it is unproven at the one layer scenarios cannot reach. the design reference
484
- carries how to take the shot when no browser tool is wired up, and what to
485
- look for. Then close it against its plan (§6a) — `pikku knowledge plan
486
- progress` has to exit zero before the last change is done — and only then
487
- mark it done, saying in the `--note` what you looked at and what it made you
488
- change.
489
-
490
- Rules that are not optional:
491
-
492
- - A function's input and output types come from its `input:`/`output:` zod
493
- schemas. Never pass generic type params, never annotate the return type inline.
494
- The schema is the type. (Generics XOR schemas — never both.)
495
- - Auth and permission checks go in the `permissions` field, never in the function
496
- body. This is what makes §4's app split safe.
497
- - No `process.env` inside a function. Read config through the injected
498
- `variables` / `secrets` services; `process.env` belongs only in bootstrap. Every
499
- secret a function reads needs a matching `defineSecret`, or `pikku all` reports
500
- PKU951 and nobody knows what to provision at deploy.
501
- - Let the database type your values, via `db/annotations.ts`. A `BOOLEAN` column
502
- is derived for you. On SQLite the other two are **not** — add them by hand,
503
- once, and they are typed AND coerced end-to-end:
504
-
505
- ```typescript
506
- export const classifications = {
507
- payment: {
508
- paid_at: { kind: 'date' }, // -> Date, not an ISO string
509
- metadata: { kind: 'json', tsType: 'PaymentMeta' }, // -> parsed object, not unknown
510
- },
511
- }
512
- ```
513
-
514
- A `TIMESTAMP`/`DATETIME`/`DATE` column with no entry types as `string`, and a
515
- `JSON` column with no entry types as `unknown` (the CLI warns PKU481). Add the
516
- annotation rather than casting around the generated type. Once the file carries
517
- manual fields, `db migrate` stops overwriting it.
518
-
519
- - A `z.date()` on a function's **input** arrives over RPC as an ISO string, not a
520
- `Date`. Normalise before calling date methods on it (`new Date(value)`), or it
521
- throws `.getTime is not a function` at runtime — schema validation accepts the
522
- string without converting it.
523
- - Every user-facing string is a translation key. Never a hardcoded literal. With
524
- two apps that means two `messages/` directories; a string used by both belongs
525
- to whichever app renders it, and duplication beats a shared bundle that couples
526
- the apps together.
527
- - Surface errors. No empty catch, no swallowed promise. If a mutation can fail,
528
- render the failure inline next to the control that triggered it — not a toast.
529
- - An exposed function with no session and no permission is reachable by anyone
530
- over `POST /rpc/:rpcName` (PKU574). Either gate it, drop `expose: true`, or —
531
- when public is the point — write `auth: false` on it to say so.
532
- - A public, signed-out read (a homepage's programme, a price list) is a
533
- `pikkuSessionlessFunc` with `auth: false` written out, which is what keeps
534
- PKU574 quiet for it. `pikkuFunc` with `auth: false` still answers
535
- `MissingSessionError` over `/rpc` to a caller with no session.
536
- - Better Auth already owns the `user`, `session`, `account` and `verification`
537
- tables. A domain table with one of those names — a class _session_, a drop-in
538
- _session_ — collides in the migration. Name it for the domain instead
539
- (`evening`, `class_meeting`) and keep the word in the UI copy.
540
- - The template's `/` redirects to `/app`, so the login screen is what a
541
- signed-out visitor sees first. Replace `/` with a public homepage and a
542
- reviewer lands on a site with no way in: link it to the login screen.
543
-
544
- Before the first run, make sure `.env` at the project root holds the secret
545
- the local stack needs. `bun run dev` appends it if missing, but check anyway —
546
- a project scaffolded from an older template, or a `.env` copied in from
547
- elsewhere, can lack it, and the failure does not name the variable:
548
-
549
- - `BETTER_AUTH_SECRET` — without it the first sign-up is a 500.
550
-
551
- Virtual user sign-in needs nothing in `.env`: `pikku dev` creates its own key
552
- pair and hands the app the public half.
553
-
554
- ```sh
555
- grep -q '^BETTER_AUTH_SECRET=' .env 2>/dev/null || echo "BETTER_AUTH_SECRET=$(openssl rand -base64 32)" >> .env
556
- ```
557
-
558
- `.env` is gitignored and local only — never commit it. A deployed stage gets its
559
- secrets from the hosting platform, not from this file.
560
-
561
- Then run it:
562
-
563
- ```sh
564
- bun run prebuild && bun run dev
565
- ```
566
-
567
- That starts the API on :3000 and every frontend in `frontends`. A
568
- frontend running against a dead API looks exactly like an app bug, so if every
569
- request fails, check that both halves came up.
570
-
571
- **Start the stack through `bun run dev`, not by launching `vite` or `pikku dev`
572
- yourself.** The frontend needs to reach _your_ API. If you start the
573
- frontend on its own (say :3000 is taken by another project), point
574
- `VITE_API_PROXY` at your API: the dev proxy defaults to `http://localhost:3000`,
575
- so beside another project's server every request goes to the wrong API.
576
-
577
- The `--bun` in `bunx --bun pikku …` is load-bearing — keep it. Without it the
578
- CLI's `#!/usr/bin/env node` shebang hands the process to whatever Node is on
579
- PATH, which fails below Node 24 with `ERR_UNKNOWN_BUILTIN_MODULE: No such
580
- built-in module: node:sqlite`.
581
-
582
- Open it, sign up, and click through what you built. **HTTP 200 is not evidence.**
583
- The pages are client-rendered: the server returns 200 with an empty shell, so a
584
- page whose component throws still looks fine to `curl`.
585
-
586
- That click-through is a smoke check, and it is the only thing it is. **Do not
587
- hand-drive a browser tool in place of a test.** Steering Playwright yourself
588
- proves a page rendered once, on your machine, in an order only you remember —
589
- nothing about it re-runs, so the next agent inherits a claim rather than a test,
590
- and a regression lands silently. When a journey is worth driving through the UI,
591
- it is worth writing as a browser step on §7's scenario and running
592
- `pikku scenario run local --spawn --run browser`: same clicks, same assertions,
593
- in the repo, green or red on every future run.
594
-
595
- ## 6a. Close the changeset against its plan, not against your memory
596
-
597
- ```sh
598
- pikku knowledge plan progress <changeset>
599
- ```
600
-
601
- It reads §5a's plan and reconciles it against the generated meta under
602
- `.pikku/` — the function exists or it does not, the route is wired or it is not,
603
- the `pikkuScenario` export is there or it is not. Nothing it reports comes from
604
- what anyone claimed. It exits non-zero while anything in the first pass is
605
- missing, and `changes done` runs the same check on the last change.
606
-
607
- Three things it says, and what each one asks of you:
608
-
609
- - **MISSING** — the first pass owes it and the meta cannot see it. Either build
610
- it, or, if it genuinely belongs to later work, move it out with a reason on
611
- the record:
612
-
613
- ```sh
614
- pikku knowledge plan defer <changeset> function:sendReminder \
615
- -r "The email service it needs is the next changeset."
616
- ```
617
-
618
- **A deferral is capped at two per plan.** Past that, the plan was wrong and the
619
- changeset is two changesets — say so rather than deferring again. What you may
620
- never do is drop the item silently. A merged changeset with deferrals leaves its
621
- notes `partial`, and `pikku knowledge gaps` files what it left behind.
622
-
623
- - **PROBLEMS** — something exists but does not do what was planned. A function
624
- planned as restricted whose meta says `auth: false`; a `cascade` no migration
625
- declares; a browser scenario that opens a page and asserts it is still on it.
626
- These are never deferred. Fix the app.
627
- - **DEFERRED to a later pass** — already accounted for. Reported so it is
628
- visible, never blocking.
629
-
630
- Do not edit the plan to match what you built — the plan was written before the
631
- code on purpose, and rewriting your own denominator afterwards is exactly what
632
- that order exists to stop.
633
-
634
- ### 6b. Feed the changeset back into the seats
635
-
636
- Before starting the next changeset, answer two questions out loud:
637
-
638
- - **What did the plan fail to say?** A field nothing wrote, a promise no function
639
- could keep, a pass 1 that turned out to be two. That is a `pikku-architect`
640
- lesson.
641
- - **What did the build learn the hard way?** Anything that cost a wasted run —
642
- a stale process, a scenario that only passes once, a diagnostic that turned
643
- out to be an echo of an earlier one. That is a `pikku-build` lesson.
644
-
645
- Then edit the skill — **at most one change to each per changeset**, and only for
646
- something that actually went wrong here. A rule with no incident behind it is a
647
- guess, and these files are read in full every time: they earn their length by
648
- naming failures a reader would otherwise repeat. Prefer sharpening an existing
649
- line to appending a new one, and delete a rule the last few changesets have
650
- shown to be noise.
651
-
652
- The gates are the compounding part. A lesson written into a scenario the suite
653
- runs, or into a check `plan progress` can see, is enforced; the same lesson
654
- written as prose is a thing the next reader has to remember. Reach for prose
655
- only when there is nothing to hang a check on.
656
-
657
- ## 7. Prove it — scenarios
658
-
659
- A scenario is a user journey run as one of your virtual users, over the real
660
- transport, with that virtual user's session. It is the only kind of test worth writing
661
- here, because a passing one proves the app works the way a signed-in person
662
- experiences it. Three ship in `packages/functions/test/scenarios/` — keep them
663
- green — and every scenario a changeset's plan names becomes one more.
664
-
665
- **Read the scenarios reference (see SKILL.md) before writing the changeset's scenarios,
666
- and again whenever one of these describes what you are doing.** It is the file
667
- where the expensive lessons live, and most of them produce a GREEN suite that
668
- proves nothing:
669
-
670
- - the shape of a scenario, and `do` vs `given`/`when`/`then`
671
- - what survives DSL extraction — a `then` in `return` position asserts nothing,
672
- and a shared setup helper credits its steps to the wrong virtual user
673
- - what to assert — refusals must read the REASON, totals must be deltas, and the
674
- assertion nobody writes is the row count
675
- - living without a state reset: the suite must be green on its SECOND run
676
- - how a shared step rots as later changesets add writers of the rows it selects
677
- - browser specifics — the click/navigate race, testids, and `Outlet` nesting
678
-
679
- Run them:
680
-
681
- ```sh
682
- bunx --bun pikku scenario run local --spawn # server-side, the fast path
683
- bunx --bun pikku scenario run local --spawn --run browser # the same journeys, driven as a human
684
- ```
685
-
686
- In a multi-app project that one run covers both frontends: each virtual user carries
687
- its own `app` and `@pikku/playwright` resolves the base url from the
688
- environment's `appUrls` map, so there is no second environment to run.
689
-
690
- **Run the whole suite, not the changeset's own scenarios.** The changeset's
691
- scenarios are the ones you wrote to pass; the regression lives in someone
692
- else's. Tightening what "archived" means is a one-function change that reads as
693
- local and quietly breaks the first changeset's scenario nobody re-ran.
694
-
695
- **Restart the server after adding a function, and never edit one while a run is
696
- in flight.** Hot reload does not register a new RPC and does not re-run
697
- `afterStart`, so a fresh function answers 404 and anything provisioned at boot
698
- is missing — failures that read like a wiring bug and are nothing but a stale
699
- process. The same reload is what makes a run unrepeatable if you edit during
700
- it: a browser pass is long enough to feel like free time, and a schema touched
701
- at minute four hot-reloads into a half-generated contract, so every scenario
702
- after that point fails on something you have already fixed. Wait for the run or
703
- kill it — a run you edited under is not a result.
704
-
705
- ### 7a. Coverage — which functions have actually been run
706
-
707
- Green scenarios tell you the journeys you wrote still work. They say nothing
708
- about the code you never wrote a journey for:
709
-
710
- ```sh
711
- bunx --bun pikku dev --coverage # server, instrumented
712
- bunx --bun pikku scenario run local --coverage # against that server
713
- ```
714
-
715
- **A function no scenario touches has no scenario coverage** — the file knows
716
- what the suite exercises and nothing else, so a unit test, a scheduled job, a
717
- webhook or a hand call leaves no trace in it. Read
718
- `coverage/scenario-coverage.json` **as each changeset closes** — per changeset it is a short list you can act on, whereas read for the
719
- first time after ten changesets it is a wall of red nobody triages. Every gap is
720
- a missing scenario, a function that should not exist, or a deferral worth
721
- writing down; the scenarios reference (see SKILL.md) says how to tell them apart. Report
722
- the number when you hand the changeset over.
723
-
724
- ## 8. Make it look like someone designed it
725
-
726
- **This section numbers 8, but half of it has already happened.** Read
727
- the design reference (see SKILL.md) before the first screen is built — a design pass run on
728
- eight changesets of scaffolded screens is a repaint, and it shows. What is left
729
- here at §8 is the theme you may have deferred and the critique you cannot run
730
- until there are screens to critique.
731
-
732
- Three separate jobs, and conflating them is why open-source builds come out
733
- looking like the template:
734
-
735
- - **Direction** — deciding what it should look like. **No open-source tool does
736
- this.** A hosting platform may offer one; you have §1's answer and 8a below.
737
- - **Execution** — whether the screens actually realise that direction, or
738
- default to whatever component was nearest. No theme does this, and it is where
739
- "works but looks like nobody decided anything" comes from.
740
- the design reference (see SKILL.md) carries the process for it, and it belongs at §6, per
741
- screen.
742
- - **Critique** — judging how well the built screens execute the direction.
743
- `impeccable` does this well, and it is free.
744
-
745
- Impeccable audits the design you chose. It will never tell you the app should
746
- have looked like something else — it will happily award a clean bill of health to
747
- a perfectly-executed default. Skip the first two and you ship Neutral with good
748
- spacing.
749
-
750
- ### 8a. Author the theme — the step nothing does for you
751
-
752
- The look lives in `packages/theme`, and it is data, not code — one JSON
753
- per theme, `active.json` naming the live one. **Read the theming reference (see SKILL.md)** for
754
- the file layout, what each field changes, and how to turn a direction in words
755
- into a theme.
756
-
757
- Two things that belong here rather than in the reference, because they govern
758
- every screen you then build:
759
-
760
- **Set the theme once, don't hardcode colours per component.** A screen full of
761
- inline `color="blue"` and one-off hex values is why apps look templated. Change
762
- the theme, not the components — and keep it theme-aware for light and dark.
763
-
764
- With two apps, **share the theme package and vary the register, not the
765
- palette.** A back-office can be denser and more tabular; a customer-facing app
766
- can be roomier and warmer — that is `structure` and layout, not a second `brand`.
767
- Two unrelated colour schemes read as two products from two companies.
768
-
769
- Then **write the direction into `knowledge/decisions/design/`** — the words the
770
- user gave you, what you chose, and what it rules out. The JSON records what the
771
- theme is; only the note records why.
772
-
773
- ### 8b. Compose real components, then critique
774
-
775
- **The design reference (see SKILL.md) is where design actually lives** — committing to a
776
- direction before the first screen, judging the result from screenshots rather
777
- than from source, the two screens that get skipped, and why you reset the dev
778
- database before judging anything. It prescribes no layouts on purpose: two apps
779
- built from this skill should not look like each other. What follows here is only
780
- the component inventory.
781
-
782
- **Compose with rich components — not tables and text everywhere:**
783
-
784
- - **shadcn `chart`** (Recharts underneath, coloured by `--chart-1` to `--chart-5`) for overviews — area,
785
- bar, line and pie charts. A metric worth showing is
786
- worth a chart, not a number in a paragraph.
787
- - **shadcn `calendar` and `date-picker`** for anything time-based, including
788
- range inputs. Never hand-roll a date field.
789
- - Composed layouts over flat lists — a timeline for history, steps for
790
- multi-step progress, `Card` + a CSS grid for a gallery, `Progress` for
791
- completion, `Badge` for status.
792
-
793
- Add each one with the shadcn CLI and use the real component.
794
-
795
- Then critique it. Free, and works across coding agents:
796
-
797
- ```sh
798
- npx impeccable install # current releases need Node 22.18+
799
- ```
800
-
801
- Impeccable scores a screen against interaction heuristics and names what is
802
- wrong: hierarchy, spacing, type registers, states you forgot. Run it on **every**
803
- screen in **every** app, fix what it finds, and re-run the ones you changed.
804
-
805
- Screenshot each page and feed it the images. Without them its findings drop to
806
- inference from source, and it misses real misalignment, contrast, and overflow.
807
- Judging your own UI from source code is guessing.
808
-
809
- **Screenshot at a phone width too (≈390px), not just desktop, and critique
810
- those.** A layout that is fine at 1440px routinely breaks at 390 — a table that
811
- overflows, a row of buttons that wraps into a pile, text jammed against the edge,
812
- a modal taller than the viewport. Tailwind gives you the tools (responsive `grid-cols-*`,
813
- `md:hidden` / `hidden md:block`, `flex-col` instead of `flex-row` at small sizes); use
814
- them. The template already mounts a phone navigation per `AGENTS.md` — pick
815
- `MobileTabBar` or `MobileNavDrawer` deliberately per app, never both.
816
-
817
- The gate: **no P0 findings left on any screen, in any app, at either width**,
818
- and every screen honestly answers the questions in the design reference —
819
- including whether it looks like the direction you committed to or like the
820
- components you had. Don't silence a finding by deleting the feature it is
821
- about.
822
-
823
- **Critique the data too, not just the layout.** Scenario runs write run-tagged
824
- rows into the dev database, so by §8 the app is full of `Ripe peaches d193e2aa`
825
- seven copies deep. Run `pikku db reset` and restart the dev server before you
826
- screenshot anything — a screen judged against that data gets designed for a
827
- problem it does not have.
828
-
829
- ## 9. Ship it, and stay deploy-ready
830
-
831
- When the queue is empty, `pikku knowledge gaps` lists nothing and the scenarios are green, read
832
- the ship reference (see SKILL.md). It carries the open-source deploy paths (`--provider
833
- standalone`, `cloudflare`, `aws`), how to serve several frontends behind one
834
- API, the pre-release gate to run, and the contract that keeps the project
835
- importable by a hosting platform later rather than a migration.
836
-
837
- The app is not handed over without its user guide. The scenarios you wrote are
838
- already its skeleton: read **pikku-guide**, write one page per audience citing
839
- every feature, and build it from a full, passing `--run browser --screenshots`
840
- run.
841
-
842
- Two things from it are worth knowing before you get there, because they are
843
- cheaper to honour than to retrofit:
844
-
845
- - **Nothing hardcodes a host, a port, or a `process.env` read inside a
846
- function.** Secrets go through `defineSecret` and the injected `secrets`
847
- service. This is the most common reason a working local project fails its
848
- first deploy, on any platform.
849
- - **Generated files stay generated.** No hand edits to `.pikku/`, `*.gen.*`, or
850
- the SDK.
36
+ 5. Report at the person's level (SKILL.md, "Who you are talking to"): plain
37
+ words, the app and API links `bun run dev` printed, and no code unless they
38
+ are a developer.
39
+
40
+ ## Rules that hold in every phase
41
+
42
+ - **Identifiers are always English**; meta follows `metaLocale`; product UI strings are
43
+ translation keys (§1a, §6).
44
+ - **Permissions live in a function's `permissions` field**, never in its body or in a frontend (§4, §6).
45
+ - **No `process.env` inside a function**; secrets go through `defineSecret` and the injected `secrets` service (§6, §9).
46
+ - **Generated files stay generated** — never hand-edit `.pikku/`, `*.gen.*`, or the SDK.
47
+ - **Nothing is built until a knowledge note says what it is** (§2), and a plan is never edited to match what you built (§5a, §6a).
48
+ - **Every changeset ends proven**: a passing scenario including its refusals (§7), screens looked at (§6), functions covered (§7a).
49
+ - **One approval, then build to the end** (§5). Stop only for what is theirs to decide.
851
50
 
852
51
  ## Reference
853
52