@pikku/skills 0.12.54 → 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 (54) 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-architect/SKILL.md +1 -1
  6. package/skills/pikku-architect/references/plan-defects.md +7 -7
  7. package/skills/pikku-auth/references/better-auth.md +92 -96
  8. package/skills/pikku-build/SKILL.md +9 -9
  9. package/skills/pikku-build/references/app.md +56 -61
  10. package/skills/pikku-build/references/design.md +14 -14
  11. package/skills/pikku-build/references/feature.md +3 -3
  12. package/skills/pikku-build/references/mocks.md +6 -0
  13. package/skills/pikku-build/references/multi-app.md +16 -16
  14. package/skills/pikku-build/references/openapi.md +34 -34
  15. package/skills/pikku-build/references/platform.md +4 -5
  16. package/skills/pikku-build/references/post-clone.md +1 -1
  17. package/skills/pikku-build/references/quick.md +63 -36
  18. package/skills/pikku-build/references/scenarios.md +17 -16
  19. package/skills/pikku-build/references/ship.md +14 -16
  20. package/skills/pikku-build/references/theming.md +2 -2
  21. package/skills/pikku-changes/SKILL.md +2 -5
  22. package/skills/pikku-concepts/SKILL.md +26 -27
  23. package/skills/pikku-concepts/references/language.md +6 -6
  24. package/skills/pikku-deploy/references/nextjs.md +1 -1
  25. package/skills/pikku-guide/SKILL.md +18 -17
  26. package/skills/pikku-i18n/references/messages.md +5 -6
  27. package/skills/pikku-knowledge/SKILL.md +3 -3
  28. package/skills/pikku-meta/references/audit.md +1 -1
  29. package/skills/pikku-permissions/SKILL.md +1 -1
  30. package/skills/pikku-react/SKILL.md +18 -20
  31. package/skills/pikku-react/references/client.md +2 -37
  32. package/skills/pikku-report/SKILL.md +10 -10
  33. package/skills/pikku-scenario/SKILL.md +63 -66
  34. package/skills/pikku-scenario/references/browser.md +5 -5
  35. package/skills/pikku-scenario/references/coverage.md +4 -4
  36. package/skills/pikku-scenario/references/steps.md +31 -30
  37. package/skills/pikku-scenario/references/{persona-run.md → virtual-user-run.md} +38 -43
  38. package/skills/pikku-scenario/references/virtual-users.md +114 -0
  39. package/skills/pikku-seo/SKILL.md +1 -1
  40. package/skills/pikku-services/references/audit-wire-service.md +2 -2
  41. package/skills/pikku-services/references/audit.md +1 -2
  42. package/skills/pikku-services/references/services.md +1 -1
  43. package/skills/pikku-software-archaeology/SKILL.md +13 -13
  44. package/skills/pikku-software-archaeology/references/blueprint.schema.json +9 -9
  45. package/skills/pikku-theme/SKILL.md +1 -1
  46. package/skills/pikku-workflow/SKILL.md +1 -1
  47. package/skills/pikku-admin-to-fabric/SKILL.md +0 -198
  48. package/skills/pikku-blueprint-to-fabric/SKILL.md +0 -362
  49. package/skills/pikku-blueprint-to-fabric/scripts/inventory.mjs +0 -367
  50. package/skills/pikku-fabric/SKILL.md +0 -331
  51. package/skills/pikku-fabric/references/database.md +0 -128
  52. package/skills/pikku-fabric/references/debugging.md +0 -114
  53. package/skills/pikku-fabric/references/deploy.md +0 -179
  54. package/skills/pikku-scenario/references/personas.md +0 -123
package/dist/snippets.js CHANGED
@@ -91,7 +91,7 @@ export const collectSnippets = async (projectDir, into = new Map(), origins = ne
91
91
  /**
92
92
  * A fence that names a region instead of carrying code:
93
93
  *
94
- * ```ts snippet:definePersonas
94
+ * ```ts snippet:defineVirtualUsers
95
95
  * ```
96
96
  *
97
97
  * The language is optional and kept. Marked fences are replaced whole — both
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/skills",
3
- "version": "0.12.54",
3
+ "version": "0.12.55",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/pikkujs/pikku.git",
@@ -285,7 +285,7 @@ one, and is worth opening for any question you cannot answer with a flat yes.
285
285
  2. **Does pass 1 slice, and does the model fit inside it?** Not "pass 1: the data model, pass 2: the
286
286
  API" — and `model` holds only the tables pass 1 or 2 actually migrates, because the model slot has
287
287
  no passes and a later table is a PROBLEM from the first day.
288
- 3. **Can each scenario actually be performed?** Name the persona; assert what the person got rather
288
+ 3. **Can each scenario actually be performed?** Name the virtual user; assert what the person got rather
289
289
  than that the code ran; write totals as deltas against a database nobody resets; check that every
290
290
  input the prose describes is a field somebody planned.
291
291
  4. **Does something produce every state and field the plan reads?** For each clause of a description,
@@ -39,8 +39,8 @@ a second changeset — say so in `covers`.
39
39
 
40
40
  ## Can each scenario actually be performed?
41
41
 
42
- **Ask which persona performs it. If the answer is "nobody", it belongs one level up or to no pass
43
- at all.** The runner drives the app as the personas the project declares; there is no anonymous
42
+ **Ask which virtual user performs it. If the answer is "nobody", it belongs one level up or to no pass
43
+ at all.** The runner drives the app as the virtual users the project declares; there is no anonymous
44
44
  RPC caller, so "a caller with no session is refused" is a sentence a backend or permission
45
45
  scenario cannot perform. Signed-out is a BROWSER fact.
46
46
 
@@ -80,7 +80,7 @@ decision that nothing records.
80
80
  in this plan, or one already in the meta. Write "when the distributor is removed its companies
81
81
  fall back to the direct catalog" with no function that removes a distributor and `plan set`
82
82
  accepts it, `plan progress` passes, and the changeset ships a sentence nothing proves. Read each
83
- description back asking *which function does this*, and cut the half you cannot name.
83
+ description back asking _which function does this_, and cut the half you cannot name.
84
84
 
85
85
  **Where a scenario calls the same function twice, check it for a per-period guard first.** A
86
86
  one-per-day, one-per-order or one-per-person rule makes the second call a refusal, so the journey
@@ -101,14 +101,14 @@ mark a product's countries, cannot be proven without amending the plan mid-build
101
101
  **Every state a scenario waits in needs the function that leaves the world like that — checked
102
102
  against the code ALREADY built.** Three failure shapes, in rising order of cost:
103
103
 
104
- - *Stale*: a staff queue of "paid orders with no invoice yet", where an earlier changeset had
104
+ - _Stale_: a staff queue of "paid orders with no invoice yet", where an earlier changeset had
105
105
  moved invoicing to checkout. No order a customer could place was ever in that state; three
106
106
  scenarios passed once against old rows, then failed.
107
- - *Unreachable*: staff asked to "capture the payment on an authorised order" when no function in
107
+ - _Unreachable_: staff asked to "capture the payment on an authorised order" when no function in
108
108
  the app or its addons could put an order in `authorized` — the addon only holds money when
109
109
  checkout was started with a flag the plan's own checkout input did not carry.
110
- - *Wrong person*: a distributor salon owner planned to buy from a catalogue that is scoped by
111
- distributor and does not show her the product. Actors are not interchangeable.
110
+ - _Wrong person_: a distributor salon owner planned to buy from a catalogue that is scoped by
111
+ distributor and does not show her the product. Virtual users are not interchangeable.
112
112
 
113
113
  **A `model` slot saying "this changeset adds no table" has to be true of the DATA the scenarios
114
114
  read, not only of the entities they name.** One promised a checkout priced by delivery country — a
@@ -101,9 +101,9 @@ export const auth = pikkuBetterAuth(async ({ secrets }) => {
101
101
 
102
102
  ## ⚠️ Stateless session — ALWAYS enable `cookieCache` for deployed apps
103
103
 
104
- By default the CLI wires the **stateful** `betterAuthSession` bridge globally — it calls `services.auth()`, so EVERY unit/worker bundles the full better-auth server (~2.5MB each). On per-unit deploy targets (Fabric/Cloudflare) that bloats every bundle and the serial upload phase.
104
+ By default the CLI wires the **stateful** `betterAuthSession` bridge globally — it calls `services.auth()`, so EVERY unit/worker bundles the full better-auth server (~2.5MB each). On per-unit deploy targets (Pikku Studio Cloud/Cloudflare) that bloats every bundle and the serial upload phase.
105
105
 
106
- Enabling `session: { cookieCache: { enabled: true } }` makes the CLI split out a lean `betterAuthStatelessSession` (`src/scaffold/auth-middleware.gen.ts`) that verifies the signed session cookie using only `BETTER_AUTH_SECRET` — no `services.auth()`, no server bundled. Non-auth units drop from ~2.5MB to ~20KB. Only the auth unit carries the server. `pikku fabric validate` warns (`better-auth-stateless-session-disabled`) when it's off.
106
+ Enabling `session: { cookieCache: { enabled: true } }` makes the CLI split out a lean `betterAuthStatelessSession` (`src/scaffold/auth-middleware.gen.ts`) that verifies the signed session cookie using only `BETTER_AUTH_SECRET` — no `services.auth()`, no server bundled. Non-auth units drop from ~2.5MB to ~20KB. Only the auth unit carries the server. `pikku validate` warns (`better-auth-stateless-session-disabled`) when it's off.
107
107
 
108
108
  **Tradeoff:** server-side session revocation isn't seen until the cookie cache expires (sign-out is still immediate — it deletes the cookie).
109
109
 
@@ -244,13 +244,13 @@ like any other. None is automatic — an app wires the ones it needs.
244
244
  | Plugin | Plugin `id` | Adds | Use it when |
245
245
  | ------------------------ | ------------------ | ----------------------------------------------------------------------- | ------------------------------------------------------------- |
246
246
  | `pikkuBan()` | `pikku-ban` | `user.banned/banReason/banExpires` | You ban users (the schema + enforcement half of the above) |
247
- | `pikkuActor()` | `actor` | `POST /sign-in/actor`, `user.actor` | Scenarios or a dev switcher sign in as a persona |
247
+ | `pikkuVirtualUser()` | `virtualUser` | `POST /sign-in/virtual-user`, `user.virtualUser` | Scenarios sign in as a virtual user |
248
248
  | `pikkuCredentialOAuth()` | `credential-oauth` | `POST /credential-oauth/link`, `/credential-oauth/callback/:providerId` | An app links OAuth2 **API credentials** for a user |
249
249
  | `pikkuDelegatedAuth()` | `delegated-auth` | `POST /sign-in/delegated` | An imported upstream API is the system of record for identity |
250
250
 
251
251
  Every one carries a `pikku` prefix, because a `plugins: [...]` array mixes these
252
- with better-auth's own and a bare `actor()` next to `organization()` says
253
- nothing about where it came from. The unprefixed names — `ban`, `actor`,
252
+ with better-auth's own and a bare `virtualUser()` next to `organization()` says
253
+ nothing about where it came from. The unprefixed names — `ban`, `virtualUser`,
254
254
  `credentialOAuth`, `delegatedAuth` — are still exported as deprecated
255
255
  aliases, so existing apps keep working.
256
256
 
@@ -342,7 +342,7 @@ an existing user row, so it cannot take over someone else's account.
342
342
  For an addon generated from an OpenAPI spec, none of this is written by hand:
343
343
  `pikku new addon --openapi … --auth-config <file>` generates
344
344
  `authenticate<Name>Upstream` in the addon and wires this plugin, the stored
345
- credential and the actor credentials into `src/auth.ts`. The config format is
345
+ credential and the virtual user credentials into `src/auth.ts`. The config format is
346
346
  in the `pikku-build` skill's `references/openapi.md`. When the upstream later
347
347
  refuses the stored token, the addon throws `CredentialRejectedError` (403,
348
348
  `reauth: 'sign-in'`): the UI shows the sign-in again.
@@ -455,154 +455,150 @@ hostname check — because a one-request path to an admin session is exactly the
455
455
  thing that must not survive a deploy. An app that has not declared the `admin`
456
456
  scope still gets a session, with a warning, since a scopeless dev user is useful.
457
457
 
458
- ### Actor sign-in (`actor` plugin)
458
+ ### Virtual user sign-in (`virtualUser` plugin)
459
459
 
460
460
  A different thing from dev quick login, and the one to reach for when "sign in as
461
461
  someone" means **a particular kind of user** rather than one fixed admin.
462
462
  Register it explicitly — it is not automatic:
463
463
 
464
464
  ```typescript
465
- import { ACTOR_SIGN_IN_OPT_IN_ENV, pikkuActor } from '@pikku/better-auth'
465
+ import {
466
+ VIRTUAL_USER_SIGN_IN_OPT_IN_ENV,
467
+ pikkuVirtualUser,
468
+ } from '@pikku/better-auth'
466
469
 
467
470
  plugins: [
468
- pikkuActor({
469
- secret: SCENARIO_ACTOR_SECRET,
470
- allowSignIn: await variables.get(ACTOR_SIGN_IN_OPT_IN_ENV),
471
+ pikkuVirtualUser({
472
+ verifyKey: await variables.get('VIRTUAL_USER_VERIFY_KEY'),
473
+ allowSignIn: await variables.get(VIRTUAL_USER_SIGN_IN_OPT_IN_ENV),
471
474
  }),
472
475
  ]
473
476
  ```
474
477
 
475
- `POST ${basePath}/sign-in/actor` `{ email, secret, name? }` → 200 + the normal
476
- session cookie. The plugin's `secret` is the **root**, and it may be a (possibly
477
- async) function so it can come off the secrets service instead of a captured
478
- value.
479
-
480
- **What a caller presents is not the root.** It is
481
- `deriveActorSecret(root, email)` from `@pikku/core/services` — HKDF-expanded
482
- HMAC-SHA256 over the lowercased address. The endpoint re-derives the expected
483
- value for the address being signed in as and compares, so a credential minted
484
- for one persona is refused for every other, and the root itself is never a valid
485
- credential. A root under 32 characters refuses the endpoint outright rather than
486
- deriving weak credentials from it (the server log names the problem; the client
487
- is not told which). Callers rarely derive by hand — `pikku persona secret <id>`
488
- mints them for a run, and the two `PersonaSignIn` implementations derive on the
489
- fly. The browser switcher holds none: it signs in through `/sign-in/persona`.
490
-
491
- **Which command is running decides whether it works, not whether a secret is
492
- set.** `pikku dev` sets `PIKKU_DEV_ACTOR_SIGN_IN` and mints an ephemeral
493
- `SCENARIO_ACTOR_SECRET` for the run, so local development needs no configuration
494
- at all. Everywhere else the endpoint refuses (`Actor sign-in is disabled outside
495
- \`pikku dev\``) — `pikku serve` clears the marker outright, so a secret that
496
- leaked into a production environment enables nothing and gets a warning naming
497
- itself instead.
478
+ There is one door, and two ways through it.
479
+
480
+ - **A run signs in from inside the app.** A scheduled virtual user run calls
481
+ `auth.api.signInVirtualUserInternal`, which is `SERVER_ONLY`: it exists on
482
+ `auth.api` and answers 404 through `auth.handler`, so no HTTP caller can reach
483
+ it. The run takes the cookie and then drives the app over HTTP.
484
+ - **An outside caller presents one signed statement.** `pikku dev`, `pikku
485
+ scenario run`, `pikku virtual-user run`, `pikku pages screenshot --as` and the
486
+ Playwright provider `POST ${basePath}/sign-in/virtual-user` `{ statement }` →
487
+ 200 + the normal session cookie. The statement is an EdDSA JWT with `sub` (the
488
+ virtual user id), `aud` (the app's origin), a short expiry (180 seconds by
489
+ default, 300 at most) and a one-time `jti`. The plugin verifies it against
490
+ `verifyKey` with a verify-only service; a replayed `jti` is refused. The replay
491
+ record is in memory, so it is per process.
492
+
493
+ **Keys.** `pikku dev` generates a key pair under
494
+ `<runtimeDir>/virtual-user/signing.key` (mode 0600, next to a `.gitignore` of
495
+ `*`) and gives the app the public half, so local development needs no
496
+ configuration. A deployed stage has one key pair: the deployer holds the private
497
+ key (`VIRTUAL_USER_SIGNING_KEY`) and the app reads the public key from
498
+ `VIRTUAL_USER_VERIFY_KEY`. A stage without a verify key refuses statements and
499
+ logs why; it never falls back to anything weaker.
500
+
501
+ **Which command is running decides whether it works.** `pikku dev` sets
502
+ `PIKKU_DEV_VIRTUAL_USER_SIGN_IN`. Everywhere else the endpoint refuses
503
+ (`VirtualUser sign-in is disabled outside \`pikku dev\``) — `pikku serve` clears the
504
+ marker outright. In production neither endpoint is registered at all.
498
505
 
499
506
  A stage that genuinely must run scenarios opts in on purpose, with
500
- `PIKKU_ALLOW_ACTOR_SIGN_IN=passwordless-actor-sign-in`. Any other value is
507
+ `PIKKU_ALLOW_VIRTUAL_USER_SIGN_IN=passwordless-virtual-user-sign-in`. Any other value is
501
508
  ignored and warned about, so the hatch cannot be opened by copying a `true` from
502
509
  the line above.
503
510
 
504
511
  **Pass it in on any runtime without a populated `process.env`.** The gate reads
505
512
  the environment by default, which is enough for Node but not for a Worker: there
506
513
  the opt-in arrives as a binding and reaches user code through the variables
507
- service, so a gate left to `process.env` stays shut on exactly the stages a
508
- deployment targets. `allowSignIn` takes the value the caller already read —
509
- `await variables.get(ACTOR_SIGN_IN_OPT_IN_ENV)` — and is checked against the same
510
- literal, near-miss warning included. It is deliberately a value and not a flag:
511
- what opens the gate is still something the deployment set and an operator can
512
- read back out of it, never something compiled into the bundle. A value passed
513
- here is the one consulted, so the environment cannot quietly override what the
514
- stage was configured with.
514
+ service. `allowSignIn` takes the value the caller already read —
515
+ `await variables.get(VIRTUAL_USER_SIGN_IN_OPT_IN_ENV)` — and is checked against the same
516
+ literal, near-miss warning included. A value passed here is the one consulted, so
517
+ the environment cannot quietly override what the stage was configured with.
515
518
 
516
519
  **Signing in and provisioning are separate powers.** An unknown address becomes
517
- an `actor: true` row only under `pikku dev`. With the opt-in set, a stage signs
518
- in as the personas the deployment provisioned when it started and refuses
519
- everything else (`No actor account exists for that address`), so holding the
520
- secret on such a stage does not let anyone invent identities. Those rows are
521
- written by `provisionPersonas` when an operator sign-in route is asked to act
522
- as an address the stage has no account for, so provisioning needs no actor
523
- secret and works on a stage whose endpoint is shut.
524
-
525
- **`SCENARIO_ACTOR_SECRET` is a credential as powerful as the most privileged
526
- persona.** Provisioning grants declared roles to actor accounts, so an
527
- `admin` persona is an actor holding real admin — anyone with the secret _and_ the
528
- opt-in can take a session as one. Do not treat "actors only" as a licence to open
529
- the hatch in production.
520
+ a `virtualUser: true` row only under `pikku dev`. With the opt-in set, a stage signs
521
+ in as the virtual users the deployment provisioned when it started and refuses
522
+ everything else (`No virtual user account exists for that address`), so holding the
523
+ signing key on such a stage does not let anyone invent identities.
524
+
525
+ **The signing key is as powerful as the most privileged virtual user.** Provisioning
526
+ grants declared roles to virtual user accounts, so an `admin` virtual user holds
527
+ real admin — anyone with the private key _and_ the opt-in can take a session as one.
528
+ Keep it with the deployer; the app only ever needs the public half.
530
529
 
531
530
  Within that boundary, three properties bound the damage:
532
531
 
533
- - **It only ever signs in actors.** The plugin adds a `user.actor` boolean
532
+ - **It only ever signs in virtual users.** The plugin adds a `user.virtualUser` boolean
534
533
  column; an email matching a row without it is refused with `User is not an
535
- actor`. So the secret cannot take over a **real user's** account — the blast
536
- radius is the actor accounts and whatever roles they were granted.
537
- - **Unknown emails are created only under `pikku dev`**, flagged `actor: true`,
538
- so a local scenario declaring a new persona needs no seed step. Anywhere else
534
+ virtual user`. So a statement cannot take over a **real user's** account.
535
+ - **Unknown emails are created only under `pikku dev`**, flagged `virtualUser: true`,
536
+ so a local scenario declaring a new virtual user needs no seed step. Anywhere else
539
537
  the account has to have been provisioned at boot first.
540
- - **A credential is bound to one address**, so a leaked one is one synthetic
541
- account rather than the whole actor population; only the root is worth the
542
- paragraph above. The comparison is constant-time and length-hiding, so a wrong
543
- credential leaks neither the length nor a prefix of the right one.
538
+ - **A statement is bound to one virtual user, one app and a few minutes**, and can
539
+ be used once, so a leaked one is a single short-lived session.
544
540
 
545
- This is the endpoint `pikku scenario` signs its actors in through. The frontend
546
- switcher does not use it: it lists from `/sign-in/personas` and posts a persona
547
- id to `/sign-in/persona`, both served by
548
- `pikkuActor({ personaSignIn: { personas, featureFlags } })` with no credential — see
549
- `pikku-scenario` for the setup and `pikku-react` for `useDevActors()`.
541
+ This is the endpoint `pikku scenario` signs its virtual users in through. The frontend
542
+ does not use it: it posts a virtual user
543
+ id to `/sign-in/switch-virtual-user`, served by
544
+ `pikkuVirtualUser({ virtualUserSignIn: { virtualUsers } })` with no credential — see
545
+ `pikku-scenario` for the setup.
550
546
 
551
- ### Provisioning personas
547
+ ### Provisioning virtual users
552
548
 
553
549
  Anywhere but `pikku dev`, the accounts have to exist before anyone signs in.
554
- `provisionPersonas` creates them from the generated personas:
550
+ `provisionVirtualUsers` creates them from the generated virtual users:
555
551
 
556
552
  ```ts
557
- import { provisionPersonas } from '@pikku/better-auth'
553
+ import { provisionVirtualUsers } from '@pikku/better-auth'
558
554
  import {
559
- personaConfigs,
560
- personaEnvironments,
561
- } from '#pikku/pikku-personas.gen.js'
555
+ virtualUserConfigs,
556
+ virtualUserEnvironments,
557
+ } from '#pikku/pikku-virtual-users.gen.js'
562
558
 
563
- await provisionPersonas(
559
+ await provisionVirtualUsers(
564
560
  { auth, scopeService, logger },
565
- { personas: personaConfigs, environments: personaEnvironments }
561
+ { virtualUsers: virtualUserConfigs, environments: virtualUserEnvironments }
566
562
  )
567
563
  ```
568
564
 
569
565
  `auth` only needs to resolve `{ $context }`, so the singleton `services.auth`
570
566
  works as is. Call it from whatever route resolves the address a caller wants to
571
567
  act as: a miss provisions the declaration and looks again. On a stage that
572
- already holds the persona that is one query, and the pass only runs when there
568
+ already holds the virtual user that is one query, and the pass only runs when there
573
569
  is genuinely something absent to create.
574
570
 
575
571
  **Do not reach for `pikkuServerLifecycle`'s `afterStart` for this.** That hook is
576
572
  invoked by `pikku serve` and `pikku dev` and by nothing else — no deploy runtime
577
573
  calls it — so a stage on Workers or a serverless target that provisioned from
578
- `afterStart` provisioned nothing, and every persona signed in holding no roles.
574
+ `afterStart` provisioned nothing, and every virtual user signed in holding no roles.
579
575
 
580
576
  Provisioning runs where the database already is, which is the point: the CLI has
581
577
  no connection to a deployed environment's database — it resolves one from the
582
- local project config — so a `pikku persona sync staging` that wrote rows would
583
- write them to whatever database the checkout happened to point at. `pikku persona
578
+ local project config — so a `pikku virtual-user sync staging` that wrote rows would
579
+ write them to whatever database the checkout happened to point at. `pikku virtual-user
584
580
  sync <environment>` still exists, and reports who that environment will provision
585
581
  and why anyone was skipped, which is what you run _before_ the deploy.
586
582
 
587
- It creates missing accounts as `actor: true`, applies the roles each persona
583
+ It creates missing accounts as `virtualUser: true`, applies the roles each virtual user
588
584
  declares, and is additive — it never revokes. `PIKKU_ENV` (or an explicit
589
585
  `environment`) selects who is eligible, through the same rule that decides who
590
- may run there; an address already held by a real, non-actor user throws rather
591
- than being granted the persona's roles.
586
+ may run there; an address already held by a real, non-virtual-user user throws rather
587
+ than being granted the virtual user's roles.
592
588
 
593
- **Deleting a persona does not delete its account.** Being additive leaves a hole:
594
- the account keeps every role it was granted, and the actor endpoint authenticates
595
- on the `actor` column alone without consulting the declaration — so an `admin`
596
- persona nobody declares any more is still a live way in wherever that endpoint is
589
+ **Deleting a virtual user does not delete its account.** Being additive leaves a hole:
590
+ the account keeps every role it was granted, and the virtual user endpoint authenticates
591
+ on the `virtualUser` column alone without consulting the declaration — so an `admin`
592
+ virtual user nobody declares any more is still a live way in wherever that endpoint is
597
593
  open. By default provisioning warns about those accounts and changes nothing.
598
594
  `orphans: 'ban'` shuts them:
599
595
 
600
596
  ```ts
601
- await provisionPersonas(
597
+ await provisionVirtualUsers(
602
598
  { auth, scopeService, logger },
603
599
  {
604
- personas: personaConfigs,
605
- environments: personaEnvironments,
600
+ virtualUsers: virtualUserConfigs,
601
+ environments: virtualUserEnvironments,
606
602
  orphans: 'ban',
607
603
  }
608
604
  )
@@ -611,14 +607,14 @@ await provisionPersonas(
611
607
  It writes the same `banned` column the console's ban RPC writes (so it needs the
612
608
  `pikkuBan()` plugin wired, and says so if it isn't), revokes the account's sessions,
613
609
  and leaves the row, its grants and its history intact — provisioning lifts the
614
- ban again by itself if the persona comes back. Deleting is deliberately not
615
- offered: an actor row is referenced by whatever those scenarios did while it
610
+ ban again by itself if the virtual user comes back. Deleting is deliberately not
611
+ offered: a virtual user row is referenced by whatever those scenarios did while it
616
612
  existed.
617
613
 
618
614
  `report` is the default because a rolling deploy runs the new replica's
619
615
  provisioning while the old replica is still serving, so for the length of that
620
- overlap "no persona claims this" is a statement about the newer declaration only.
621
- A persona pinned to another environment counts as unclaimed here — it has no
616
+ overlap "no virtual user claims this" is a statement about the newer declaration only.
617
+ A virtual user pinned to another environment counts as unclaimed here — it has no
622
618
  business holding a signable account in an environment its own rule refuses it.
623
619
 
624
620
  ---
@@ -3,7 +3,7 @@ name: pikku-build
3
3
  description: >-
4
4
  Use to build on Pikku — turning a fresh scaffold into a working app (quick spike, real product,
5
5
  or a showcase that exercises every surface), adding a feature to an app that already exists, and
6
- the one-off cleanup right after a template is cloned. Covers the knowledge base, personas and
6
+ the one-off cleanup right after a template is cloned. Covers the knowledge base, virtual users and
7
7
  roles, filing the work as changes, the scenario that proves each one, theming, multi-app
8
8
  layouts and deploying. TRIGGER when: the user asks for an app to be built on
9
9
  Pikku, a freshly scaffolded project needs turning into a product, the user asks to add a feature
@@ -12,7 +12,7 @@ description: >-
12
12
  (use pikku-concepts), wants one specific surface explained rather than built (use that surface's
13
13
  skill), only needs the technical plan for one claimed changeset (use pikku-architect), or is
14
14
  working items off a deployed stage's changes queue (use pikku-changes).
15
- allowed-tools: Bash(yarn pikku meta *), Bash(yarn pikku all *), Bash(yarn tsc), Bash(git status *), Bash(git diff *), Bash(git switch *), Bash(git checkout *), Bash(git checkout -b *), Bash(git add *), Bash(git commit *), Bash(git rm *), Bash(git mv *), Bash(git log *), Bash(git branch *), Bash(yarn pikku fabric report *), Bash(npx --no pikku fabric report *)
15
+ allowed-tools: Bash(yarn pikku meta *), Bash(yarn pikku all *), Bash(yarn tsc), Bash(git status *), Bash(git diff *), Bash(git switch *), Bash(git checkout *), Bash(git checkout -b *), Bash(git add *), Bash(git commit *), Bash(git rm *), Bash(git mv *), Bash(git log *), Bash(git branch *), Bash(yarn pikku cloud report *), Bash(npx --no pikku cloud report *)
16
16
  argument-hint: '[feature description]'
17
17
  installGroups: [core]
18
18
  agent:
@@ -45,7 +45,7 @@ agent:
45
45
  | A screen needs data no function returns yet | `references/mocks.md` |
46
46
  | Adding a second frontend (app §4), at the changeset that needs it | `references/multi-app.md` |
47
47
  | Shipping an app as a desktop or Android app | `references/native-app.md` |
48
- | Deploying, and the Fabric-readiness contract (app §9) | `references/ship.md` |
48
+ | Deploying, and the deploy-readiness contract (app §9) | `references/ship.md` |
49
49
  | The request is built on an OpenAPI spec (auth mode, sign-in or connect screen) | `references/openapi.md` |
50
50
 
51
51
  **App is the default.** A small or toy-sounding app does not make it Quick;
@@ -155,11 +155,11 @@ generated functions through `ref()`.
155
155
 
156
156
  Whenever pikku or a skill costs you time — a command that failed on a fresh
157
157
  tree, a skill that described a flag the CLI does not have, generated code you
158
- had to fix by hand — file it then with `pikku fabric report --stdin`. The
159
- `pikku-report` skill has the fields. Leave out secrets, tokens and customer data.
158
+ had to fix by hand — file it then, the way the
159
+ `pikku-report` skill describes. That skill has the fields. Leave out secrets, tokens and customer data.
160
160
 
161
161
  Findings are held until hand-over. The last thing in the hand-over, after the
162
- app runs (and is deployed, if they chose to), is `pikku fabric report`: if it
162
+ app runs (and is deployed, if they chose to), is the `pikku-report` send step: if it
163
163
  lists findings and asks, ask the user — Yes, No, Always or Never — and run it
164
164
  again with `--consent <answer>`. If it says reporting is off, do not ask.
165
165
 
@@ -171,8 +171,8 @@ build makes or changes something the console can show, give the
171
171
  `http://localhost:<port>/console/...` link instead of describing it.
172
172
 
173
173
  Until the app is deployed that is the local open-source console, on the port
174
- `pikku dev` printed. Once it is on Fabric, link the Fabric console for the stage
175
- you are talking about instead; the `pikku-fabric` skill says which.
174
+ `pikku dev` printed. Once it is deployed, link the hosted console for the stage
175
+ you are talking about instead.
176
176
 
177
177
  | Level | Links | Code in the conversation |
178
178
  | ------------------ | --------------------------- | ------------------------ |
@@ -188,7 +188,7 @@ words and link to where they can see it.
188
188
  | Shows | Path |
189
189
  | --------------------- | -------------------------------------------------------------------------- |
190
190
  | Knowledge, plans | `/console/knowledge`, `/console/knowledge?id=<note path>` |
191
- | Personas | `/console/personas`, `/console/virtual-users?persona=<id>` |
191
+ | Virtual users | `/console/virtual-users`, `/console/virtual-users?virtual user=<id>` |
192
192
  | Roles and permissions | `/console/roles`, `/console/scopes` (only with `@pikku/addon-admin` wired) |
193
193
  | Scenarios and runs | `/console/scenarios?id=<id>`, `/console/scenarios?view=runs&run=<run id>` |
194
194
  | Workflows | `/console/workflow?id=<id>` |