@pikku/skills 0.12.35 → 0.12.38

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 (41) hide show
  1. package/README.md +9 -4
  2. package/dist/index.d.ts +7 -4
  3. package/dist/index.js +9 -5
  4. package/dist/skills.gen.d.ts +1 -0
  5. package/dist/skills.gen.js +5 -3
  6. package/dist/snippets.d.ts +26 -0
  7. package/dist/snippets.js +148 -0
  8. package/package.json +2 -2
  9. package/skills/pikku-addon/SKILL.md +70 -24
  10. package/skills/pikku-addon/references/addon-package-manifest.md +9 -4
  11. package/skills/pikku-addon/references/openapi.md +130 -0
  12. package/skills/pikku-agent/references/agents.md +3 -1
  13. package/skills/pikku-auth/references/better-auth.md +33 -2
  14. package/skills/pikku-build/SKILL.md +94 -6
  15. package/skills/pikku-build/references/app.md +82 -12
  16. package/skills/pikku-build/references/design.md +16 -5
  17. package/skills/pikku-build/references/feature.md +23 -96
  18. package/skills/pikku-build/references/openapi.md +119 -0
  19. package/skills/pikku-build/references/platform.md +4 -0
  20. package/skills/pikku-build/references/quick.md +16 -6
  21. package/skills/pikku-changes/SKILL.md +172 -0
  22. package/skills/pikku-concepts/SKILL.md +33 -138
  23. package/skills/pikku-concepts/references/bootstrap.md +58 -0
  24. package/skills/pikku-concepts/references/concept-mapping.md +16 -0
  25. package/skills/pikku-concepts/references/language.md +87 -0
  26. package/skills/pikku-deploy/SKILL.md +1 -1
  27. package/skills/pikku-fabric/SKILL.md +13 -13
  28. package/skills/pikku-guide/SKILL.md +264 -0
  29. package/skills/pikku-kysely/SKILL.md +1 -1
  30. package/skills/pikku-n8n-import/SKILL.md +4 -3
  31. package/skills/pikku-react/references/client.md +12 -0
  32. package/skills/pikku-realtime/SKILL.md +6 -6
  33. package/skills/pikku-report/SKILL.md +143 -0
  34. package/skills/pikku-scenario/SKILL.md +71 -562
  35. package/skills/pikku-scenario/references/browser.md +59 -0
  36. package/skills/pikku-scenario/references/coverage.md +70 -0
  37. package/skills/pikku-scenario/references/personas.md +149 -0
  38. package/skills/pikku-scenario/references/steps.md +366 -0
  39. package/skills/pikku-service-backends/SKILL.md +1 -1
  40. package/skills/pikku-wiring/SKILL.md +1 -1
  41. package/skills/pikku-workflow/SKILL.md +7 -8
@@ -24,7 +24,6 @@ agent:
24
24
  command: pikku knowledge validate
25
25
  - id: typechecks
26
26
  command: pikku all --tsc-summary
27
-
28
27
  ---
29
28
 
30
29
  # Build on Pikku
@@ -45,12 +44,14 @@ plus more effort" — it is App plus a deliberate surface checklist, so read the
45
44
  base first and follow it in full rather than blending the two into one plan.
46
45
 
47
46
  The supporting references belong to whichever mode sends you to them:
48
- `references/multi-app.md` (a second frontend), `references/design.md` (offering
49
- to mock the screens first, committing to a design direction, and judging whether
50
- the screens realise it — read before the first screen is built, not after the
51
- last), `references/theming.md`
47
+ `references/multi-app.md` (a second frontend), `references/design.md` (showing
48
+ a picture of the screens first, committing to a design direction, and judging
49
+ whether the screens realise it — read before the first screen is built, not
50
+ after the last), `references/theming.md`
52
51
  (authoring the theme),
53
- `references/ship.md` (deploying, and the Fabric-readiness contract).
52
+ `references/ship.md` (deploying, and the Fabric-readiness contract),
53
+ `references/openapi.md` (an app on an OpenAPI spec: the auth mode, the
54
+ auth-config format, and the sign-in or connect screen it implies).
54
55
 
55
56
  ## Bootstrap before anything else
56
57
 
@@ -63,6 +64,24 @@ generated code depends on, and on a fresh scaffold **every command that touches
63
64
  codegen fails until it has run**, including ones you would reasonably reach for
64
65
  while still planning. Those failures look alarming and are nothing but this.
65
66
 
67
+ ## Start from what you were handed
68
+
69
+ When the request comes with a file or a URL, look at it before planning
70
+ anything. Two kinds are converted first and then built on:
71
+
72
+ | Handed | Say, then do |
73
+ | ------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
74
+ | An **OpenAPI / Swagger spec** — top-level `openapi` or `swagger` key, a `paths` object | "This is an OpenAPI spec — I'll turn it into an addon first." Pick the auth mode in `references/openapi.md`, then follow the `pikku-addon` skill's OpenAPI reference. |
75
+ | An **n8n export** — an object with `nodes` and `connections`, an array of them, or a `{ workflows: [...] }` wrapper | "This is an n8n workflow — I'll import it first." Follow `pikku-n8n-import`. |
76
+
77
+ Say it at once, in one line, and start: this is the obvious first move, not a
78
+ question for the user. Generate the whole spec, however large.
79
+
80
+ Neither is the app. When the conversion compiles, come back here and carry on
81
+ in the mode the request calls for — App by default — planning milestones around
82
+ what the user wants to do with the API or the workflow, and reaching the
83
+ generated functions through `ref()`.
84
+
66
85
  ## What holds in every mode
67
86
 
68
87
  - **The branch and the diff are the contract.** A reviewer sees real, compiled,
@@ -86,6 +105,75 @@ while still planning. Those failures look alarming and are nothing but this.
86
105
  `pikku knowledge plan progress` measures the build against it from the
87
106
  generated meta. You plan it and you build it; what you never do is edit the
88
107
  plan afterwards to match what you built — that is grading yourself.
108
+ - **Print the links whenever the stack comes up, and in every hand-over.** Full,
109
+ clickable URLs, with the ports taken from what `bun run dev` actually printed:
110
+ - **App** — the frontend's URL (`http://localhost:7104` in the template; each
111
+ frontend in `pikkufabric.config.json` has its own port)
112
+ - **API** — `http://localhost:3000`
113
+ - **Console** — `http://localhost:3000/console`, plus a deep link to each
114
+ page that shows what this turn produced (the paths are below)
115
+
116
+ A person who has to go hunting for the port assumes the app did not start.
117
+
118
+ ## Keep a BUILD-REPORT.md
119
+
120
+ Whenever pikku or a skill costs you time — a command that failed on a fresh
121
+ tree, a skill that described a flag the CLI does not have, generated code you
122
+ had to fix by hand — add an entry to `BUILD-REPORT.md` at the repo root as it
123
+ happens: what you ran, what you expected, what happened, and the workaround.
124
+ Leave out secrets, tokens and customer data.
125
+
126
+ At hand-over, show the file and ask the user whether to send it. Only with
127
+ their okay, send each entry with `pikku fabric report --stdin` (JSON on stdin;
128
+ `"kind": "product"` when pikku behaved wrongly, `"kind": "harness"` with
129
+ `"skill"` and `"passage"` when a skill misled you). The `pikku-report` skill
130
+ has the fields. When the CLI is not signed in to Fabric, the report is queued
131
+ locally rather than sent: say so, and that `pikku fabric findings flush` sends
132
+ the queue once they sign in. Do not retry or file it twice.
133
+
134
+ ## Who you are talking to
135
+
136
+ The prompt asks first how technical the person is: **not technical**,
137
+ **technical, no code**, or **developer** (the default when unsaid). Whenever the
138
+ build makes or changes something the console can show, give the
139
+ `http://localhost:<port>/console/...` link instead of describing it.
140
+
141
+ Until the app is deployed that is the local open-source console, on the port
142
+ `pikku dev` printed. Once it is on Fabric, link the Fabric console for the stage
143
+ you are talking about instead; the `pikku-fabric` skill says which.
144
+
145
+ | Level | Links | Code in the conversation |
146
+ | ------------------ | --------------------------- | ------------------------ |
147
+ | Not technical | Product pages only | Never |
148
+ | Technical, no code | Product and technical pages | Never |
149
+ | Developer | Product and technical pages | As normal |
150
+
151
+ "Never" includes snippets and command lines; say what changed in the person's
152
+ words and link to where they can see it.
153
+
154
+ **Product pages** — the only ones a non-technical person gets:
155
+
156
+ | Shows | Path |
157
+ | --------------------- | -------------------------------------------------------------------------- |
158
+ | Knowledge, plans | `/console/knowledge`, `/console/knowledge?id=<note path>` |
159
+ | Personas | `/console/personas`, `/console/virtual-users?persona=<id>` |
160
+ | Roles and permissions | `/console/roles`, `/console/scopes` (only with `@pikku/addon-admin` wired) |
161
+ | Scenarios and runs | `/console/scenarios?id=<id>`, `/console/scenarios?view=runs&run=<run id>` |
162
+ | Workflows | `/console/workflow?id=<id>` |
163
+ | Agents | `/console/agents`, `/console/agents/playground?id=<agent id>` |
164
+
165
+ **Technical pages** — never for a non-technical person: `/console/overview`,
166
+ `/console/functions`, `/console/surface`, `/console/database`,
167
+ `/console/changes`, `/console/wires/http`, `/console/wires/channel`,
168
+ `/console/wires/mcp`, `/console/wires/cli`, `/console/wires/gateway`,
169
+ `/console/async/scheduler`, `/console/async/queue`, `/console/async/trigger`,
170
+ `/console/runtime`, `/console/emails`, `/console/webhooks`, `/console/secrets`,
171
+ `/console/variables`, `/console/security`, `/console/auth-providers`,
172
+ `/console/addons`, `/console/analytics`, `/console/credentials`,
173
+ `/console/users`, `/console/audit`, `/console/flags`, `/console/scorers`.
174
+
175
+ These come from `packages/console/src/App.tsx`; do not link a path that is not
176
+ listed here.
89
177
 
90
178
  ## What NOT to do
91
179
 
@@ -24,6 +24,9 @@ project shaped so `pikku fabric init` later adopts it with zero rework.
24
24
  wirings, schemas or generated clients may have changed.
25
25
  4. If validation fails, fix the source cause and rerun. Do not paper over
26
26
  generated errors by editing generated files.
27
+ 5. Report at the person's level (SKILL.md, "Who you are talking to"): a console
28
+ link for everything the console can show, and no code unless they are a
29
+ developer.
27
30
 
28
31
  ## 0. Bootstrap, before anything else
29
32
 
@@ -62,11 +65,18 @@ in one message. Then stop; do not interview the user.
62
65
  a reference (brand guide, screenshots, a site whose register they want); or
63
66
  their own design agent/prompt, whose output you take as the direction.
64
67
  - **Do they want to see the screens before you build them?** Offer it here, in
65
- this same round, as a question and not a gate: one HTML page mocking the main
66
- screens, a few minutes, far cheaper to change than built screens. If they say
67
- yes, `references/design.md` owns what to make and what it then binds — the
68
+ this same round, with yes marked recommended, in the words of
69
+ `references/design.md` — "a picture of the main screens so you can say 'yes,
70
+ like that' or 'no, move this'", never "mock" or "wireframe". Behind it is one
71
+ HTML page mocking the main screens, a few minutes of work. On a yes, or no
72
+ answer at all, `references/design.md` owns what to make and what it then binds — the
68
73
  approved page becomes source of truth for the screens, and the theme is written
69
- before it so what they approve is what ships. If they say no, build.
74
+ before it so what they approve is what ships. Only an explicit no skips it.
75
+ - **May I write test records into the system it talks to?** Ask only when the
76
+ app reads a live system through an addon (an ERP, a CRM) and a milestone needs
77
+ data that isn't there yet: an unpaid invoice, a closed ticket. Say what you
78
+ would create and that it will be marked "Test". A no means building those
79
+ screens against their empty states.
70
80
  - **What language should the app speak, and what language does the team work
71
81
  in?** Two answers, not one — see §1a, which is where they go. Ask only if the
72
82
  request is not obviously English; a brief written in English about an English
@@ -343,8 +353,9 @@ What a milestone is:
343
353
  persona. If you cannot write the gherkin, you cannot build it yet — that is a
344
354
  `questions/` note, not a milestone.
345
355
 
346
- If §1's screen mock was made and approved, the milestones are read off it: every
347
- screen on that page belongs to some milestone, and a screen no milestone builds
356
+ If §1's screen mock was made — approved, or drawn because nobody answered — the
357
+ milestones are read off it: every screen on that page belongs to some milestone,
358
+ and a screen no milestone builds
348
359
  is a hole in this plan. Say which milestone covers which screen.
349
360
 
350
361
  How to order them:
@@ -363,8 +374,17 @@ How to order them:
363
374
  Number the files (`01-…`, `02-…`) so the order is visible in the tree. Then
364
375
  `knowledge index && knowledge validate` before you write a line of code.
365
376
 
366
- **Show the user the list before building.** This is the last cheap moment to
367
- reorder — after §6 the migrations are numbered and the order is concrete.
377
+ **One approval, then build to the end.** Show the picture of the screens and
378
+ the milestone list together, in one message, as the plan: which milestone builds
379
+ which screen, in what order. That is the only approval you ask for. It is the
380
+ last cheap moment to reorder: after §6 the migrations are numbered and the order
381
+ is concrete.
382
+
383
+ Once they approve it, or don't answer, build every milestone in order without
384
+ stopping to ask between them. Post one line as each milestone closes, with its
385
+ console links, and carry on. Stop only for what is theirs to decide: a
386
+ credential you don't have, spending money, posting in public, deleting or
387
+ overwriting their data, or a finding that changes the plan.
368
388
 
369
389
  ## 5a. The technical plan — one milestone at a time, before you build it
370
390
 
@@ -399,6 +419,8 @@ no plan, and everything after the current milestone is still allowed to move.
399
419
 
400
420
  ## 6. Implement milestones, one at a time
401
421
 
422
+ All of them, one after another, on the one approval from §5.
423
+
402
424
  **Per milestone** — plan it (§5a), set its note to `status: dispatched`, do the
403
425
  six steps, close it out (§6a), set it to `built`. Do not start the next one
404
426
  until §6a passes, §7 is green for this one _and §7a shows its functions
@@ -494,6 +516,35 @@ Rules that are not optional:
494
516
  render the failure inline next to the control that triggered it — not a toast.
495
517
  - An exposed function with no session and no permission is reachable by anyone
496
518
  over `POST /rpc/:rpcName` (PKU574). Either gate it or drop `expose: true`.
519
+ - A public, signed-out read (a homepage's programme, a price list) is a
520
+ `pikkuSessionlessFunc`. `pikkuFunc` with `auth: false` still answers
521
+ `MissingSessionError` over `/rpc` to a caller with no session.
522
+ - Better Auth already owns the `user`, `session`, `account` and `verification`
523
+ tables. A domain table with one of those names — a class _session_, a drop-in
524
+ _session_ — collides in the migration. Name it for the domain instead
525
+ (`evening`, `class_meeting`) and keep the word in the UI copy.
526
+ - The template's `/` redirects to `/app`, so the login screen — and its "Sign in
527
+ as …" switcher — is what a signed-out visitor sees first. Replace `/` with a
528
+ public homepage and that stops being true: mount `<DevActorSwitcher />` in the
529
+ public layout as well, or a reviewer lands on a site with no way in.
530
+
531
+ Before the first run, make sure `.env` at the project root holds the two
532
+ secrets the local stack needs. `bun run dev` appends whichever is missing, but
533
+ check anyway — a project scaffolded from an older template, or a `.env` copied
534
+ in from elsewhere, can lack one, and neither failure names the variable:
535
+
536
+ - `BETTER_AUTH_SECRET` — without it the first sign-up is a 500.
537
+ - `SCENARIO_ACTOR_SECRET` — without it `/api/auth/sign-in/actor` is disabled:
538
+ every scenario fails at sign-in before its first step, and the "Sign in as …"
539
+ switcher renders nothing.
540
+
541
+ ```sh
542
+ grep -q '^BETTER_AUTH_SECRET=' .env 2>/dev/null || echo "BETTER_AUTH_SECRET=$(openssl rand -base64 32)" >> .env
543
+ grep -q '^SCENARIO_ACTOR_SECRET=' .env 2>/dev/null || echo "SCENARIO_ACTOR_SECRET=$(openssl rand -base64 32)" >> .env
544
+ ```
545
+
546
+ `.env` is gitignored and local only — never commit it. A deployed stage gets its
547
+ secrets from the platform (`pikku fabric secrets`), not from this file.
497
548
 
498
549
  Then run it:
499
550
 
@@ -505,6 +556,19 @@ That starts the API on :3000 and every frontend in `pikkufabric.config.json`. A
505
556
  frontend running against a dead API looks exactly like an app bug, so if every
506
557
  request fails, check that both halves came up.
507
558
 
559
+ **Start the stack through `bun run dev`, not by launching `vite` or `pikku dev`
560
+ yourself.** The dev script reads the personas, derives one credential per
561
+ persona from `SCENARIO_ACTOR_SECRET`, and hands both to the frontend as
562
+ `VITE_DEV_ACTORS` / `VITE_DEV_ACTOR_SECRETS`. Vite reads those once, at boot. A
563
+ frontend started any other way — or restarted by hand later — has an empty
564
+ actor list, and the switcher silently disappears from every page. If you do
565
+ start the frontend on its own (say :3000 is taken by another project), you owe
566
+ it three things: the two `VITE_DEV_*` values the dev script would have computed,
567
+ and `VITE_API_PROXY` pointing at your API — the dev proxy defaults to
568
+ `http://localhost:3000`, so beside another project's server your sign-ins go to
569
+ _its_ API and come back `401 Invalid actor secret`, which reads like a bad
570
+ credential rather than the wrong server.
571
+
508
572
  The `--bun` in `bunx --bun pikku …` is load-bearing — keep it. Without it the
509
573
  CLI's `#!/usr/bin/env node` shebang hands the process to whatever Node is on
510
574
  PATH, which fails below Node 24 with `ERR_UNKNOWN_BUILTIN_MODULE: No such
@@ -614,10 +678,11 @@ export const tenantReportsAFaultScenario = pikkuScenario<void, { id: string }>({
614
678
  - **Write the refusals.** The third step above is the whole point of §4: one
615
679
  persona reaching for another's row has to be rejected, and that rejection is a
616
680
  scenario. It is how you prove access control instead of asserting it.
617
- - **Add `SCENARIO_ACTOR_SECRET` to `.env`.** `bun run dev` generates that file
618
- with a `BETTER_AUTH_SECRET` and nothing else, and without the actor secret
619
- `/api/auth/sign-in/actor` is disabled — every scenario then fails at sign-in,
620
- before its first step, for a reason that reads like an auth bug.
681
+ - **`SCENARIO_ACTOR_SECRET` must be in `.env`** (§6, before the first run).
682
+ Without it `/api/auth/sign-in/actor` is disabled — every scenario then fails
683
+ at sign-in, before its first step, for a reason that reads like an auth bug.
684
+ `pikku scenario run` reads it from the environment, so source `.env` first
685
+ (`set -a && . ./.env && set +a`) when you run outside `bun run dev`.
621
686
  - **There is no state reset.** A scenario runs against a live server: scope what
622
687
  you create to your own rows and unique ids, and never assume a clean database.
623
688
 
@@ -786,6 +851,11 @@ standalone`, `cloudflare`, `aws`), how to serve several frontends behind one
786
851
  API, the pre-release gate to run, and the contract that keeps `pikku fabric init`
787
852
  a one-command import later rather than a migration.
788
853
 
854
+ The app is not handed over without its user guide. The scenarios you wrote are
855
+ already its skeleton: read **pikku-guide**, write one page per audience citing
856
+ every feature, and build it from a full, passing `--run browser --screenshots`
857
+ run.
858
+
789
859
  Two things from it are worth knowing before you get there, because they are
790
860
  cheaper to honour than to retrofit:
791
861
 
@@ -54,13 +54,18 @@ allowed is landing on one because it was nearest to hand.
54
54
  ## Offer to draw the screens before you build them
55
55
 
56
56
  Before the first milestone, **ask** whether they want to see the screens first.
57
- One question, in §1's round, not a gate of its own:
57
+ One question, in §1's round, not a gate of its own, with yes marked
58
+ recommended every time:
58
59
 
59
- > Want me to mock the main screens as a page you can look at before I build
60
- > anything? It takes a few minutes and it is much cheaper to change a picture
61
- > than a built screen.
60
+ > Before I build, shall I show you a picture of the main screens so you can say
61
+ > "yes, like that" or "no, move this"? (Recommended: it takes a few minutes and
62
+ > changing a picture is much cheaper than changing a built app.)
62
63
 
63
- If they decline, build; the direction in words is enough to be accountable to.
64
+ Never say "mock", "mockup" or "wireframe" to the person; most people do not
65
+ know the words. They stay the technical terms in this file only.
66
+
67
+ If they do not answer, draw it anyway and build from it. Only an explicit no
68
+ skips it; then build, and the direction in words is enough to be accountable to.
64
69
  If they accept, this is the cheapest decision in the project — a picture of eight
65
70
  screens costs a fraction of eight built screens, and it is the only point where
66
71
  "that is not what I meant" is free.
@@ -105,6 +110,12 @@ first. Whatever your host offers for showing a page is how you show it: an
105
110
  Artifact, a file they open, a preview server. The page is the deliverable; how it
106
111
  gets in front of them is not this file's business.
107
112
 
113
+ **Say it is a picture, on the page and in the message.** A well-drawn screen
114
+ reads as a finished app, and a person who thinks it is already built asks why
115
+ nothing works. The page opens with a banner that stays in view: "A picture of
116
+ the planned screens. Nothing is built yet." The message that shows it says the
117
+ same, then says what happens next: "Once you're happy with it, I'll build it."
118
+
108
119
  Write it to `knowledge/decisions/design/screens.html` and treat it as **source of
109
120
  truth for the screens** once they approve it. That has consequences worth
110
121
  stating:
@@ -1,5 +1,19 @@
1
1
  # Pikku Create-a-Feature
2
2
 
3
+ The stages, in order:
4
+
5
+ - [Agent Operating Procedure](#agent-operating-procedure)
6
+ - [Stage 1 — Discover](#stage-1--discover)
7
+ - [Stage 2 — State intent in plain English (BEFORE writing code)](#stage-2--state-intent-in-plain-english-before-writing-code)
8
+ - [Stage 3 — Branch off](#stage-3--branch-off)
9
+ - [Stage 4 — Implement](#stage-4--implement)
10
+ - [Stage 5 — Verify](#stage-5--verify)
11
+ - [Stage 6 — Commit](#stage-6--commit)
12
+ - [Stage 7 — Hand off](#stage-7--hand-off)
13
+ - [Report what fought you](#report-what-fought-you)
14
+ - [Hard constraints](#hard-constraints)
15
+ - [Output discipline](#output-discipline)
16
+
3
17
  ## Agent Operating Procedure
4
18
 
5
19
  Use this skill as an execution checklist, not reference material.
@@ -7,7 +21,7 @@ Use this skill as an execution checklist, not reference material.
7
21
  1. Discover before editing. Run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
8
22
  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.
9
23
  3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
10
- 4. Validate with the narrowest relevant command first, then run `pikku-verify` or `pikku all` when functions, wirings, schemas, or generated clients may have changed.
24
+ 4. Validate with the narrowest relevant command first, then run `pikku all` when functions, wirings, schemas, or generated clients may have changed.
11
25
  5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
12
26
  6. Report anything about pikku itself that cost you time, the moment it happens — see **Report what fought you**.
13
27
 
@@ -242,103 +256,16 @@ Do not push without explicit confirmation. Do not merge.
242
256
 
243
257
  ## Report what fought you
244
258
 
245
- When pikku itself is what cost you time, report it with `pikku fabric report`.
246
- Nothing is written to the repo; the finding goes to the linked fabric project
247
- and the terminal shows you exactly what was sent.
248
-
249
- **Report at the moment it happens**, not at the end from memory — a run that
250
- falls over never reaches its end. One finding per thing that fought you.
251
-
252
- ### The ladder
253
-
254
- 1. **Find the quicker workaround.** The user is paying for their feature, not
255
- for pikku's health.
256
- 2. **Investigate** only when there is no workaround, or when the user asks why
257
- something is slow or wrong.
258
- 3. **Report at the depth you already reached.** Never spend extra effort to
259
- file; never throw away effort you already spent. If the investigation took
260
- you to the mechanism, the finding says so — named file, named function, what
261
- is actually happening, and what pikku should do instead.
262
-
263
- **Never fix pikku itself.** Not a patch in `node_modules`, not a linked
264
- checkout, not a branch in the framework repo. Many agents each patching pikku to
265
- unblock themselves is many divergent copies and a merge problem nobody signed up
266
- for. Work around it in the app, report it, and let the fix happen once.
267
-
268
- ### What counts
269
-
270
- Anything that cost you time and would cost the next person the same. Most of
271
- these never produce an error: output that is quietly wrong, a generated type
272
- that disagrees with the runtime, a check that passes when it should not, a
273
- narrowing you had to write by hand because the framework should have written it.
274
- **Having to write code the framework should have written for you is a finding.**
275
-
276
- So is anything that only shows up in one place — invisible locally, fatal
277
- deployed, or the reverse. Say which, with `--surface`.
278
-
279
- Not a finding: a preference, a thing you would have designed differently, or
280
- baseline noise that was already failing before you started.
281
-
282
- ### Two kinds
283
-
284
- - `--kind product` — pikku behaved wrongly. Fixing it is a change to the
285
- framework.
286
- - `--kind harness` — a skill misled you: it told you to run something that does
287
- not exist, described a flag that is spelled differently, or contradicted what
288
- the CLI actually did. Pass `--skill <name>` and `--passage "<the line or
289
- section>"`. This is the most useful kind to file, because it is fixable
290
- immediately — so file it even when the cost was small.
291
-
292
- ### When there was no workaround
293
-
294
- Report it anyway with `--unresolved`, and put what you tried and how each
295
- attempt failed in `--tried`. That is what stops the next person walking the same
296
- dead ends. Tell the user what you did instead — abandoned it, shipped something
297
- degraded, or stopped.
298
-
299
- `--unresolved` means **no workaround was found**. It does not mean the
300
- workaround was unpleasant.
301
-
302
- ### The command
303
-
304
- Send it as JSON on stdin. Most of a finding is prose, and prose carries
305
- apostrophes, quotes, backticks and newlines — each one a shell metacharacter
306
- before it is a character in your sentence. A stack trace passed to `--error`
307
- breaks the command at its first newline; a backtick in `--actual` runs whatever
308
- follows it. Quote the heredoc delimiter (`<<'EOF'`, never `<<EOF`) so the shell
309
- leaves the body alone.
310
-
311
- ```bash
312
- pikku fabric report --stdin <<'EOF'
313
- {
314
- "title": "<one-line title>",
315
- "kind": "product",
316
- "model": "<the model you are>",
317
- "expected": "<what you expected pikku to do>",
318
- "actual": "<what it did instead>",
319
- "command": "<the command you ran>",
320
- "workaround": "<what you did instead, inside the app>"
321
- }
322
- EOF
323
- ```
324
-
325
- Add whichever of these you actually have: `error` (the error's message line,
326
- verbatim), `repro` (the shortest way to reach it again), `proposal` (what pikku
327
- should do), `area`, `surface` (`local`, `deployed` or `both`), `cost` (measured
328
- if you measured it — "98s vs 20s steady" ranks; "slow" does not), `run` (an id
329
- shared by every finding from this build), `deployTarget`.
330
-
331
- The same fields exist as flags — `--kind`, `--expected` and so on — for a
332
- finding short enough to type. Anything with a newline or a quote in it goes
333
- through `--stdin`.
259
+ When pikku itself is what cost you time — a wrong generated type, a check that
260
+ passed when it should not, a skill that misled you — file it with `pikku fabric
261
+ report`. The `pikku-report` skill owns the ladder, the two kinds, the JSON-on-
262
+ stdin form and the local spool; read it before filing.
334
263
 
335
- Versions, platform and package manager are read off the installed tree for you.
336
- Do not pass them and do not ask the user for them.
264
+ Reporting at all is permitted here (see **Hard constraints**) and is the one
265
+ network call a build may make. Nothing is written to the repo. Never patch pikku
266
+ itself — not `node_modules`, not a linked checkout — work around it in the app,
267
+ report it, and let the fix happen once.
337
268
 
338
- Reporting never fails a build. A finding that cannot be sent — logged out, or
339
- fabric unreachable — is held on the machine and goes out with the next report
340
- that succeeds, so nothing you file is lost. If it says the finding was queued,
341
- carry on with the feature; do not try to fix it, and do not file it again.
342
269
 
343
270
  ## Hard constraints
344
271
 
@@ -0,0 +1,119 @@
1
+ # An app on top of an OpenAPI spec
2
+
3
+ The spec becomes an addon first — the `pikku-addon` skill's
4
+ `references/openapi.md` covers generating, verifying and checking it against
5
+ the real API. This reference is the decision that shapes the app around it:
6
+ **whose credentials reach the upstream**, and what the person signs in with.
7
+
8
+ ## Pick the auth mode
9
+
10
+ | The upstream… | Mode | Command | Who signs in, and how |
11
+ | ------------------------------------------------------------------------ | ----------------- | ----------------------------- | ------------------------------------------------------- |
12
+ | Is where the users already have accounts, and has a login endpoint | Delegated | `--auth-config <file>` | Users sign in with their upstream login; no new account |
13
+ | Takes a per-user API key, bearer token or basic auth | Per-user key | (the default) | App account, then a Connect screen for the key |
14
+ | Uses OAuth2 | Per-user OAuth | (the default) | App account, then Connect via the OAuth consent |
15
+ | Is used on everyone's behalf with one key the business owns | Shared secret | `--auth shared` | App account only; the key is a secret |
16
+ | Takes no auth | None | `--auth none` | App account only |
17
+
18
+ Per-user is the default because the upstream then enforces each person's own
19
+ permissions. A shared secret acts with one identity for everyone, so the
20
+ install exposes only its reads; widen that deliberately, never by default.
21
+
22
+ The mode comes from the spec's `securitySchemes` unless a flag overrides it.
23
+ Many real specs describe auth only in prose. The generator then refuses rather
24
+ than guessing. Read the API's docs and pass the flag that is true.
25
+
26
+ **Delegated is the right call whenever the upstream is the system of record for
27
+ who the users are** — an ERP, a CRM, a helpdesk the whole team already logs in
28
+ to. The app then has no separate sign-up, the upstream token is stored per user
29
+ at sign-in, and every call acts as that user.
30
+
31
+ ## The auth-config file
32
+
33
+ JSON, passed with `--auth-config`. Every field is optional except where noted.
34
+
35
+ ```json
36
+ {
37
+ "headerName": "DOLAPIKEY",
38
+ "headerFormat": "raw",
39
+ "extraHeaders": { "Origin": "https://tenant.example.com" },
40
+ "delegated": {
41
+ "loginPath": "/login",
42
+ "loginMethod": "post",
43
+ "credentials": ["login", "password"],
44
+ "fields": { "login": "login", "password": "password" },
45
+ "encoding": "json",
46
+ "tokenPath": "success.token",
47
+ "expiresAtPath": "success.expires",
48
+ "identity": { "path": "/users/info", "method": "get" },
49
+ "claims": {
50
+ "source": "identity",
51
+ "externalId": "id",
52
+ "email": "email",
53
+ "name": ["firstname", "lastname"],
54
+ "role": "admin",
55
+ "tenantId": "entity"
56
+ },
57
+ "emailTemplate": "{login}@{host}",
58
+ "roles": { "1": "dolibarr-admin" }
59
+ }
60
+ }
61
+ ```
62
+
63
+ Top level — how every call authenticates:
64
+
65
+ | Field | Meaning |
66
+ | -------------- | ---------------------------------------------------------------------------------------------------- |
67
+ | `headerName` | The header the API reads the token or key from. Setting it alone means a per-user API key |
68
+ | `headerFormat` | `raw` sends the bare value, `bearer` prefixes `Bearer `. Default: bearer for `Authorization`, else raw |
69
+ | `extraHeaders` | Static headers on every request, login included — for upstreams that route on a header |
70
+ | `delegated` | Present when users sign in with their upstream login |
71
+
72
+ `delegated`:
73
+
74
+ | Field | Meaning |
75
+ | ---------------- | --------------------------------------------------------------------------------------------------------- |
76
+ | `loginPath` | Required. The spec-relative login operation |
77
+ | `loginMethod` | Default `post` |
78
+ | `credentials` | What the sign-in form collects: `login` (a username or email), `email`, `password`, `apiKey`. Default `["email","password"]` |
79
+ | `fields` | The upstream's name for each credential, e.g. `{ "login": "username" }` |
80
+ | `encoding` | `json`, `form` or `query` — how the login fields travel. Default `json` |
81
+ | `apiKeyHeader` | The header an `apiKey` credential is sent in. Default `x-api-key` |
82
+ | `tokenPath` | Required. Dot-path to the token in the login response |
83
+ | `expiresAtPath` | Dot-path to an epoch-seconds expiry. Defaults to the JWT's `exp` when claims come from the JWT |
84
+ | `identity` | An operation that returns the signed-in user, called with the new token — for logins that return only a token |
85
+ | `claims.source` | `jwt`, `response` or `identity`. Default `identity` when `identity` is set, else `response` |
86
+ | `claims.*` | Dot-paths to `externalId`, `email`, `name` (one path or several joined with a space), `role`, `tenantId` |
87
+ | `emailTemplate` | Builds an email for an upstream user who has none: `{login}`, `{externalId}`, `{host}`. Such an address never claims an existing user |
88
+ | `roles` | Maps the raw `claims.role` value onto an app role; an unmapped value gets no role |
89
+
90
+ Check it before building on it: start `pikku dev`, sign in once with
91
+ `POST /api/auth/sign-in/delegated` as a real upstream user, and call an exposed
92
+ operation with the session. A wrong password must be refused. Keep real
93
+ credentials in the environment, never in the config or a test.
94
+
95
+ What the install wires for delegated mode — `pikkuDelegatedAuth` in
96
+ `src/auth.ts`, the token stored per user, actors carrying it into scenarios — is
97
+ in the `pikku-auth` skill's `references/better-auth.md`.
98
+
99
+ ## The screens
100
+
101
+ **If the app has a UI, build the sign-in or connect screen that matches the chosen auth mode (Sign in with <X> for delegated, a Connect <X> screen for per-user keys/OAuth, nothing for a shared secret), labelling fields in the upstream's terms (e.g. 'Dolibarr login', not 'Email').**
102
+
103
+ - **Delegated** — the sign-in page posts to `POST /api/auth/sign-in/delegated`
104
+ with the fields named in `credentials` (`login` or `username`, `password`).
105
+ It replaces email sign-up; there is no "create account".
106
+ - **Per-user key or OAuth** — a Connect screen after sign-in, and wherever a
107
+ call fails with `missing_credential`.
108
+ - A `credential_rejected` error (`CredentialRejectedError`, 403) means the
109
+ upstream refused the stored token: show the sign-in again (`reauth:
110
+ 'sign-in'`) or the Connect screen (`reauth: 'connect'`), not a generic error.
111
+
112
+ ## Scenarios
113
+
114
+ A persona that signs in through the upstream needs an upstream credential to
115
+ act with. The install adds `credentials` to `pikkuActor` in `src/auth.ts`, and
116
+ at sign-in each actor stores `ACTOR_CREDENTIAL_<PERSONA>_<NAME>` from the
117
+ environment (e.g. `ACTOR_CREDENTIAL_SALES_REP_DOLIBARR`). Without it every
118
+ scenario step that reaches the upstream fails with `missing_credential`. The
119
+ `pikku-scenario` skill's `references/personas.md` has the details.
@@ -16,6 +16,10 @@ failed; a showcase where every surface is a stub has also failed. The bar for
16
16
  each surface below: **it does something the app genuinely needs, and a scenario
17
17
  proves it.** A cron job that logs "tick" is not a schedule — it is a comment.
18
18
 
19
+ Each surface lands with its console link, filtered by the person's level
20
+ (SKILL.md, "Who you are talking to"): a non-technical person sees the workflow
21
+ or agent page, never the queue, scheduler or wire pages behind it.
22
+
19
23
  Budget the extra surfaces at one milestone each. They are not free, and a
20
24
  half-wired workflow engine is worse than no workflow engine.
21
25
 
@@ -149,7 +149,17 @@ than they save:
149
149
  keystroke now and a rewrite later.
150
150
  - Never hardcode a host or port — the API base resolves to same-origin `/api`.
151
151
 
152
- Then run it:
152
+ Before the first run, make sure `.env` holds both local secrets — `bun run dev`
153
+ appends missing ones, but an older scaffold or a copied `.env` may not have them:
154
+
155
+ ```sh
156
+ grep -q '^BETTER_AUTH_SECRET=' .env 2>/dev/null || echo "BETTER_AUTH_SECRET=$(openssl rand -base64 32)" >> .env
157
+ grep -q '^SCENARIO_ACTOR_SECRET=' .env 2>/dev/null || echo "SCENARIO_ACTOR_SECRET=$(openssl rand -base64 32)" >> .env
158
+ ```
159
+
160
+ Never commit `.env`. Then run it — through `bun run dev`, never `vite` on its
161
+ own, because the dev script is what hands the frontend the persona list for the
162
+ "Sign in as …" switcher; without it the switcher silently renders nothing:
153
163
 
154
164
  ```sh
155
165
  bun run prebuild && bun run dev
@@ -200,8 +210,7 @@ export const ownerCreatesAndSeesItScenario = pikkuScenario<void, { id: string }>
200
210
  `pikkuScenarioStep`.** An RPC name in a `then` will not resolve.
201
211
  - **Every scenario must assert.** A ladder with no `then` is a PKU680 critical —
202
212
  it fails `pikku all`, stopping codegen rather than a test.
203
- - **Add `SCENARIO_ACTOR_SECRET` to `.env`.** `bun run dev` writes that file with
204
- only a `BETTER_AUTH_SECRET`; without the actor secret
213
+ - **`SCENARIO_ACTOR_SECRET` must be in `.env`** (above). Without it
205
214
  `/api/auth/sign-in/actor` is disabled and every scenario fails at sign-in, for
206
215
  a reason that reads like an auth bug.
207
216
  - **There is no state reset** — scope what you create to unique ids.
@@ -214,9 +223,10 @@ bunx --bun pikku scenario run local --spawn
214
223
 
215
224
  ## 6. Hand it over honestly
216
225
 
217
- Tell the user, in one short paragraph: what runs, what it is seeded with, and
218
- that this is a quick build — no knowledge base, no milestones, no design pass,
219
- access control clicked-through rather than proven.
226
+ Tell the user, in one short paragraph and at their level (SKILL.md, "Who you
227
+ are talking to"), with console links rather than descriptions: what runs, what
228
+ it is seeded with, and that this is a quick build — no knowledge base, no
229
+ milestones, no design pass, access control clicked-through rather than proven.
220
230
 
221
231
  **Upgrading to a real build is additive, not a rewrite.** If they want it, switch
222
232
  to `references/app.md` and do this, in order: