@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
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.52",
3
+ "version": "0.12.55",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/pikkujs/pikku.git",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: pikku-a11y
3
3
  description: >-
4
- Accessibility rules (WCAG 2.2) for the app UI: labeled inputs, real buttons/links, keyboard and focus, contrast and not-color-alone, modals, reduced motion.
4
+ Accessibility rules (WCAG 2.2) for shadcn/Tailwind app UI: labeled inputs, real buttons/links, keyboard and focus, contrast and not-color-alone, modals, reduced motion.
5
5
  TRIGGER when: building forms or any interactive UI, icon-only buttons, modals/drawers, tables/lists with actions, keyboard/focus work, or the user mentions accessibility / screen readers / WCAG.
6
6
  DO NOT TRIGGER when: working on backend functions, database, or deployment with no UI.
7
7
  installGroups: [client]
@@ -9,24 +9,23 @@ installGroups: [client]
9
9
 
10
10
  # Accessibility Rules
11
11
 
12
- Mantine components are accessible ONLY when used properly — the rules below are the
13
- "properly". They apply to every page; heading order, landmarks, and image alt text are
14
- covered in the `pikku-seo` skill and apply app-wide, not just on public pages.
12
+ shadcn components are accessible ONLY when used properly — the rules below are the
13
+ "properly". They apply to every page, public or logged-in. This skill owns heading hierarchy,
14
+ landmarks and image alt text app-wide (below); `pikku-seo` covers only public-route SEO.
15
15
 
16
16
  ## Every input has a label
17
17
 
18
- - Use the `label` prop on every Mantine input — a placeholder is NOT a label (it
18
+ - Give every input a `<Label htmlFor>` — a placeholder is NOT a label (it
19
19
  disappears on input and is never announced as one). Placeholder = example value only.
20
- - Use the `error` and `description` props for validation/help text — Mantine associates
21
- them with the input for screen readers; a loose `<Text c="red">` next to the field
22
- does not.
23
- - Icon-only controls (`ActionIcon`, icon `Button`) MUST have `aria-label={m.key()}`
20
+ - Wire validation and help text to the input with `aria-describedby` (and `aria-invalid`) — a loose
21
+ red paragraph next to the field is not announced with it.
22
+ - Icon-only controls (icon `Button`) MUST have `aria-label={m.key()}`
24
23
  naming the action ("Delete item", not "Trash icon").
25
24
 
26
25
  ## Interactive = a real button or link
27
26
 
28
- - Never `onClick` on a `div`/`Box`/`Card` — it is invisible to keyboard and screen
29
- readers. Use `Button`, `ActionIcon`, `UnstyledButton`, or `<Link>`; navigation is a
27
+ - Never `onClick` on a `div`/`Card` — it is invisible to keyboard and screen
28
+ readers. Use `Button` or `<Link>`; navigation is a
30
29
  link (href), actions are buttons.
31
30
  - Everything reachable by Tab, activatable by Enter/Space. Never remove focus outlines
32
31
  (the theme owns the focus ring), never set `tabIndex` greater than 0, never trap focus
@@ -38,14 +37,14 @@ covered in the `pikku-seo` skill and apply app-wide, not just on public pages.
38
37
 
39
38
  - Status must carry text or an icon, not only a color: a Badge says "Overdue", a form
40
39
  error has a message — a red tint by itself is invisible to colorblind users.
41
- - Contrast comes from the theme; don't undermine it by stacking `c="dimmed"` on small
40
+ - Contrast comes from the theme; don't undermine it by stacking `text-muted-foreground` on small
42
41
  text over tinted backgrounds. Body copy stays at least AA-readable.
43
- - Touch targets: WCAG 2.2 minimum 24px — don't shrink `ActionIcon`/`Checkbox` below
44
- size `sm`, and keep adjacent row actions spaced.
42
+ - Touch targets: WCAG 2.2 minimum 24px — don't shrink icon buttons or checkboxes below
43
+ 24px, and keep adjacent row actions spaced.
45
44
 
46
45
  ## Overlays and motion
47
46
 
48
- - Modals/drawers: use Mantine `Modal`/`Drawer` and ALWAYS pass `title` — that is what
47
+ - Modals/drawers: use shadcn `Sheet`/`Dialog` and ALWAYS include a `Title` — that is what
49
48
  gets announced; focus trap and Escape come built in. (This project uses drawers, not
50
49
  dialogs.)
51
50
  - Landing-page animation (the only custom-CSS surface) respects
@@ -57,3 +56,11 @@ Tab through the page once: every control reachable and visibly focused, every in
57
56
  labeled, every icon button named, every status readable without color. A browser
58
57
  scenario proves the flow works, not that it is reachable without a mouse — this
59
58
  manual pass is the only check that does.
59
+
60
+ ## Headings, landmarks, alt text (app-wide)
61
+
62
+ - Exactly one h1 per page, naming its topic. Below it: h1 → h2 → h3, no skipped levels.
63
+ Never pick a level for its font size — set the size on the correct level
64
+ (`<h2 className="text-sm">`).
65
+ - Landmarks on every page: `<nav>`, `<main>`, `<footer>` where present (render the element itself, `<nav>`, rather than a `<div>` with a role).
66
+ - Every meaningful `<img>` has alt text describing it; decorative images get `alt=""`.
@@ -4,11 +4,11 @@ description: >-
4
4
  Use when creating or consuming reusable function packages (addons) in Pikku. Covers wireAddon,
5
5
  ref(), pikkuAddonServices, pikkuAddonWireServices, addon package structure, addons that ship
6
6
  database tables (pikku db export), generating an addon from an OpenAPI/Swagger spec, and
7
- cross-project function sharing. TRIGGER when: code uses wireAddon/ref()/pikkuAddonServices, user
8
- asks about addons, reusable function packages, cross-project sharing, or addon package structure,
9
- or the user hands over an OpenAPI/Swagger spec (file or URL) to build on. DO NOT
10
- TRIGGER when: user asks about internal function composition (use pikku-wiring) or general function
11
- definitions (use pikku-concepts).
7
+ cross-project function sharing. TRIGGER when: code uses wireAddon/ref()/pikkuAddonServices, the
8
+ user asks about addons, reusable function packages, cross-project sharing or addon package
9
+ structure, or hands over an OpenAPI/Swagger spec (file or URL) to build on. DO NOT TRIGGER when:
10
+ the user asks about internal function composition (use pikku-wiring), general function
11
+ definitions (use pikku-concepts), or importing an n8n export (use pikku-n8n-import).
12
12
  installGroups: [core]
13
13
  ---
14
14
 
@@ -1,5 +1,12 @@
1
1
  # Addon Package Manifest Reference
2
2
 
3
+ ## Contents
4
+
5
+ - Package Structure
6
+ - pikku.config.json
7
+ - package.json (key fields)
8
+ - tsconfig.json (key fields)
9
+
3
10
  `npx pikku new addon` scaffolds these files. You rarely hand-edit them — consult this when wiring exports or config by hand.
4
11
 
5
12
  ## Package Structure
@@ -1,5 +1,16 @@
1
1
  # An addon from an OpenAPI spec
2
2
 
3
+ ## Contents
4
+
5
+ - Recognising one
6
+ - 1 — Look at the spec first
7
+ - 2 — Generate, from the app's root
8
+ - 3 — Check what was generated
9
+ - 4 — Call it from the app
10
+ - 5 — Verify
11
+ - 6 — Check the spec against the real API
12
+ - Then
13
+
3
14
  When you are handed an OpenAPI or Swagger spec — a file, or a URL to one — the
4
15
  API it describes becomes an addon: one function per operation, each with its
5
16
  input and output schemas, behind one service that makes the HTTP calls. Say so
@@ -4,11 +4,11 @@ description: >-
4
4
  Use when building AI agents, chatbots or LLM-powered assistants with Pikku — pikkuAgent, ref()
5
5
  tool registration, memory, streaming, tool approval, thread ownership, invocation via rpc.agent,
6
6
  the VercelAgentRunner and its provider map, and the voiceInput/voiceOutput middlewares. TRIGGER
7
- when: code uses pikkuAgent/rpc.agent/runAgent/streamAgent/VercelAgentRunner/voiceInput, user asks
8
- about AI agents, chatbots, tool-calling, agent memory or streaming, model providers, speech in or
9
- out, or `pikku enable agent`. DO NOT TRIGGER when: user asks about MCP tool exposure (use
10
- pikku-wiring), workflows (use pikku-workflow), or general function definitions (use
11
- pikku-concepts).
7
+ when: code uses pikkuAgent/rpc.agent/runAgent/streamAgent/VercelAgentRunner/voiceInput, or the
8
+ user asks about AI agents, chatbots, tool-calling, agent memory or streaming, model providers,
9
+ speech in or out, or `pikku enable agent`. DO NOT TRIGGER when: the user asks about MCP tool
10
+ exposure (use pikku-wiring), workflows (use pikku-workflow), React agent hooks (use pikku-react),
11
+ or general function definitions (use pikku-concepts).
12
12
  installGroups: [core]
13
13
  ---
14
14
 
@@ -49,7 +49,7 @@ session, the credentials and the RPC depth for you.
49
49
  `output` schema, and the picture passed as an `attachments` entry.
50
50
  - **`auth` defaults to `false`**, because agents are normally invoked from an
51
51
  already-authenticated `pikkuFunc`. `scopes` and `permissions` are enforced
52
- either way — see `pikku-auth`.
52
+ either way — see `pikku-permissions`.
53
53
  - **`approvalRequired` sits on the tool function, not on the agent.** The run
54
54
  then resolves `status: 'suspended'` with `pendingApprovals`; answer with
55
55
  `rpc.agent.approve(runId, approvals)`.
@@ -1,5 +1,12 @@
1
1
  # Pikku AI Agent Wiring
2
2
 
3
+ ## Contents
4
+
5
+ - Before You Start
6
+ - API Reference
7
+ - Usage Patterns
8
+ - Complete Example
9
+
3
10
 
4
11
  ## Before You Start
5
12
 
@@ -83,7 +90,7 @@ import cycle.
83
90
 
84
91
  `auth` defaults to `false` because agents are usually invoked from an
85
92
  already-authenticated `pikkuFunc`. `scopes` and `permissions` are enforced either
86
- way — see `pikku-auth`.
93
+ way — see `pikku-permissions`.
87
94
 
88
95
  ### Invoking an agent
89
96
 
@@ -25,7 +25,7 @@ const runner = new VercelAgentRunner(
25
25
 
26
26
  - `stream(params: AgentRunnerParams, channel: AgentStreamChannel): Promise<AgentStepResult>` — Stream AI responses with tool calls
27
27
  - `run(params: AgentRunnerParams): Promise<AgentStepResult>` — Execute a single AI step (non-streaming)
28
- - `transcribe({ model, audio, … })` / `generateSpeech({ model, text, voice, … })` — what `voiceInput`/`voiceOutput` call; see `references/voice.md`
28
+ - `transcribe({ model, audio, … })` / `generateSpeech({ model, text, voice, … })` — what `voiceInput`/`voiceOutput` call; see the voice reference
29
29
  - `generateImage`, `embed`, `embedMany`, `rerank` — the remaining AI SDK surfaces
30
30
  - `withApiKey(apiKey)` — returns a **new** runner built from `providerFactory`; returns `this` unchanged when no factory was supplied or the key is blank. This is the per-user-credential path
31
31
 
@@ -85,7 +85,7 @@ export const assistant = pikkuAgent({
85
85
  ```
86
86
 
87
87
  There is no `wireAgent` — agents are declared with `pikkuAgent` from the
88
- generated agent types. See `references/agents.md` for the full config.
88
+ generated agent types. The full config is in the agents reference.
89
89
 
90
90
  ### Testing without a real provider
91
91
 
@@ -1,5 +1,11 @@
1
1
  # Pikku AI Voice (Speech I/O)
2
2
 
3
+ ## Contents
4
+
5
+ - `@pikku/ai-voice` is deprecated and empty
6
+ - API Reference
7
+ - Usage Pattern
8
+
3
9
 
4
10
  ## `@pikku/ai-voice` is deprecated and empty
5
11
 
@@ -9,7 +15,7 @@ dependency.
9
15
 
10
16
  Voice now lives in **`@pikku/core/agent`** as two AI middlewares, and the
11
17
  speech models are reached through the `agentRunner` (`transcribe` /
12
- `generateSpeech`) rather than through separate services. See `references/runner-vercel.md`.
18
+ `generateSpeech`) rather than through separate services. The runner reference covers that wiring.
13
19
 
14
20
  ## API Reference
15
21
 
@@ -3,7 +3,7 @@ name: pikku-architect
3
3
  description: >-
4
4
  Use to turn one claimed changeset into the technical plan its build is measured against —
5
5
  the tables, functions, wires, roles, scopes, screens and scenarios it owes, split into passes and
6
- written through `pikku knowledge plan set <changeset>`. The plan is the denominator
6
+ written through `pikku knowledge plan set`. The plan is the denominator
7
7
  `pikku knowledge plan progress` divides by, so it is written BEFORE any of the changeset's code
8
8
  exists and never edited afterwards to match what got built. TRIGGER when: `pikku changes
9
9
  claim` says a changeset needs a plan, `changes done` or `pikku changes next` refuses one for having no
@@ -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,
@@ -5,6 +5,8 @@ item. These are the ones it cannot, each carried back from a changeset that ship
5
5
  grouped by the question that finds them, and the fastest way to use this file is to read the six
6
6
  questions and only open the group that worries you.
7
7
 
8
+ ## Contents
9
+
8
10
  1. [Is this a plan for THIS note?](#is-this-a-plan-for-this-note)
9
11
  2. [Does pass 1 slice, and does the model fit inside it?](#does-pass-1-slice-and-does-the-model-fit-inside-it)
10
12
  3. [Can each scenario actually be performed?](#can-each-scenario-actually-be-performed)
@@ -37,8 +39,8 @@ a second changeset — say so in `covers`.
37
39
 
38
40
  ## Can each scenario actually be performed?
39
41
 
40
- **Ask which persona performs it. If the answer is "nobody", it belongs one level up or to no pass
41
- 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
42
44
  RPC caller, so "a caller with no session is refused" is a sentence a backend or permission
43
45
  scenario cannot perform. Signed-out is a BROWSER fact.
44
46
 
@@ -78,7 +80,7 @@ decision that nothing records.
78
80
  in this plan, or one already in the meta. Write "when the distributor is removed its companies
79
81
  fall back to the direct catalog" with no function that removes a distributor and `plan set`
80
82
  accepts it, `plan progress` passes, and the changeset ships a sentence nothing proves. Read each
81
- 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.
82
84
 
83
85
  **Where a scenario calls the same function twice, check it for a per-period guard first.** A
84
86
  one-per-day, one-per-order or one-per-person rule makes the second call a refusal, so the journey
@@ -99,14 +101,14 @@ mark a product's countries, cannot be proven without amending the plan mid-build
99
101
  **Every state a scenario waits in needs the function that leaves the world like that — checked
100
102
  against the code ALREADY built.** Three failure shapes, in rising order of cost:
101
103
 
102
- - *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
103
105
  moved invoicing to checkout. No order a customer could place was ever in that state; three
104
106
  scenarios passed once against old rows, then failed.
105
- - *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
106
108
  the app or its addons could put an order in `authorized` — the addon only holds money when
107
109
  checkout was started with a flag the plan's own checkout input did not carry.
108
- - *Wrong person*: a distributor salon owner planned to buy from a catalogue that is scoped by
109
- 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.
110
112
 
111
113
  **A `model` slot saying "this changeset adds no table" has to be true of the DATA the scenarios
112
114
  read, not only of the entities they name.** One promised a checkout priced by delivery country — a
@@ -1,15 +1,16 @@
1
1
  ---
2
2
  name: pikku-auth
3
3
  description: >-
4
- Use for anything about identity in a Pikku app — authenticating a caller (login, logout,
5
- sessions, cookies, bearer tokens, API keys, JWT, OAuth/social providers, MFA, Better Auth) and
6
- authorizing one (pikkuPermission, pikkuAuth, scopes, defineScope, global permissions). Covers
7
- which of the two a problem actually is, the built-in auth strategies, machine-to-machine auth
8
- and `pikku login`, and the JWT service. TRIGGER when: user asks about login, logout, session,
9
- cookie auth, bearer tokens, API keys, JWT, Better Auth, social providers, restricting who may
10
- call a function, resource ownership, roles, scopes, or hits MissingScopeError or
11
- InvalidSessionError. DO NOT TRIGGER when: user asks about middleware mechanics with no identity
12
- involved (use pikku-middleware) or about secrets and env vars (use pikku-services).
4
+ Use for identity in a Pikku app: authenticating a caller (login, logout, sessions, cookies,
5
+ bearer tokens, API keys, JWT, OAuth/social providers, MFA, Better Auth, machine callers,
6
+ `pikku login`). Covers telling authentication from authorization, the built-in strategies and
7
+ the JWT service. Authorization rules (ownership, roles, scopes, global permissions) live in
8
+ pikku-permissions. TRIGGER when: user asks about login, logout, session, cookie auth, bearer
9
+ tokens, API keys, JWT, Better Auth, social providers, how a CLI/agent/sandbox authenticates,
10
+ or hits InvalidSessionError. DO NOT TRIGGER when: the question is who may call a function,
11
+ such as ownership, roles, per-row checks, scopes or MissingScopeError (use pikku-permissions),
12
+ middleware mechanics with no identity involved (use pikku-middleware), or secrets and env
13
+ vars (use pikku-services).
13
14
  installGroups: [core]
14
15
  ---
15
16
 
@@ -28,7 +29,8 @@ Pikku app.
28
29
  function runs, and ends in a call to `setSession`.
29
30
 
30
31
  **Authorization** answers _may this caller do this_. It is declared on the
31
- function — `scopes` and `permissions` — never checked inside its body.
32
+ function — `scopes` and `permissions` — never checked inside its body. All of it is
33
+ covered by the pikku-permissions skill.
32
34
 
33
35
  A `permissions` entry that verifies a bearer token and returns `true` is
34
36
  authentication wearing an authorization hat: it leaves the function sessionless,
@@ -39,7 +41,7 @@ middleware instead.
39
41
 
40
42
  | You are… | Read |
41
43
  | --- | --- |
42
- | Restricting who may call a function — ownership, roles, scopes | `references/permissions.md` |
44
+ | Restricting who may call a function — ownership, roles, scopes | the pikku-permissions skill |
43
45
  | Reading or setting the session, or wiring `authBearer`/`authCookie`/`authAPIKey` | `references/sessions.md` |
44
46
  | Standing up user sign-in — OAuth, email+password, MFA, organizations | `references/better-auth.md` |
45
47
  | Authenticating a CLI, agent, sandbox or worker — API keys, `pikku login` | `references/machine-auth.md` |
@@ -53,30 +55,8 @@ will not work against them. `references/better-auth.md` has the setup; the
53
55
  built-in strategies in `references/sessions.md` are how a resolved credential
54
56
  becomes a Pikku session, not a replacement for it.
55
57
 
56
- ## The three gates
57
-
58
- Authorization is three independent gates, all of which must pass, in this order:
59
-
60
- 1. **Scopes** (`scopes`) — AND'd, checked before input validation, fails closed.
61
- 2. **Global permissions** (`addGlobalPermission`) — AND'd, an app-wide baseline.
62
- 3. **The function's own `permissions`** — OR'd groups of AND'd entries.
63
-
64
- They are independent: a broad global gate can never satisfy a function's own
65
- requirement, and a scope can only ever narrow access, never grant it.
66
-
67
58
  ## What NOT to do
68
59
 
69
- - **Do not check authorization inside `func`.** The `permissions` field is
70
- visible to the inspector; a check in the body is invisible to `pikku info
71
- permissions` and to an audit. The one sanctioned exception is
72
- `permissionsInBody`, which is purely declarative — see
73
- `references/permissions.md`.
74
- - **Do not write an "is signed in" permission.** `auth: true` (the default on
75
- `pikkuFunc`) already requires the session. A checker returning `!!session`
76
- gates nothing.
77
- - **Do not put `scopes` on a `pikkuSessionlessFunc`.** Scopes fail closed and an
78
- anonymous caller holds none, so it would reject every caller it exists to
79
- serve. Gate those with `permissions`, which receive an optional session.
80
60
  - **Do not expect the built-in strategies to authenticate a non-HTTP caller.**
81
61
  `authBearer`, `authCookie` and `authAPIKey` all step aside when there is no
82
62
  HTTP request — a queue job, a scheduled task or a channel message needs its
@@ -84,6 +64,5 @@ requirement, and a scope can only ever narrow access, never grant it.
84
64
  - **Do not share one header between the human and machine paths.**
85
65
  `Authorization: Bearer` is the human session; `x-api-key` is the machine key.
86
66
  Merging them reintroduces the ambiguity the split exists to remove.
87
- - **Do not reach for wire-, tag- or route-level permissions.** They were removed
88
- in #972. Permissions live on the function, plus the optional global gate; tags
89
- are organizational only.
67
+ - **Do not write authorization here.** Ownership, roles, scopes and global
68
+ permissions belong in the `permissions`/`scopes` fields; see pikku-permissions.