@pikku/skills 0.12.52 → 0.12.55

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 (112) hide show
  1. package/README.md +1 -1
  2. package/dist/skills.gen.js +2 -2
  3. package/dist/snippets.js +1 -1
  4. package/package.json +1 -1
  5. package/skills/pikku-a11y/SKILL.md +22 -15
  6. package/skills/pikku-addon/SKILL.md +5 -5
  7. package/skills/pikku-addon/references/addon-package-manifest.md +7 -0
  8. package/skills/pikku-addon/references/openapi.md +11 -0
  9. package/skills/pikku-agent/SKILL.md +6 -6
  10. package/skills/pikku-agent/references/agents.md +8 -1
  11. package/skills/pikku-agent/references/runner-vercel.md +2 -2
  12. package/skills/pikku-agent/references/voice.md +7 -1
  13. package/skills/pikku-architect/SKILL.md +2 -2
  14. package/skills/pikku-architect/references/plan-defects.md +9 -7
  15. package/skills/pikku-auth/SKILL.md +15 -36
  16. package/skills/pikku-auth/references/better-auth.md +106 -133
  17. package/skills/pikku-auth/references/jose.md +6 -0
  18. package/skills/pikku-auth/references/machine-auth.md +6 -0
  19. package/skills/pikku-auth/references/sessions.md +6 -0
  20. package/skills/pikku-build/SKILL.md +37 -34
  21. package/skills/pikku-build/references/app.md +113 -99
  22. package/skills/pikku-build/references/design.md +43 -31
  23. package/skills/pikku-build/references/feature.md +17 -3
  24. package/skills/pikku-build/references/mocks.md +29 -0
  25. package/skills/pikku-build/references/multi-app.md +25 -18
  26. package/skills/pikku-build/references/native-app.md +9 -0
  27. package/skills/pikku-build/references/openapi.md +41 -34
  28. package/skills/pikku-build/references/platform.md +23 -14
  29. package/skills/pikku-build/references/post-clone.md +1 -1
  30. package/skills/pikku-build/references/quick.md +76 -39
  31. package/skills/pikku-build/references/scenarios.md +28 -16
  32. package/skills/pikku-build/references/ship.md +19 -16
  33. package/skills/pikku-build/references/theming.md +76 -127
  34. package/skills/pikku-changes/SKILL.md +7 -10
  35. package/skills/pikku-concepts/SKILL.md +34 -44
  36. package/skills/pikku-concepts/references/concept-mapping.md +18 -0
  37. package/skills/pikku-concepts/references/language.md +6 -6
  38. package/skills/pikku-convert-screen/SKILL.md +45 -0
  39. package/skills/pikku-deploy/references/cloudflare.md +8 -0
  40. package/skills/pikku-deploy/references/lambda.md +8 -0
  41. package/skills/pikku-deploy/references/nextjs.md +1 -1
  42. package/skills/pikku-deploy/references/uws.md +1 -1
  43. package/skills/pikku-deploy/references/ws.md +3 -3
  44. package/skills/pikku-emails/SKILL.md +3 -9
  45. package/skills/pikku-guide/SKILL.md +28 -27
  46. package/skills/pikku-i18n/SKILL.md +7 -7
  47. package/skills/pikku-i18n/references/enum-labels.md +9 -0
  48. package/skills/pikku-i18n/references/messages.md +21 -9
  49. package/skills/pikku-i18n/references/rtl.md +18 -38
  50. package/skills/pikku-knowledge/SKILL.md +12 -13
  51. package/skills/pikku-kysely/SKILL.md +3 -21
  52. package/skills/pikku-list-query/SKILL.md +2 -2
  53. package/skills/pikku-meta/references/audit.md +10 -1
  54. package/skills/pikku-meta/references/meta.md +6 -0
  55. package/skills/pikku-meta/references/versioning.md +10 -0
  56. package/skills/pikku-middleware/SKILL.md +7 -16
  57. package/skills/pikku-n8n-import/SKILL.md +9 -1
  58. package/skills/pikku-n8n-import/references/addon-mapping.md +6 -0
  59. package/skills/pikku-n8n-import/references/code-translation.md +7 -0
  60. package/skills/pikku-permissions/SKILL.md +41 -10
  61. package/skills/pikku-permissions/references/scopes.md +59 -0
  62. package/skills/pikku-react/SKILL.md +20 -22
  63. package/skills/pikku-react/references/client.md +2 -70
  64. package/skills/pikku-react/references/react-query.md +11 -0
  65. package/skills/pikku-react/references/workflows.md +11 -0
  66. package/skills/pikku-realtime/SKILL.md +9 -3
  67. package/skills/pikku-report/SKILL.md +17 -16
  68. package/skills/pikku-scenario/SKILL.md +72 -81
  69. package/skills/pikku-scenario/references/browser.md +5 -5
  70. package/skills/pikku-scenario/references/coverage.md +4 -4
  71. package/skills/pikku-scenario/references/steps.md +31 -30
  72. package/skills/pikku-scenario/references/{persona-run.md → virtual-user-run.md} +47 -42
  73. package/skills/pikku-scenario/references/virtual-users.md +114 -0
  74. package/skills/pikku-seo/SKILL.md +12 -16
  75. package/skills/pikku-service-backends/SKILL.md +0 -10
  76. package/skills/pikku-service-backends/references/aws.md +6 -0
  77. package/skills/pikku-service-backends/references/mongodb.md +7 -0
  78. package/skills/pikku-service-backends/references/redis.md +6 -0
  79. package/skills/pikku-services/references/audit-wire-service.md +2 -2
  80. package/skills/pikku-services/references/audit.md +10 -1
  81. package/skills/pikku-services/references/config.md +6 -0
  82. package/skills/pikku-services/references/services.md +9 -2
  83. package/skills/pikku-software-archaeology/SKILL.md +21 -27
  84. package/skills/pikku-software-archaeology/references/blueprint.schema.json +16 -16
  85. package/skills/pikku-software-archaeology/references/pikku-mapping.md +3 -3
  86. package/skills/pikku-software-archaeology/references/second-opinion.md +13 -1
  87. package/skills/pikku-tailwind/SKILL.md +111 -0
  88. package/skills/pikku-theme/SKILL.md +187 -0
  89. package/skills/pikku-theme/references/from-a-reference.md +44 -0
  90. package/skills/pikku-theme/references/presets.md +40 -0
  91. package/skills/pikku-ui/SKILL.md +41 -0
  92. package/skills/pikku-wiring/SKILL.md +13 -12
  93. package/skills/pikku-wiring/references/channel.md +7 -1
  94. package/skills/pikku-wiring/references/cli.md +7 -1
  95. package/skills/pikku-wiring/references/gateway-slack.md +6 -0
  96. package/skills/pikku-wiring/references/http.md +10 -3
  97. package/skills/pikku-wiring/references/mcp.md +8 -0
  98. package/skills/pikku-wiring/references/queue.md +6 -0
  99. package/skills/pikku-wiring/references/realtime.md +34 -4
  100. package/skills/pikku-wiring/references/rpc.md +6 -0
  101. package/skills/pikku-wiring/references/scheduler.md +6 -0
  102. package/skills/pikku-wiring/references/trigger.md +8 -0
  103. package/skills/pikku-workflow/SKILL.md +9 -7
  104. package/skills/pikku-admin-to-fabric/SKILL.md +0 -212
  105. package/skills/pikku-auth/references/permissions.md +0 -261
  106. package/skills/pikku-blueprint-to-fabric/SKILL.md +0 -377
  107. package/skills/pikku-blueprint-to-fabric/scripts/inventory.mjs +0 -367
  108. package/skills/pikku-fabric/SKILL.md +0 -619
  109. package/skills/pikku-fabric/references/debugging.md +0 -110
  110. package/skills/pikku-mantine/SKILL.md +0 -80
  111. package/skills/pikku-scenario/references/personas.md +0 -117
  112. package/skills/pikku-wiring/references/realtime-other-routes.md +0 -23
@@ -35,5 +35,5 @@ part of cleanup — this step is project hygiene, not a build.
35
35
  Templates are structure-only starting points. Without this pass, clones carry a
36
36
  misleading README, a placeholder package name, and (historically) a gitignored
37
37
  lockfile — all of which leak template assumptions into a real project. Running
38
- it immediately after clone keeps every Pikku project, OSS or Fabric, starting
38
+ it immediately after clone keeps every Pikku project starting
39
39
  from a clean, honest baseline.
@@ -1,16 +1,26 @@
1
1
  # Build an app on Pikku, fast
2
2
 
3
+ ## Contents
4
+
5
+ - Agent Operating Procedure
6
+ - 1. One question, then build
7
+ - 2. Virtual users — 60 seconds, not optional
8
+ - 3. Build
9
+ - 4. Look at it — actually
10
+ - 5. One smoke scenario
11
+ - 6. Hand it over honestly
12
+
3
13
  You have a scaffolded project with skills installed. Get it to working, seeded,
4
14
  signed-in screens in as few steps as possible.
5
15
 
6
16
  **What this mode deliberately skips**, and what that costs:
7
17
 
8
- | Skipped | Cost |
9
- |---|---|
10
- | `knowledge/` | Another agent — or you next week — cannot resume this. Nothing records *why*. |
11
- | Changesets and plans | No build order, no per-piece proof. Fine at this size, painful past it. |
12
- | Design direction | It will look like the template. |
13
- | Refusal scenarios | Access control is asserted, not proven. |
18
+ | Skipped | Cost |
19
+ | -------------------- | ----------------------------------------------------------------------------- |
20
+ | `knowledge/` | Another agent — or you next week — cannot resume this. Nothing records _why_. |
21
+ | Changesets and plans | No build order, no per-piece proof. Fine at this size, painful past it. |
22
+ | Design direction | It will look like the template. |
23
+ | Refusal scenarios | Access control is asserted, not proven. |
14
24
 
15
25
  **Say this out loud to the user, once, when you finish.** A quick build that gets
16
26
  mistaken for a real one is the only way this mode does damage. §6 is the way out.
@@ -42,12 +52,12 @@ Identifiers are English in every project, whatever the product's market;
42
52
  `template`, which the Console renders) stays `en` unless the user already told
43
53
  you otherwise. If the request says the app's UI is not English, that is the
44
54
  message catalogue only: add the locale and set `defaultLocale`, and leave
45
- `baseLocale` at `en`. `references/app.md` §1a has the three axes in full; getting
55
+ `baseLocale` at `en`. the app workflow's §1a has the three axes in full; getting
46
56
  them confused is how a project ends up unable to add a second language.
47
57
 
48
- ## 2. Personas — 60 seconds, not optional
58
+ ## 2. Virtual users — 60 seconds, not optional
49
59
 
50
- `packages/functions/src/personas.ts` ships with a `visitor`. Add one persona per
60
+ `packages/functions/src/virtual-users.ts` ships with a `visitor`. Add one virtual user per
51
61
  kind of person, plus **a second one of the primary kind** — that is what makes
52
62
  "you see yours, not theirs" observable when you click around.
53
63
 
@@ -55,10 +65,14 @@ kind of person, plus **a second one of the primary kind** — that is what makes
55
65
  rule, and it lives in each function's `permissions`, not in a role:
56
66
 
57
67
  ```typescript
58
- import { definePersonas } from '#pikku/scopes/pikku-personas.gen.js'
68
+ import { defineVirtualUsers } from '#pikku/scopes/pikku-virtual-users.gen.js'
59
69
 
60
- definePersonas({
61
- visitor: { name: 'Visitor', jobTitle: 'Synthetic health-check user', account: {} },
70
+ defineVirtualUsers({
71
+ visitor: {
72
+ name: 'Visitor',
73
+ jobTitle: 'Synthetic health-check user',
74
+ account: {},
75
+ },
62
76
  amina: { name: 'Amina', jobTitle: 'Gardener', account: {} },
63
77
  bilal: { name: 'Bilal', jobTitle: 'Gardener', account: {} },
64
78
  })
@@ -68,16 +82,28 @@ definePersonas({
68
82
  named:
69
83
 
70
84
  ```typescript
71
- import { definePersonas } from '#pikku/scopes/pikku-personas.gen.js'
85
+ import { defineVirtualUsers } from '#pikku/scopes/pikku-virtual-users.gen.js'
72
86
  import { defineSystemRole } from '#pikku/scopes'
73
87
 
74
88
  defineSystemRole({
75
- owner: { displayName: 'Owner', description: 'Sees only their own rows', scopes: [] },
76
- tenant: { displayName: 'Tenant', description: 'Sees only their own tenancy', scopes: [] },
89
+ owner: {
90
+ displayName: 'Owner',
91
+ description: 'Sees only their own rows',
92
+ scopes: [],
93
+ },
94
+ tenant: {
95
+ displayName: 'Tenant',
96
+ description: 'Sees only their own tenancy',
97
+ scopes: [],
98
+ },
77
99
  })
78
100
 
79
- definePersonas({
80
- visitor: { name: 'Visitor', jobTitle: 'Synthetic health-check user', account: {} },
101
+ defineVirtualUsers({
102
+ visitor: {
103
+ name: 'Visitor',
104
+ jobTitle: 'Synthetic health-check user',
105
+ account: {},
106
+ },
81
107
  amina: { name: 'Amina', jobTitle: 'Owner', roles: ['owner'], account: {} },
82
108
  bilal: { name: 'Bilal', jobTitle: 'Owner', roles: ['owner'], account: {} },
83
109
  chidi: { name: 'Chidi', jobTitle: 'Tenant', roles: ['tenant'], account: {} },
@@ -87,10 +113,10 @@ definePersonas({
87
113
  Two owners in both examples, deliberately: one owner cannot demonstrate that
88
114
  owners are separated from each other.
89
115
 
90
- - **Keep `visitor`.** The shipped scenarios name `actors.visitor`; removing it
116
+ - **Keep `visitor`.** The shipped scenarios name `virtualUsers.visitor`; removing it
91
117
  fails `pikku all` and nothing you write after that registers.
92
- - **One `definePersonas` call for the whole project.**
93
- - **Never write an email address** — each is derived from the persona id and
118
+ - **One `defineVirtualUsers` call for the whole project.**
119
+ - **Never write an email address** — each is derived from the virtual user id and
94
120
  `scenarios.emailDomain` in `pikku.config.json`.
95
121
  - `roles` is typechecked against `defineSystemRole`; an undeclared role is a
96
122
  build error.
@@ -112,7 +138,7 @@ Then, in this order — it is the order codegen depends on:
112
138
  the file is plain `INSERT`s — no `ON CONFLICT DO NOTHING`. It is local only:
113
139
  no deploy applies it, so anything the app cannot run without belongs in a
114
140
  migration instead. **Be generous, and
115
- seed rows for both personas.** An empty app demos badly, and you cannot see a
141
+ seed rows for both virtual users.** An empty app demos badly, and you cannot see a
116
142
  layout break against zero rows.
117
143
  3. **Functions** — one `pikkuFunc` per `*.function.ts`, `expose: true`. Pikku
118
144
  generates the typed RPC client and React Query hooks; you do NOT write HTTP
@@ -150,17 +176,15 @@ than they save:
150
176
  keystroke now and a rewrite later.
151
177
  - Never hardcode a host or port — the API base resolves to same-origin `/api`.
152
178
 
153
- Before the first run, make sure `.env` holds both local secrets — `bun run dev`
154
- appends missing ones, but an older scaffold or a copied `.env` may not have them:
179
+ Before the first run, make sure `.env` holds the local secret — `bun run dev`
180
+ appends it if missing, but an older scaffold or a copied `.env` may not have it:
155
181
 
156
182
  ```sh
157
183
  grep -q '^BETTER_AUTH_SECRET=' .env 2>/dev/null || echo "BETTER_AUTH_SECRET=$(openssl rand -base64 32)" >> .env
158
- grep -q '^SCENARIO_ACTOR_SECRET=' .env 2>/dev/null || echo "SCENARIO_ACTOR_SECRET=$(openssl rand -base64 32)" >> .env
159
184
  ```
160
185
 
161
186
  Never commit `.env`. Then run it — through `bun run dev`, never `vite` on its
162
- own, because the dev script is what hands the frontend the persona list for the
163
- "Sign in as …" switcher; without it the switcher silently renders nothing:
187
+ own, because the dev script is what points the frontend at your API:
164
188
 
165
189
  ```sh
166
190
  bun run prebuild && bun run dev
@@ -186,22 +210,35 @@ taller than the viewport. It is the most likely width your demo gets opened at.
186
210
  If you have five spare minutes, `npx impeccable install` (Node 22.18+) scores
187
211
  each screen against interaction heuristics and names what is wrong. Feed it
188
212
  screenshots, not source. It will polish the default look; it will not give the
189
- app a look — that is `references/app.md` §8a.
213
+ app a look — that is the app workflow's §8a.
190
214
 
191
215
  ## 5. One smoke scenario
192
216
 
193
- Not the full ladder — one journey, end to end, as a real persona, so the app has
217
+ Not the full ladder — one journey, end to end, as a real virtual user, so the app has
194
218
  at least one thing that stays true.
195
219
 
196
220
  ```typescript
197
221
  import { pikkuScenario } from '#pikku/scenarios'
198
222
 
199
- export const ownerCreatesAndSeesItScenario = pikkuScenario<void, { id: string }>({
223
+ export const ownerCreatesAndSeesItScenario = pikkuScenario<
224
+ void,
225
+ { id: string }
226
+ >({
200
227
  title: 'An owner creates a thing and sees it',
201
228
  tags: ['scenario', 'smoke'],
202
- func: async (_services, _data, { scenario, actors }) => {
203
- const row = await scenario.do('creates', 'createThing', { name: 'first' }, { actor: actors.amina })
204
- await scenario.then('sees it listed', 'thingShowsInList', { id: row.id }, { actor: actors.amina })
229
+ func: async (_services, _data, { scenario, virtualUsers }) => {
230
+ const row = await scenario.do(
231
+ 'creates',
232
+ 'createThing',
233
+ { name: 'first' },
234
+ { virtualUser: virtualUsers.amina }
235
+ )
236
+ await scenario.then(
237
+ 'sees it listed',
238
+ 'thingShowsInList',
239
+ { id: row.id },
240
+ { virtualUser: virtualUsers.amina }
241
+ )
205
242
  return { id: row.id }
206
243
  },
207
244
  })
@@ -211,9 +248,9 @@ export const ownerCreatesAndSeesItScenario = pikkuScenario<void, { id: string }>
211
248
  `pikkuScenarioStep`.** An RPC name in a `then` will not resolve.
212
249
  - **Every scenario must assert.** A ladder with no `then` is a PKU680 critical —
213
250
  it fails `pikku all`, stopping codegen rather than a test.
214
- - **`SCENARIO_ACTOR_SECRET` must be in `.env`** (above). Without it
215
- `/api/auth/sign-in/actor` is disabled and every scenario fails at sign-in, for
216
- a reason that reads like an auth bug.
251
+ - **Run scenarios against a `pikku dev` server.** `pikku dev` creates the virtual
252
+ user signing key; without a running or previously started dev server every
253
+ scenario fails at sign-in, for a reason that reads like an auth bug.
217
254
  - **There is no state reset** — scope what you create to unique ids.
218
255
 
219
256
  Keep the three shipped scenarios in `packages/functions/test/scenarios/` green.
@@ -230,16 +267,16 @@ it is seeded with, and that this is a quick build — no knowledge base, no
230
267
  changesets, no design pass, access control clicked-through rather than proven.
231
268
 
232
269
  **Upgrading to a real build is additive, not a rewrite.** If they want it, switch
233
- to `references/app.md` and do this, in order:
270
+ to the app workflow (the app reference, see SKILL.md) and do this, in order:
234
271
 
235
272
  1. Write `knowledge/` for what already exists — `entities/` for what you built,
236
273
  `decisions/` for what you chose silently, `questions/` for what you guessed
237
274
  at. Then `pikku knowledge index && pikku knowledge validate`.
238
275
  2. Backfill an entity note for each thing the app holds, so `pikku knowledge
239
- gaps` has something to measure against.
240
- 3. Write the refusal scenarios — the ones proving one persona cannot reach
276
+ gaps` has something to measure against.
277
+ 3. Write the refusal scenarios — the ones proving one virtual user cannot reach
241
278
  another's rows. This is the gap that matters most.
242
- 4. Then pick up `references/app.md` at its §4 (apps) or §5 (changes) for
279
+ 4. Then pick up the app workflow at its §4 (apps) or §5 (changes) for
243
280
  anything new.
244
281
 
245
282
  Nothing built here has to be thrown away to do that — which is the whole reason
@@ -1,7 +1,18 @@
1
1
  # Scenarios — writing journeys that stay proven
2
2
 
3
- A scenario is a user journey run as one of your personas, over the real transport, with that
4
- persona's session. It is the only kind of test worth writing here, because a passing one proves the
3
+ ## Contents
4
+
5
+ - The shape of a scenario
6
+ - Extraction: what survives, and what silently does not
7
+ - What to assert
8
+ - There is no state reset
9
+ - Steps rot as the app grows
10
+ - Browser scenarios
11
+ - Running them
12
+ - Coverage — which functions have actually been run
13
+
14
+ A scenario is a user journey run as one of your virtual users, over the real transport, with that
15
+ virtual user's session. It is the only kind of test worth writing here, because a passing one proves the
5
16
  app works the way a signed-in person experiences it.
6
17
 
7
18
  The traps below all cost a real changeset a red run, and most of them are invisible on the run that
@@ -28,26 +39,27 @@ import { pikkuScenario } from '#pikku/scenarios'
28
39
 
29
40
  export const tenantReportsAFaultScenario = pikkuScenario<void, { id: string }>({
30
41
  title: 'A tenant reports a fault and the owner sees it',
31
- description: 'The report lands on the owning landlord’s queue, and nobody else’s',
42
+ description:
43
+ 'The report lands on the owning landlord’s queue, and nobody else’s',
32
44
  tags: ['scenario', 'maintenance'],
33
- func: async (_services, _data, { scenario, actors }) => {
45
+ func: async (_services, _data, { scenario, virtualUsers }) => {
34
46
  const report = await scenario.do(
35
47
  'reports a broken boiler',
36
48
  'createMaintenanceReport',
37
49
  { summary: 'No hot water' },
38
- { actor: actors.chidi },
50
+ { virtualUser: virtualUsers.chidi }
39
51
  )
40
52
  await scenario.then(
41
53
  'appears on the owner’s queue',
42
54
  'reportShowsOnQueue',
43
55
  { id: report.id },
44
- { actor: actors.amina },
56
+ { virtualUser: virtualUsers.amina }
45
57
  )
46
58
  await scenario.then(
47
59
  'is invisible to the other owner',
48
60
  'reportIsNotVisible',
49
61
  { id: report.id },
50
- { actor: actors.bilal },
62
+ { virtualUser: virtualUsers.bilal }
51
63
  )
52
64
  return { id: report.id }
53
65
  },
@@ -59,10 +71,10 @@ export const tenantReportsAFaultScenario = pikkuScenario<void, { id: string }>({
59
71
  (server-side by default, plus a `browser` one that drives the page). Reaching for an RPC name in a
60
72
  `then` will not resolve.
61
73
 
62
- **`SCENARIO_ACTOR_SECRET` must be in `.env`** (app.md §6, before the first run). Without it
63
- `/api/auth/sign-in/actor` is disabled — every scenario then fails at sign-in, before its first step,
64
- for a reason that reads like an auth bug. `pikku scenario run` reads it from the environment, so
65
- source `.env` first (`set -a && . ./.env && set +a`) when you run outside `bun run dev`.
74
+ **Scenarios sign in with the key `pikku dev` created.** Start `pikku dev` once in the project (it writes
75
+ `.pikku-runtime/virtual-user/signing.key`); without that key every scenario fails at sign-in, before its
76
+ first step, for a reason that reads like an auth bug. Against a deployed stage export
77
+ `VIRTUAL_USER_SIGNING_KEY`, which that stage's deployer holds.
66
78
 
67
79
  ---
68
80
 
@@ -88,7 +100,7 @@ each half of that rule, because the first half does not imply the second.
88
100
 
89
101
  **Write each scenario's setup out in full rather than sharing a local helper.** A helper holding
90
102
  setup steps does not fail extraction — the steps are recorded and the suite goes green — but the
91
- extractor cannot bind an `actor` that arrives as a function parameter, so the transcript credits
103
+ extractor cannot bind an `virtualUser` that arrives as a function parameter, so the transcript credits
92
104
  every setup step to whoever the last literal binding named. Seven permission scenarios here read
93
105
  "henrik sets up her company Salon Nordlicht" in a suite whose whole point was that the company is
94
106
  finja's. A scenario body is a recorded document, and the duplication is the price of it saying who
@@ -98,7 +110,7 @@ did what.
98
110
 
99
111
  ## What to assert
100
112
 
101
- **Write the refusals, and assert WHY.** One persona reaching for another's row has to be rejected,
113
+ **Write the refusals, and assert WHY.** One virtual user reaching for another's row has to be rejected,
102
114
  and that rejection is a scenario — it is how you prove access control instead of asserting it. But
103
115
  only if the step reads the reason: "not ok" is also what a malformed request returns, so a refusal
104
116
  step that stops at the status code passes on a call the function never even ran. One here posted
@@ -139,7 +151,7 @@ second run starts where its first one stopped.
139
151
  **Run the suite twice and require the second run green.** A suite that only passes on a fresh
140
152
  database is a suite that passes once. Two corollaries, both of which cost a changeset a red run:
141
153
 
142
- - **Name nothing a setup step might already own.** `setsUpHerCompany` returns the company that actor
154
+ - **Name nothing a setup step might already own.** `setsUpHerCompany` returns the company that virtual user
143
155
  already has rather than renaming it, so a later step passed the literal name it had asked for and
144
156
  was told no such company exists. Read the name, slug or id back off the step's output and pass
145
157
  THAT.
@@ -176,7 +188,7 @@ scenarios that had been green for months.
176
188
 
177
189
  **A selector on IDENTITY alone rots the same way once rows gain a lifecycle.** One reused the first
178
190
  licence assigned to an email regardless of its state, which was correct until a new changeset's
179
- refusal scenarios left that actor holding cancelled ones — it then handed back a dead licence, read
191
+ refusal scenarios left that virtual user holding cancelled ones — it then handed back a dead licence, read
180
192
  as success, and failed a scenario two changesets older at a step that needed a live one.
181
193
 
182
194
  **A step's input is a recorded contract, and it does not take a version.** Widening one — an extra
@@ -240,7 +252,7 @@ bunx --bun pikku scenario run local --spawn # server-side, the
240
252
  bunx --bun pikku scenario run local --spawn --run browser # the same journeys, driven as a human
241
253
  ```
242
254
 
243
- In a multi-app project that one run covers both frontends: each persona carries
255
+ In a multi-app project that one run covers both frontends: each virtual user carries
244
256
  its own `app` and `@pikku/playwright` resolves the base url from the
245
257
  environment's `appUrls` map, so there is no second environment to run.
246
258
 
@@ -1,4 +1,9 @@
1
- # Shipping, and staying Fabric-ready
1
+ # Shipping, and staying deploy-ready
2
+
3
+ ## Contents
4
+
5
+ - Ship it — open source, no platform
6
+ - Staying deploy-ready
2
7
 
3
8
  Read this when the changes queue is empty and the scenarios are green — it is
4
9
  the last phase, and nothing in it is needed before then.
@@ -81,7 +86,7 @@ Changing the strategy renames units, and a unit name is what a queue consumer, a
81
86
  scheduled task and a `dependsOn` point at. The manifest rewrites all three, but
82
87
  anything holding a unit name outside the manifest does not follow. Units dropped
83
88
  from the manifest are not deleted by OSS `deploy()` either (pikkujs/pikku#543) —
84
- fabric sweeps its dispatch namespace after each deploy, plain pikku leaves the
89
+ a hosting platform may sweep them after each deploy, plain pikku leaves the
85
90
  old workers in place.
86
91
 
87
92
  `tags` matches the tags a function inherits from its wirings — `wireHTTP({ ...,
@@ -114,7 +119,7 @@ one hostname, first-party cookies — give its `frontends` entry `"serve": {}`
114
119
  and a standalone deploy then mount its built `dist`. Pikku never builds it, so
115
120
  build the frontend first. Only one entry may set `serve`.
116
121
 
117
- To ship an app as a desktop or Android app, read `references/native-app.md`.
122
+ To ship an app as a desktop or Android app, read the native-app reference (see SKILL.md).
118
123
 
119
124
  Before shipping, run the full gate:
120
125
 
@@ -153,23 +158,22 @@ lint over function return types, catching a `Pii`/`Secret` field that leaks
153
158
  through an exposed function. Expensive, so run it before a release rather than on
154
159
  every save — but run it.
155
160
 
156
- ## Staying Fabric-ready
161
+ ## Staying deploy-ready
157
162
 
158
163
  Everything above is open source. This is the contract that keeps
159
- `pikku fabric init` a one-command import later, instead of a migration.
164
+ the project a one-command import for a hosting platform later, instead of a migration.
160
165
 
161
166
  - **`frontends` in `pikku.config.json` describes reality.** Every app has an
162
167
  entry with the right `cwd`, `kind` and `dev` (`command`, `port`); exactly one
163
- is `primary`; `serves` and `personas` name real personas from the personas
164
- section.
165
- - **`fabric.projectId` in `pikku.config.json` is optional.** Leave it out unless you
166
- have the real id; the CLI writes it after finding the project from the git
167
- remote. Do not invent a value.
168
- - **One `definePersonas` call**, every persona reachable through exactly one
169
- frontend. Fabric materialises these as its virtual users; a persona nobody
168
+ is `primary`; `serves` and `virtualUsers` name real virtual users declared in
169
+ `defineVirtualUsers`.
170
+ - **`cloud.projectId` in `pikku.config.json` is optional.** Leave it out unless you
171
+ have the real id; the CLI writes it once the project is linked. Do not invent a value.
172
+ - **One `defineVirtualUsers` call**, every virtual user reachable through exactly one
173
+ frontend. A hosting platform may materialise these as its users; a virtual user nobody
170
174
  serves imports as a person with no way in.
171
175
  - **`knowledge/` passes `validate`, and `pikku knowledge gaps` lists nothing.** This is
172
- the part Fabric itself reads and continues from.
176
+ the part a hosting platform reads and continues from.
173
177
  - **Every plan under `knowledge/plans/` passes `pikku knowledge plan progress`.**
174
178
  A merged changeset is a claim; the plan reconciled against the generated meta
175
179
  is the check. Anything the first pass still owes is either built now or deferred with
@@ -187,7 +191,6 @@ Everything above is open source. This is the contract that keeps
187
191
  the SDK.
188
192
  - **`pikku validate` is clean**, and `pikku all` has no critical diagnostics.
189
193
 
190
- What you deliberately do NOT do: run any `pikku fabric` subcommand, add a card,
191
- or link a project. None of it is needed to build, test, critique, or deploy — it
192
- is needed the day the user wants Fabric to host and build it, and on that day, if
194
+ What you deliberately do NOT do: link a project to a hosting platform. None of it is needed to build, test, critique, or deploy — it
195
+ is needed the day the user wants one to host and build it, and on that day, if
193
196
  this section holds, that day is one command long.