@pikku/skills 0.12.52 → 0.12.54

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 (104) hide show
  1. package/dist/skills.gen.js +2 -2
  2. package/package.json +1 -1
  3. package/skills/pikku-a11y/SKILL.md +22 -15
  4. package/skills/pikku-addon/SKILL.md +5 -5
  5. package/skills/pikku-addon/references/addon-package-manifest.md +7 -0
  6. package/skills/pikku-addon/references/openapi.md +11 -0
  7. package/skills/pikku-admin-to-fabric/SKILL.md +7 -21
  8. package/skills/pikku-agent/SKILL.md +6 -6
  9. package/skills/pikku-agent/references/agents.md +8 -1
  10. package/skills/pikku-agent/references/runner-vercel.md +2 -2
  11. package/skills/pikku-agent/references/voice.md +7 -1
  12. package/skills/pikku-architect/SKILL.md +1 -1
  13. package/skills/pikku-architect/references/plan-defects.md +2 -0
  14. package/skills/pikku-auth/SKILL.md +15 -36
  15. package/skills/pikku-auth/references/better-auth.md +22 -45
  16. package/skills/pikku-auth/references/jose.md +6 -0
  17. package/skills/pikku-auth/references/machine-auth.md +6 -0
  18. package/skills/pikku-auth/references/sessions.md +6 -0
  19. package/skills/pikku-blueprint-to-fabric/SKILL.md +6 -21
  20. package/skills/pikku-blueprint-to-fabric/scripts/inventory.mjs +1 -1
  21. package/skills/pikku-build/SKILL.md +29 -26
  22. package/skills/pikku-build/references/app.md +60 -41
  23. package/skills/pikku-build/references/design.md +31 -19
  24. package/skills/pikku-build/references/feature.md +14 -0
  25. package/skills/pikku-build/references/mocks.md +23 -0
  26. package/skills/pikku-build/references/multi-app.md +9 -2
  27. package/skills/pikku-build/references/native-app.md +9 -0
  28. package/skills/pikku-build/references/openapi.md +7 -0
  29. package/skills/pikku-build/references/platform.md +20 -10
  30. package/skills/pikku-build/references/quick.md +14 -4
  31. package/skills/pikku-build/references/scenarios.md +12 -1
  32. package/skills/pikku-build/references/ship.md +6 -1
  33. package/skills/pikku-build/references/theming.md +74 -125
  34. package/skills/pikku-changes/SKILL.md +6 -6
  35. package/skills/pikku-concepts/SKILL.md +8 -17
  36. package/skills/pikku-concepts/references/concept-mapping.md +18 -0
  37. package/skills/pikku-convert-screen/SKILL.md +45 -0
  38. package/skills/pikku-deploy/references/cloudflare.md +8 -0
  39. package/skills/pikku-deploy/references/lambda.md +8 -0
  40. package/skills/pikku-deploy/references/uws.md +1 -1
  41. package/skills/pikku-deploy/references/ws.md +3 -3
  42. package/skills/pikku-emails/SKILL.md +3 -9
  43. package/skills/pikku-fabric/SKILL.md +7 -295
  44. package/skills/pikku-fabric/references/database.md +128 -0
  45. package/skills/pikku-fabric/references/debugging.md +9 -5
  46. package/skills/pikku-fabric/references/deploy.md +179 -0
  47. package/skills/pikku-guide/SKILL.md +10 -10
  48. package/skills/pikku-i18n/SKILL.md +7 -7
  49. package/skills/pikku-i18n/references/enum-labels.md +9 -0
  50. package/skills/pikku-i18n/references/messages.md +16 -3
  51. package/skills/pikku-i18n/references/rtl.md +18 -38
  52. package/skills/pikku-knowledge/SKILL.md +9 -10
  53. package/skills/pikku-kysely/SKILL.md +3 -21
  54. package/skills/pikku-list-query/SKILL.md +2 -2
  55. package/skills/pikku-meta/references/audit.md +9 -0
  56. package/skills/pikku-meta/references/meta.md +6 -0
  57. package/skills/pikku-meta/references/versioning.md +10 -0
  58. package/skills/pikku-middleware/SKILL.md +7 -16
  59. package/skills/pikku-n8n-import/SKILL.md +9 -1
  60. package/skills/pikku-n8n-import/references/addon-mapping.md +6 -0
  61. package/skills/pikku-n8n-import/references/code-translation.md +7 -0
  62. package/skills/pikku-permissions/SKILL.md +41 -10
  63. package/skills/pikku-permissions/references/scopes.md +59 -0
  64. package/skills/pikku-react/SKILL.md +2 -2
  65. package/skills/pikku-react/references/client.md +1 -34
  66. package/skills/pikku-react/references/react-query.md +11 -0
  67. package/skills/pikku-react/references/workflows.md +11 -0
  68. package/skills/pikku-realtime/SKILL.md +9 -3
  69. package/skills/pikku-report/SKILL.md +10 -9
  70. package/skills/pikku-scenario/SKILL.md +11 -17
  71. package/skills/pikku-scenario/references/persona-run.md +11 -1
  72. package/skills/pikku-scenario/references/personas.md +7 -1
  73. package/skills/pikku-seo/SKILL.md +12 -16
  74. package/skills/pikku-service-backends/SKILL.md +0 -10
  75. package/skills/pikku-service-backends/references/aws.md +6 -0
  76. package/skills/pikku-service-backends/references/mongodb.md +7 -0
  77. package/skills/pikku-service-backends/references/redis.md +6 -0
  78. package/skills/pikku-services/references/audit.md +10 -0
  79. package/skills/pikku-services/references/config.md +6 -0
  80. package/skills/pikku-services/references/services.md +8 -1
  81. package/skills/pikku-software-archaeology/SKILL.md +9 -15
  82. package/skills/pikku-software-archaeology/references/blueprint.schema.json +7 -7
  83. package/skills/pikku-software-archaeology/references/pikku-mapping.md +3 -3
  84. package/skills/pikku-software-archaeology/references/second-opinion.md +13 -1
  85. package/skills/pikku-tailwind/SKILL.md +111 -0
  86. package/skills/pikku-theme/SKILL.md +187 -0
  87. package/skills/pikku-theme/references/from-a-reference.md +44 -0
  88. package/skills/pikku-theme/references/presets.md +40 -0
  89. package/skills/pikku-ui/SKILL.md +41 -0
  90. package/skills/pikku-wiring/SKILL.md +13 -12
  91. package/skills/pikku-wiring/references/channel.md +7 -1
  92. package/skills/pikku-wiring/references/cli.md +7 -1
  93. package/skills/pikku-wiring/references/gateway-slack.md +6 -0
  94. package/skills/pikku-wiring/references/http.md +10 -3
  95. package/skills/pikku-wiring/references/mcp.md +8 -0
  96. package/skills/pikku-wiring/references/queue.md +6 -0
  97. package/skills/pikku-wiring/references/realtime.md +34 -4
  98. package/skills/pikku-wiring/references/rpc.md +6 -0
  99. package/skills/pikku-wiring/references/scheduler.md +6 -0
  100. package/skills/pikku-wiring/references/trigger.md +8 -0
  101. package/skills/pikku-workflow/SKILL.md +8 -6
  102. package/skills/pikku-auth/references/permissions.md +0 -261
  103. package/skills/pikku-mantine/SKILL.md +0 -80
  104. package/skills/pikku-wiring/references/realtime-other-routes.md +0 -23
@@ -1,14 +1,24 @@
1
1
  ---
2
2
  name: pikku-permissions
3
3
  description: >-
4
- Use when deciding WHO may call a function — resource ownership, role gates, admin-only actions, or any "only their own rows" rule. Covers the `permissions` field, `pikkuPermission`, `pikkuAuth`, scopes, and where ownership belongs versus where it does not.
5
- TRIGGER when: writing or reviewing any function that touches a row a user owns, gating an action on a role, building the permissions half of a contract in build PHASE 2, or about to write an `if` in a function body that decides whether the caller is allowed.
6
- DO NOT TRIGGER when: the question is how to sign someone in or seed a persona (that is pikku-auth), or how to shape a paginated list (that is pikku-list-query).
4
+ Use when deciding WHO may call a function: resource ownership, role gates, admin-only actions, or any "only their own rows" rule. Covers the `permissions` field, `pikkuPermission`, `pikkuAuth`, scopes and `defineScope`, `addGlobalPermission`, the three gates, `permissionsInBody`, and where ownership belongs versus where it does not.
5
+ TRIGGER when: writing or reviewing any function that touches a row a user owns, gating an action on a role, building the permissions half of a contract in build PHASE 2, about to write an `if` in a function body that decides whether the caller is allowed, or hitting `MissingScopeError`.
6
+ DO NOT TRIGGER when: the question is how a caller proves who they are, such as sign-in, sessions, tokens, API keys or seeding a persona (use pikku-auth), or how to shape a paginated list (use pikku-list-query).
7
7
  installGroups: [core]
8
8
  ---
9
9
 
10
10
  # Pikku Permissions
11
11
 
12
+ ## First: is the caller a machine with a token?
13
+
14
+ Then this is NOT a permissions problem. Resolve the token in `addHTTPMiddleware('*')`
15
+ middleware that calls `setSession`, make the function a `pikkuFunc`, and gate it with
16
+ `scopes` (pikku-auth, `../pikku-auth/references/machine-auth.md`). A `permissions` check that verifies a
17
+ bearer token and returns `true` is authentication wearing an authorization hat, and it
18
+ leaves the function sessionless. The only exception is a bootstrap endpoint whose caller
19
+ has no identity yet (a shared-secret registration, a login): it is sessionless and
20
+ declares its gate here.
21
+
12
22
  ## The rule
13
23
 
14
24
  **Authorization goes in the `permissions` field. Never in the `func` body.**
@@ -107,11 +117,32 @@ gets straight through if the function itself is open.
107
117
  ## Scopes
108
118
 
109
119
  `scopes: ['admin:invoices:void']` is an AND gate checked BEFORE permissions and before
110
- input validation. Declare the tree once with `defineScope`; a function naming an
111
- undeclared scope fails codegen rather than gating on nothing. A grant satisfies a scope if
112
- it is that scope, an ancestor, or a wildcard — a session holding `admin` satisfies
113
- `admin:invoices:void`. Most apps need roles, not scopes; reach for these only when the
114
- plan asked for granular grants.
120
+ input validation. A function naming an undeclared scope fails codegen rather than gating on
121
+ nothing. A grant satisfies a scope if it is that scope, an ancestor, or a wildcard. Most
122
+ apps need roles, not scopes; reach for these only when the plan asked for granular grants.
123
+ The `defineScope` tree, grant matching and the sessionless restriction are in
124
+ [references/scopes.md](references/scopes.md).
125
+
126
+ ## Global permissions
127
+
128
+ `addGlobalPermission([isEmployee])` (from `#pikku/auth`) is an app-wide baseline every
129
+ function must additionally pass. It is an independent AND gate: it can only narrow access,
130
+ never grant what a function's own `permissions` would deny. Multiple calls accumulate and
131
+ are AND'd.
132
+
133
+ ## The three gates
134
+
135
+ All must pass, in this order:
136
+
137
+ 1. **Scopes** (`scopes`) — AND'd, checked before input validation, fails closed.
138
+ 2. **Global permissions** (`addGlobalPermission`) — AND'd together, a baseline that only narrows.
139
+ 3. **The function's own `permissions`** — OR'd groups of AND'd entries.
140
+
141
+ They are independent: a broad global (e.g. `isEmployee`) can never satisfy an admin-only
142
+ function's own requirement. Wire-, tag- and HTTP-route-level permissions
143
+ (`addHTTPPermission`, `addTagPermission`, a `permissions` field on a wiring) no longer
144
+ exist; tags are organizational only. Use tag/HTTP middleware for cross-cutting request
145
+ handling, not authorization.
115
146
 
116
147
  ## The one sanctioned exception
117
148
 
@@ -122,5 +153,5 @@ the auditor the openness is deliberate. Anything expressible as a permission mus
122
153
 
123
154
  ## After changes
124
155
 
125
- `pikku all` — regenerates and typechecks the checkers. A permission whose signature is
126
- wrong fails here, not at runtime.
156
+ `pikku all --tsc` — regenerates, then verifies the checker types. A permission whose
157
+ signature is wrong fails here, not at runtime.
@@ -0,0 +1,59 @@
1
+ # Scopes
2
+
3
+ Scopes answer "what was this session granted?" before permissions ask "may this user do
4
+ this to this resource?". They are AND-ed: every scope listed must be held. Because they
5
+ are checked first and fail closed, a scope can only ever narrow access.
6
+
7
+ ## Declaring the tree
8
+
9
+ Declare the scope tree once with `defineScope`. The body is a no-op that tree-shakes
10
+ away; the CLI reads the call by AST and generates a `ScopeId` union, so a function naming
11
+ an undeclared scope fails the build.
12
+
13
+ ```typescript
14
+ // src/scopes.ts
15
+ import { defineScope } from '#pikku/scopes'
16
+
17
+ defineScope({
18
+ admin: {
19
+ displayName: 'Administration',
20
+ description: 'Administrative access',
21
+ scopes: {
22
+ invoices: {
23
+ description: 'Invoice management',
24
+ scopes: {
25
+ create: { description: 'Create invoices' },
26
+ void: { description: 'Void invoices' },
27
+ },
28
+ },
29
+ },
30
+ },
31
+ billing: {},
32
+ })
33
+ ```
34
+
35
+ Every node is grantable, keyed by segment: the above yields `admin`, `admin:invoices`,
36
+ `admin:invoices:create`, `admin:invoices:void` and `billing`. Scopes may be declared
37
+ across more than one file; the declarations merge.
38
+
39
+ ## Using them
40
+
41
+ ```typescript
42
+ export const voidInvoice = pikkuFunc({
43
+ scopes: ['admin:invoices:void'],
44
+ permissions: { owner: isInvoiceOwner },
45
+ func: async ({ kysely }, { invoiceId }) => { ... },
46
+ })
47
+ ```
48
+
49
+ A grant satisfies a required scope if it is the scope itself, an ancestor of it, or a
50
+ wildcard at any level: a session holding `admin` satisfies `admin:invoices:void`, and so
51
+ does `admin:*`. A missing scope throws `MissingScopeError` naming the first one that
52
+ failed.
53
+
54
+ ## Not on sessionless functions
55
+
56
+ `scopes` requires a session and is unavailable on `pikkuSessionlessFunc`: scopes fail
57
+ closed, an anonymous caller holds none, and a sessionless function with scopes would
58
+ reject every caller it exists to serve. Gate those with `permissions`, which receive the
59
+ optional session and may pass anonymous.
@@ -7,8 +7,8 @@ description: >-
7
7
  the dev actor switcher. TRIGGER when: writing a React component that fetches or mutates backend
8
8
  data, wiring PikkuProvider, paginating, running or tracking a workflow from the client, or
9
9
  asking about useDevActors / DevActorSwitcher / quick login. DO NOT TRIGGER when: working on the
10
- backend (use pikku-wiring), defining the workflow itself (use pikku-workflow), or writing
11
- user-facing copy (use pikku-i18n).
10
+ backend (use pikku-wiring), defining the workflow itself (use pikku-workflow), rendering fetched values such as
11
+ dates (use pikku-tailwind), or writing user-facing copy (use pikku-i18n).
12
12
  installGroups: [client]
13
13
  ---
14
14
 
@@ -259,9 +259,7 @@ const { actors, signInAs, pendingId, isPending, error } = useDevActors({
259
259
  ```
260
260
 
261
261
  - **It is UI-free**, so render it however you like. For the default rendering use
262
- `<DevActorSwitcher />` from `@pikku/mantine/dev` — a separate entry point from
263
- `@pikku/mantine/core`, whose contract is "drop-in alias for `@mantine/core`"
264
- and so must not export components Mantine has no counterpart for.
262
+ the starter template's `<DevActorSwitcher />` (`components/dev/DevActorSwitcher.tsx`).
265
263
  - **No credential reaches the bundle.** It lists from `/auth/sign-in/personas`
266
264
  and `signInAs(id)` posts only the persona id to `/auth/sign-in/persona`. The
267
265
  server decides who is offered and who may sign in, and offers nobody in
@@ -276,37 +274,6 @@ const { actors, signInAs, pendingId, isPending, error } = useDevActors({
276
274
  Do not hand-write the list-and-sign-in pair per app; that copy-paste is exactly
277
275
  what this replaced.
278
276
 
279
- ### Linking from a Mantine element: `renderRoot`, not `component`
280
-
281
- Handing TanStack's `Link` to a Mantine element as `component={Link}` compiles,
282
- renders, and navigates — and silently unties the type. Mantine's polymorphic
283
- `component` prop widens the router generic to `AnyRouter`, so `to` and
284
- `params` stop being checked against your actual routes. Renaming a route then
285
- breaks the running app instead of the build, which is the one thing the typed
286
- router exists to prevent.
287
-
288
- Wrap the typed `Link` once and reach it through `renderRoot`, which passes the
289
- props through without re-typing the element:
290
-
291
- ```tsx
292
- // components/links.tsx — one wrapper the whole app links through
293
- import { Link } from '@tanstack/react-router'
294
-
295
- export const AssessmentLink = (props: { assessmentId: string; children: React.ReactNode }) => (
296
- <Link to="/assessments/$assessmentId" params={{ assessmentId: props.assessmentId }}>
297
- {props.children}
298
- </Link>
299
- )
300
- ```
301
-
302
- ```tsx
303
- <Button renderRoot={(p) => <AssessmentLink assessmentId={id} {...p} />}>
304
- Open
305
- </Button>
306
- ```
307
-
308
- The wrapper is where `to` and `params` are checked, and it is checked once.
309
-
310
277
  ## What NOT to do
311
278
 
312
279
  - Don't instantiate `PikkuFetch`/`PikkuRPC` inside a component — `createPikku`
@@ -1,5 +1,16 @@
1
1
  # Pikku React Query Hooks
2
2
 
3
+ ## Contents
4
+
5
+ - [Discover what's available on the client](#discover-whats-available-on-the-client)
6
+ - [Setup (once per app)](#setup-once-per-app)
7
+ - [TanStack Start (SSR)](#tanstack-start-ssr)
8
+ - [The hooks](#the-hooks)
9
+ - [Workflow hooks](#workflow-hooks)
10
+ - [Calling RPCs without React Query](#calling-rpcs-without-react-query)
11
+ - [Common patterns](#common-patterns)
12
+ - [What NOT to do](#what-not-to-do)
13
+
3
14
 
4
15
  ## Discover what's available on the client
5
16
 
@@ -1,5 +1,16 @@
1
1
  # Pikku Workflows — Client Hooks
2
2
 
3
+ ## Contents
4
+
5
+ - [Discover what workflows exist](#discover-what-workflows-exist)
6
+ - [Setup](#setup)
7
+ - [useRunWorkflow(name, options?) — run and wait](#userunworkflowname-options--run-and-wait)
8
+ - [useStartWorkflow(name, options?) — fire-and-poll](#usestartworkflowname-options--fire-and-poll)
9
+ - [useWorkflowStatus(workflowName, runId, options?) — observe](#useworkflowstatusworkflowname-runid-options--observe)
10
+ - [Putting it together — start + observe](#putting-it-together--start--observe)
11
+ - [Backend: streaming richer progress](#backend-streaming-richer-progress)
12
+ - [What NOT to do](#what-not-to-do)
13
+
3
14
 
4
15
  ## Discover what workflows exist
5
16
 
@@ -1,9 +1,15 @@
1
1
  ---
2
2
  name: pikku-realtime
3
3
  description: >-
4
- Use when making ANY view live/realtime in a Pikku app — a board, shared list, dashboard, ticker, bidding room, live count — or when adding two-way chat/presence. Covers the DEFAULT event-hub SSE path and the two-way WebSocket channel.
5
- TRIGGER when: the user wants live updates, realtime, "update without refresh", a live board/feed/ticker/room, presence, or chat; or when data that MORE THAN ONE signed-in user can change should reflect others
6
- DO NOT TRIGGER when: a plain one-shot query/refetch is fine (data only one user changes, or a manual refresh is acceptable), or for background jobs (see `pikku-wiring`'s scheduler and queue references, or `pikku-workflow`).
4
+ Use when making a view live in a Pikku app — a board, shared list, dashboard, ticker, bidding
5
+ room, live count — or when adding two-way chat or presence. Covers the default event-hub SSE path
6
+ (`eventHub.publish` on the backend, `PikkuRealtime.subscribeToTopic` on the client) and the
7
+ two-way WebSocket channel. TRIGGER when: the user wants live updates, realtime, "update without
8
+ refresh", a live board/feed/ticker/room, presence or chat, or data that more than one signed-in
9
+ user can change should reflect to others. DO NOT TRIGGER when: a one-shot query/refetch is enough
10
+ (data only one user changes, or a manual refresh is acceptable), for background jobs (use
11
+ pikku-workflow, or pikku-wiring's queue and scheduler references), or for the full wiring
12
+ reference of channels, SSE routes and the `/events` scaffold (use pikku-wiring).
7
13
  installGroups: [core, client]
8
14
  ---
9
15
 
@@ -1,14 +1,15 @@
1
1
  ---
2
2
  name: pikku-report
3
3
  description: >-
4
- Use when pikku itself cost you time — wrong generated types, a check that passes when it
5
- should not, output that is quietly wrong, a skill that misled you — or when the user asks you
6
- to report a framework bug or file a finding. Owns `pikku fabric report` (a finding is about
7
- pikku, not the app), the product-vs-harness kinds, the workaround-first ladder, and asking the
8
- user at hand-over whether to send what was filed. TRIGGER when: the framework fought you,
9
- codegen produced something broken, a skill told you to run something that does not exist, the
10
- user says "report this to pikku" / "file a finding", or you are handing over a build. DO NOT TRIGGER when: the bug is in the app you are building (fix it
11
- there), or you are tempted to patch pikku's source (never do that from an app).
4
+ Use when pikku itself cost you time — wrong generated types, a check that passes when it should
5
+ not, output that is quietly wrong, a skill that misled you — or when the user asks you to report
6
+ a framework bug or file a finding. Owns `pikku fabric report` (a finding is about pikku, not the
7
+ app), the product-vs-harness kinds, the workaround-first ladder, and asking the user at hand-over
8
+ whether to send what was filed. TRIGGER when: the framework fought you, codegen produced
9
+ something broken, a skill told you to run something that does not exist, the user says "report
10
+ this to pikku" / "file a finding", or you are handing over a build. DO NOT TRIGGER when: the bug
11
+ is in the app you are building (fix it there), you are debugging a deployed stage (use
12
+ pikku-fabric), or you are tempted to patch pikku's source (never do that from an app).
12
13
  installGroups: [core]
13
14
  ---
14
15
 
@@ -61,7 +62,7 @@ baseline noise that was already failing before you started.
61
62
  - `--kind harness` — a skill misled you: it told you to run something that does
62
63
  not exist, described a flag that is spelled differently, or contradicted what
63
64
  the CLI actually did. Pass `--skill <name>` and `--passage "<the line or
64
- section>"`. This is the most useful kind to file, because it is fixable
65
+ section>"`. This is the most useful kind to file, because it is fixable
65
66
  immediately — so file it even when the cost was small.
66
67
 
67
68
  ## The command
@@ -8,7 +8,7 @@ description: >-
8
8
  expectEventually / expectError / expectService / expectScore, declared steps via
9
9
  pikkuScenarioStep (browser steps driven by @pikku/playwright) written as intent rather than
10
10
  clicks, personas / actors / environments in pikku.config.json, SCENARIO_ACTOR_SECRET, the
11
- `pikku scenario list|run` and `pikku persona run|list|sync|secret` commands, and live coverage
11
+ `pikku scenario` and `pikku persona` commands, and live coverage
12
12
  via `pikku dev --coverage`. TRIGGER when: user asks about scenarios, testing a Pikku function,
13
13
  coverage, e2e flows, browser/UI e2e, health checks, personas, virtual users, or adversarial
14
14
  runs against a stage. DO NOT TRIGGER when: user asks about running an existing suite (use
@@ -20,19 +20,15 @@ installGroups: [core]
20
20
 
21
21
  ## Agent Operating Procedure
22
22
 
23
- Use this skill as an execution checklist, not reference material.
24
-
25
23
  1. Discover before editing: `pikku scenario list` for what exists, `pikku info functions --verbose` for what a scenario can call.
26
- 2. Identify the source files that own the behavior. Do not start by reading generated output, `.pikku`, `node_modules`, or build artifacts.
27
- 3. Make the smallest source change that satisfies the task. Keep generated files generated.
28
- 4. Validate with the narrowest relevant command first, then `pikku all --tsc` when functions, wirings or schemas may have changed.
29
- 5. If validation fails, fix the source cause and rerun. Do not paper over generated errors by editing generated files.
24
+ 2. Edit source files only; never generated output or `.pikku`.
25
+ 3. Validate with the narrowest relevant command first, then `pikku all --tsc` when functions, wirings or schemas may have changed. If it fails, fix the source cause and rerun.
30
26
 
31
27
  **`pikku tests` does not exist.** It was removed in #865 — scenarios own coverage now. Any reference you find to it is stale.
32
28
 
33
29
  ## Pick the reference
34
30
 
35
- This skill covers writing and running scenarios end to end. Four topics are one level down, and
31
+ This skill covers writing and running scenarios end to end. Five topics are one level down, and
36
32
  each says when to open it:
37
33
 
38
34
  | Read | For |
@@ -186,12 +182,6 @@ Hooks are scenario-only. A `before`/`after` on a `pikkuWorkflowFunc` never runs
186
182
 
187
183
  ### Grouping scenarios (`pikkuFeature`)
188
184
 
189
- A feature is also cited by a page of the user guide: `pikku scenario guide`
190
- fills each cited block with the feature's recordings and screenshots, and
191
- nothing else (see **pikku-guide**). A scenario's `title` captions its recording
192
- and a screenshot's `name` captions the still, so write both as copy a user would
193
- read. Mark plumbing features `document: false`.
194
-
195
185
  `pikkuFeature` groups scenarios the way gherkin's `Feature:` groups `Scenario:`. Scenarios are referenced by **imported identifier**, so a renamed or deleted scenario is a compile error rather than a silent skip:
196
186
 
197
187
  ```typescript
@@ -229,6 +219,12 @@ export const credentialFeature = pikkuFeature({
229
219
 
230
220
  The **feature is the run unit**: `--flows` on a scenario whose every feature entry carries `data` errors and names the features containing it, because the feature is what supplies that data. Use `--features` for those. A scenario referenced bare anywhere, or in no feature at all, still runs standalone.
231
221
 
222
+ A feature is also cited by a page of the user guide: `pikku scenario guide`
223
+ fills each cited block with the feature's recordings and screenshots, and
224
+ nothing else (see **pikku-guide**). A scenario's `title` captions its recording
225
+ and a screenshot's `name` captions the still, so write both as copy a user would
226
+ read. Mark plumbing features `document: false`.
227
+
232
228
  ## Steps
233
229
 
234
230
  `scenario.do` can only name an RPC. A **step** — `pikkuScenarioStep` — is a named, typed unit of
@@ -289,7 +285,7 @@ scenario actor per person, and seeds a user row each:
289
285
  - `scenarios.emailDomain` is the mail domain actor addresses are built on.
290
286
  - `scenarios.browserDriver` is the package driving `browser` bindings.
291
287
  - `scenarios.model` is the model a persona thinks with (`actor.converse`, `pikku virtual-user run`).
292
- - `environments.<name>` are the targets a run can point at. The key is the required positional of `pikku scenario run`; `apiUrl` is required and `signInPath`/`rpcPath`/`sessionPath` have defaults.
288
+ - `environments.<name>` are the targets a run can point at. The key is the required positional of `pikku scenario run`; `apiUrl` is required; `signInPath` defaults to `/auth/sign-in/actor`, `rpcPath` to `/rpc`, and `sessionPath` has a default too.
293
289
 
294
290
  **`references/personas.md`** covers the parts that bite: one entry is one
295
291
  person and one actor, emails are derived and never written, `runnable: false`
@@ -297,7 +293,6 @@ for someone only acted upon, `definePersonas` being read from source rather
297
293
  than evaluated, and the same actor list powering a human "Sign in as …"
298
294
  switcher.
299
295
 
300
- - `environments.<name>.apiUrl` is required. `signInPath` defaults to `/auth/sign-in/actor`, `rpcPath` to `/rpc`.
301
296
  - **`SCENARIO_ACTOR_SECRET` is an environment variable and never goes in `pikku.config.json`.** It signs actors in. `pikku scenario run` throws without it; a server auto-building actors warns and runs without them.
302
297
 
303
298
  ## Running
@@ -351,7 +346,6 @@ and where a plain unit test is still the right tool.
351
346
 
352
347
  | Smell | Why it's wrong |
353
348
  | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
354
- | `pikku tests …` | Removed in #865. Use `pikku scenario`. |
355
349
  | `.feature` files / Gherkin for function tests | Scenarios are TypeScript, not Gherkin. The in-process cucumber function world was deleted. |
356
350
  | `scenario.do(...)` with no `{ actor }` | Throws. Every step runs as somebody. |
357
351
  | A scenario per function | Scenarios are user flows. One flow covers many functions; that is the point. |
@@ -1,5 +1,15 @@
1
1
  # Running a persona as a virtual user
2
2
 
3
+ ## Contents
4
+
5
+ - [The shape of a run](#the-shape-of-a-run)
6
+ - [The dispositions](#the-dispositions)
7
+ - [Credentials, and which one wins](#credentials-and-which-one-wins)
8
+ - [Production is opt-in, twice](#production-is-opt-in-twice)
9
+ - [The role check happens before the first step](#the-role-check-happens-before-the-first-step)
10
+ - [The other subcommands](#the-other-subcommands)
11
+ - [What NOT to do](#what-not-to-do)
12
+
3
13
  `pikku persona run <environment> <persona>` signs a declared persona in over the
4
14
  app's real auth and works the API in character, driven by a model. A persona
5
15
  while running **is** the virtual user — there is no second declaration for it.
@@ -125,7 +135,7 @@ drift.
125
135
 
126
136
  `sync` **reports**; it does not provision. The CLI has no connection to a
127
137
  deployed environment's database, so the provisioning happens in the deployment —
128
- pass the generated personas to `pikkuFabric` from `@pikku/better-auth`.
138
+ pass the generated personas to `provisionPersonas` from `@pikku/better-auth`.
129
139
 
130
140
  ## What NOT to do
131
141
 
@@ -1,5 +1,11 @@
1
1
  # Personas and actors
2
2
 
3
+ ## Contents
4
+
5
+ - [Declaring personas in TypeScript](#declaring-personas-in-typescript)
6
+ - [Actors that call a third-party API](#actors-that-call-a-third-party-api)
7
+ - [The same actors sign a human in](#the-same-actors-sign-a-human-in)
8
+
3
9
  A **persona** is a person your product is for; an **actor** is one body that signs in as them. Every entry materialises exactly one actor, so `actors.<id>` exists for each declared persona and there is no second way to declare a login.
4
10
 
5
11
  - Two people of the same kind are two entries, not one persona with two logins — "you see yours, not theirs" is only testable with two customers.
@@ -96,7 +102,7 @@ sign-in opted in **and** its `devSwitcher` feature flag on; production never
96
102
  has the opt-in, so it lists nobody and refuses every persona sign-in.
97
103
 
98
104
  Do not hand-roll the switcher: `useDevActors()` (`pikku-react`, a separate install) is the logic and
99
- `<DevActorSwitcher />` from `@pikku/mantine/dev` is a ready rendering of it.
105
+ the starter template's `<DevActorSwitcher />` is a ready rendering of it.
100
106
  `pikku fabric validate` **requires** any frontend with a login screen to ship
101
107
  one — without it a reviewer is locked out of their own sandbox.
102
108
  When the switcher is missing, it is one of three things, and none of them
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  name: pikku-seo
3
3
  description: >-
4
- On-page SEO rules for the app's PUBLIC pages: per-route head() titles and meta descriptions, Open Graph tags, one-h1 heading hierarchy, semantic/crawlable markup, JSON-LD on the landing page, and noindex for the logged-in area.
4
+ On-page SEO rules for the app's PUBLIC pages: per-route head() titles and meta descriptions, Open Graph tags, crawlable links and URLs, JSON-LD on the landing page, and noindex for the logged-in area.
5
5
  TRIGGER when: building or reworking any public page (landing, pricing, about, blog/content pages), writing page titles or meta tags, or the user asks about SEO / Google / discoverability / social sharing previews.
6
- DO NOT TRIGGER when: working on logged-in /app screens (they are noindexed — only the one robots rule below applies), backend functions, database, or deployment.
6
+ DO NOT TRIGGER when: the task is accessibility, including heading order, landmarks or alt text (use pikku-a11y), or working on logged-in /app screens (they are noindexed — only the one robots rule below applies), backend functions, database, or deployment.
7
7
  installGroups: [client]
8
8
  ---
9
9
 
@@ -38,19 +38,20 @@ export const Route = createFileRoute('/pricing')({
38
38
  })
39
39
  ```
40
40
 
41
- `head()` strings are plain strings (they do not go through the Mantine i18n
41
+ `head()` strings are plain strings (they do not go through the component i18n
42
42
  gate) — write real copy for THIS app, in the app's voice.
43
43
 
44
44
  - **Title**: unique per page, 50–60 characters, the page's primary topic first,
45
- brand at the end (`Topic — AppName`). The template's `__APP_TITLE__` default
46
- must never survive the rebrand, on any page.
45
+ brand at the end (`Topic — AppName`). The template's `__APP_TITLE__` placeholder
46
+ (in `src/app-meta.ts` and `messages/en.json`) is filled with the app title at scaffold time
47
+ by `fabric-new-app`; it must never survive in any page's title.
47
48
  - **Description**: unique per page, 150–160 characters, states the concrete
48
49
  value of the page in plain language — a reason to click, not a keyword list.
49
50
  - **Dynamic public pages** (e.g. a public detail page) build both from loader
50
51
  data: `head: ({ loaderData }) => ({ meta: [{ title: `${loaderData.name} — AppName` }, ...] })`.
51
52
  - **Never invent URLs**: the deployed domain is unknown at build time, so do
52
53
  NOT emit `canonical`, `og:url`, or `og:image` pointing at a made-up domain —
53
- omit them (same principle as the `/api` serverUrl rule). `og:image` only if a
54
+ omit them. `og:image` only if a
54
55
  real asset exists in the app.
55
56
 
56
57
  ## Logged-in area = noindex
@@ -64,24 +65,19 @@ head: () => ({ meta: [{ name: 'robots', content: 'noindex' }] })
64
65
  Never noindex a public page, and never put per-page SEO effort into `/app`
65
66
  screens — they are invisible to crawlers by design.
66
67
 
67
- ## Headings — exactly one h1 per page
68
+ ## Headings, landmarks, alt text
68
69
 
69
- - Every page has EXACTLY ONE h1 (`<Title order={1}>` in Mantine, `<h1>` in
70
- Tailwind) and it names the page's primary topic — aligned with the title tag,
71
- not identical boilerplate.
72
- - Logical hierarchy below it: h1 → h2 → h3, no skipped levels, headings
73
- describe the content under them. Never pick a heading level for its font
74
- size — set the size on the correct level (`<Title order={2} fz="xs">`).
70
+ Owned by `pikku-a11y` and apply app-wide; follow it. SEO addition only: the
71
+ public page's h1 names its primary topic, aligned with the title tag (not
72
+ identical boilerplate).
75
73
 
76
74
  ## Crawlable, semantic markup
77
75
 
78
- - Landmarks on public pages: `<nav>`, `<main>`, `<footer>` (Mantine: `component="nav"` etc.).
79
76
  - Navigation between public pages uses real links (`<Link>`/`<a href>`) with
80
77
  descriptive anchor text — crawlers follow hrefs; a `div onClick` navigation
81
78
  is invisible to them. No public page may be orphaned: every public page is
82
79
  reachable by link from the landing page (directly or via nav/footer).
83
- - Every meaningful `<img>` has alt text describing the image; decorative images
84
- get `alt=""`. Prefer descriptive file names for real assets.
80
+ - Prefer descriptive file names for real assets (alt text: see `pikku-a11y`).
85
81
  - Readable URLs: public routes are lowercase, hyphen-separated, and named for
86
82
  their content (`/pricing`, `/how-it-works`) — never `/page2` or query-param
87
83
  navigation.
@@ -16,16 +16,6 @@ installGroups: [core]
16
16
 
17
17
  # Pikku Service Backends
18
18
 
19
- ## Agent Operating Procedure
20
-
21
- Use this skill as an execution checklist, not reference material.
22
-
23
- 1. Discover before editing. Run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
24
- 2. Identify the source files that own the behavior. Do not start by reading generated output, `.pikku`, `node_modules`, vendored packages, or broad build artifacts.
25
- 3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
26
- 4. Validate with the narrowest relevant command first, then run `pikku all` when functions, wirings, schemas, or generated clients may have changed.
27
- 5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
28
-
29
19
  Constructor shapes and method signatures come from `pikku doc` — run
30
20
  `pikku doc --ai` for the installed surface. This skill is the part the compiler
31
21
  cannot tell you: which backend implements which interface, and what changes when
@@ -1,5 +1,11 @@
1
1
  # AWS (`@pikku/aws-services`)
2
2
 
3
+ ## Contents
4
+
5
+ - [`S3Content` — ContentService](#s3content-contentservice)
6
+ - [`SQSQueueService` — QueueService](#sqsqueueservice-queueservice)
7
+ - [`AWSSecrets` — SecretService](#awssecrets-secretservice)
8
+
3
9
  ```bash
4
10
  yarn add @pikku/aws-services
5
11
  ```
@@ -1,5 +1,12 @@
1
1
  # MongoDB (`@pikku/mongodb`)
2
2
 
3
+ ## Contents
4
+
5
+ - [`PikkuMongoDB` — connection wrapper](#pikkumongodb-connection-wrapper)
6
+ - [Available services](#available-services)
7
+ - [`MongoDBSecretService`](#mongodbsecretservice)
8
+ - [Full setup](#full-setup)
9
+
3
10
  ```bash
4
11
  yarn add @pikku/mongodb
5
12
  ```
@@ -1,5 +1,11 @@
1
1
  # Redis (`@pikku/redis`)
2
2
 
3
+ ## Contents
4
+
5
+ - [`RedisSecretService`](#redissecretservice)
6
+ - [`RedisLeaseService`](#redisleaseservice)
7
+ - [Full setup](#full-setup)
8
+
3
9
  ```bash
4
10
  yarn add @pikku/redis
5
11
  ```
@@ -1,5 +1,15 @@
1
1
  # Pikku Audit
2
2
 
3
+ ## Contents
4
+
5
+ - [Mental model — two layers](#mental-model-two-layers)
6
+ - [Wiring (services.ts)](#wiring-servicests)
7
+ - [Recording events — explicit domain events (default)](#recording-events-explicit-domain-events-default)
8
+ - [Recording events — automatic query capture (optional)](#recording-events-automatic-query-capture-optional)
9
+ - [Sinks](#sinks)
10
+ - [AuditEvent shape](#auditevent-shape)
11
+ - [Do / Don't](#do-dont)
12
+
3
13
 
4
14
  ## Mental model — two layers
5
15
 
@@ -1,5 +1,11 @@
1
1
  # Pikku Config, Secrets & OAuth2
2
2
 
3
+ ## Contents
4
+
5
+ - [Before You Start](#before-you-start)
6
+ - [Secrets & Variables](#secrets-variables)
7
+ - [Credentials](#credentials)
8
+
3
9
 
4
10
  ## Before You Start
5
11
 
@@ -1,5 +1,12 @@
1
1
  # Pikku Services (Dependency Injection)
2
2
 
3
+ ## Contents
4
+
5
+ - [Before You Start](#before-you-start)
6
+ - [API Reference](#api-reference)
7
+ - [Usage Patterns](#usage-patterns)
8
+ - [Complete Example](#complete-example)
9
+
3
10
  ## Before You Start
4
11
 
5
12
  ```bash
@@ -75,7 +82,7 @@ export const lifecycle = pikkuServerLifecycle<SingletonServices>({
75
82
  })
76
83
  ```
77
84
 
78
- Every hook is optional. Order is `beforeStart` → server starts → `afterStart`, then on SIGINT `beforeStop` → services stopped → server stopped → `afterStop`.
85
+ Every hook is optional. Order is `beforeStart` → server starts → `afterStart`, then on SIGINT or SIGTERM `beforeStop` → services stopped → server stopped → `afterStop`. A repeat signal while this runs is ignored, so the sequence always finishes.
79
86
 
80
87
  **`afterStop` runs after the singleton services have been stopped.** It still receives the services object, but the services inside it are shut down — using one there is a use-after-close bug. Anything that needs a live service goes in `beforeStop`.
81
88