@pikku/skills 0.12.54 → 0.12.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/skills.gen.js +2 -2
- package/dist/snippets.js +1 -1
- package/package.json +1 -1
- package/skills/pikku-architect/SKILL.md +1 -1
- package/skills/pikku-architect/references/plan-defects.md +7 -7
- package/skills/pikku-auth/references/better-auth.md +92 -96
- package/skills/pikku-build/SKILL.md +9 -9
- package/skills/pikku-build/references/app.md +56 -61
- package/skills/pikku-build/references/design.md +14 -14
- package/skills/pikku-build/references/feature.md +3 -3
- package/skills/pikku-build/references/mocks.md +6 -0
- package/skills/pikku-build/references/multi-app.md +16 -16
- package/skills/pikku-build/references/openapi.md +34 -34
- package/skills/pikku-build/references/platform.md +4 -5
- package/skills/pikku-build/references/post-clone.md +1 -1
- package/skills/pikku-build/references/quick.md +63 -36
- package/skills/pikku-build/references/scenarios.md +17 -16
- package/skills/pikku-build/references/ship.md +14 -16
- package/skills/pikku-build/references/theming.md +2 -2
- package/skills/pikku-changes/SKILL.md +2 -5
- package/skills/pikku-concepts/SKILL.md +26 -27
- package/skills/pikku-concepts/references/language.md +6 -6
- package/skills/pikku-deploy/references/nextjs.md +1 -1
- package/skills/pikku-guide/SKILL.md +18 -17
- package/skills/pikku-i18n/references/messages.md +5 -6
- package/skills/pikku-knowledge/SKILL.md +3 -3
- package/skills/pikku-meta/references/audit.md +1 -1
- package/skills/pikku-permissions/SKILL.md +1 -1
- package/skills/pikku-react/SKILL.md +18 -20
- package/skills/pikku-react/references/client.md +2 -37
- package/skills/pikku-report/SKILL.md +10 -10
- package/skills/pikku-scenario/SKILL.md +63 -66
- package/skills/pikku-scenario/references/browser.md +5 -5
- package/skills/pikku-scenario/references/coverage.md +4 -4
- package/skills/pikku-scenario/references/steps.md +31 -30
- package/skills/pikku-scenario/references/{persona-run.md → virtual-user-run.md} +38 -43
- package/skills/pikku-scenario/references/virtual-users.md +114 -0
- package/skills/pikku-seo/SKILL.md +1 -1
- package/skills/pikku-services/references/audit-wire-service.md +2 -2
- package/skills/pikku-services/references/audit.md +1 -2
- package/skills/pikku-services/references/services.md +1 -1
- package/skills/pikku-software-archaeology/SKILL.md +13 -13
- package/skills/pikku-software-archaeology/references/blueprint.schema.json +9 -9
- package/skills/pikku-theme/SKILL.md +1 -1
- package/skills/pikku-workflow/SKILL.md +1 -1
- package/skills/pikku-admin-to-fabric/SKILL.md +0 -198
- package/skills/pikku-blueprint-to-fabric/SKILL.md +0 -362
- package/skills/pikku-blueprint-to-fabric/scripts/inventory.mjs +0 -367
- package/skills/pikku-fabric/SKILL.md +0 -331
- package/skills/pikku-fabric/references/database.md +0 -128
- package/skills/pikku-fabric/references/debugging.md +0 -114
- package/skills/pikku-fabric/references/deploy.md +0 -179
- package/skills/pikku-scenario/references/personas.md +0 -123
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
- PHASE 1 — What the app is
|
|
10
10
|
- 2. Write the knowledge graph — before any code
|
|
11
11
|
- PHASE 2 — Who it is for, and what it is made of
|
|
12
|
-
- 3. Declare the people —
|
|
12
|
+
- 3. Declare the people — virtual users and roles
|
|
13
13
|
- 4. Declare the apps — one API, several frontends
|
|
14
14
|
- PHASE 3 — The plan
|
|
15
15
|
- 5. File the work as changes
|
|
@@ -19,23 +19,23 @@
|
|
|
19
19
|
- 6a. Close the changeset against its plan, not against your memory
|
|
20
20
|
- 7. Prove it — scenarios
|
|
21
21
|
- 8. Make it look like someone designed it
|
|
22
|
-
- 9. Ship it, and stay
|
|
22
|
+
- 9. Ship it, and stay deploy-ready
|
|
23
23
|
- Reference
|
|
24
24
|
|
|
25
25
|
You have a scaffolded project with skills installed. This skill owns everything
|
|
26
|
-
from here: no
|
|
27
|
-
project shaped so
|
|
26
|
+
from here: no hosted account, no card, no hosted build — while keeping the
|
|
27
|
+
project shaped so a hosting platform can adopt it later with zero rework.
|
|
28
28
|
|
|
29
29
|
**Four phases, and you do not skip ahead:**
|
|
30
30
|
|
|
31
31
|
1. Write the knowledge graph — what the app IS, before any code
|
|
32
|
-
2. Declare the people and the apps —
|
|
32
|
+
2. Declare the people and the apps — virtual users, roles, frontends
|
|
33
33
|
3. File the work as changes — grouped into changesets, in dependency order
|
|
34
34
|
4. Build them one changeset at a time — each proven by a scenario before it merges
|
|
35
35
|
|
|
36
36
|
The sections below follow those phases in order, and are meant to be worked
|
|
37
37
|
through rather than searched: §0 bootstrap, §1-§1a the last questions and the
|
|
38
|
-
three languages, §2 the knowledge graph, §3-§4
|
|
38
|
+
three languages, §2 the knowledge graph, §3-§4 virtual users and apps, §5-§5a the
|
|
39
39
|
changes and each changeset's technical plan, §6-§6a building and closing one,
|
|
40
40
|
§7-§7a proving it, §8 design, §9 ship. Four of them have their own reference —
|
|
41
41
|
scenarios, design, multi-app and ship, each listed in SKILL.md — to read at the
|
|
@@ -68,7 +68,7 @@ One command, run once, now — not later when you start building. It wires the
|
|
|
68
68
|
`#pikku` import alias the generated code depends on. On a fresh scaffold `.pikku/`
|
|
69
69
|
is empty, so **every command that touches codegen fails until this has run** —
|
|
70
70
|
including ones you would reasonably reach for while still planning, like
|
|
71
|
-
`pikku
|
|
71
|
+
`pikku virtual-user list`. Those failures look alarming (`Cannot find module
|
|
72
72
|
'#pikku/workflow/pikku-workflow-types.gen.js'`, `Schema generation failed for 16
|
|
73
73
|
schemas`) and they are nothing but this missing step.
|
|
74
74
|
|
|
@@ -81,7 +81,7 @@ Ask **three to five** questions that actually change the schema or the screens,
|
|
|
81
81
|
in one message. Then stop; do not interview the user.
|
|
82
82
|
|
|
83
83
|
- **Who uses it — who are the distinct kinds of people?** Ask this however small
|
|
84
|
-
the app is. The answer becomes §3's
|
|
84
|
+
the app is. The answer becomes §3's virtual users and roles, and you build only the
|
|
85
85
|
roles they name.
|
|
86
86
|
- **What are the two or three core objects?**
|
|
87
87
|
- **What is the main thing someone does on their first visit?** This answer
|
|
@@ -121,11 +121,11 @@ A brief saying "the entire UI is German" is about **one** of these. Getting this
|
|
|
121
121
|
wrong has already shipped a project that can never add a second language, so
|
|
122
122
|
settle all three explicitly before you write code.
|
|
123
123
|
|
|
124
|
-
| Axis | What it covers
|
|
125
|
-
| --------------- |
|
|
126
|
-
| **Identifiers** | Function, component, type, variable and file names. Database tables and columns. Commit messages.
|
|
127
|
-
| **Meta** | `description` on functions and steps, `name`/`title` on features and scenarios, step `template`, role and
|
|
128
|
-
| **Product UI** | Every string the app shows a user
|
|
124
|
+
| Axis | What it covers | Where it goes |
|
|
125
|
+
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
126
|
+
| **Identifiers** | Function, component, type, variable and file names. Database tables and columns. Commit messages. | Nowhere — **always English**, no setting, not negotiable |
|
|
127
|
+
| **Meta** | `description` on functions and steps, `name`/`title` on features and scenarios, step `template`, role and virtual user descriptions | `metaLocale` in `pikku.config.json`, default `en` |
|
|
128
|
+
| **Product UI** | Every string the app shows a user | `messages/<locale>.json`, and `defaultLocale` for what a first-time visitor opens in |
|
|
129
129
|
|
|
130
130
|
**Identifiers are English.** The product's market does not change this and
|
|
131
131
|
neither does `metaLocale`. Identifiers are the surface the generated `#pikku/*`
|
|
@@ -160,7 +160,7 @@ A German medical portal, correctly:
|
|
|
160
160
|
```jsonc
|
|
161
161
|
// project.inlang/settings.json
|
|
162
162
|
{ "baseLocale": "en", "locales": ["en", "de"] }
|
|
163
|
-
// apps/app/src/i18n/active.json
|
|
163
|
+
// apps/app/src/i18n/active.json
|
|
164
164
|
{ "defaultLocale": "de" }
|
|
165
165
|
// pikku.config.json
|
|
166
166
|
{ "metaLocale": "de" }
|
|
@@ -228,14 +228,14 @@ bunx --bun pikku knowledge validate
|
|
|
228
228
|
|
|
229
229
|
## PHASE 2 — Who it is for, and what it is made of
|
|
230
230
|
|
|
231
|
-
## 3. Declare the people —
|
|
231
|
+
## 3. Declare the people — virtual users and roles
|
|
232
232
|
|
|
233
233
|
The answer to "who uses it" becomes code, in one file:
|
|
234
|
-
`packages/functions/src/
|
|
234
|
+
`packages/functions/src/virtual-users.ts`. It ships with a single `visitor`; add the
|
|
235
235
|
people the user named, and the roles they imply.
|
|
236
236
|
|
|
237
237
|
```typescript
|
|
238
|
-
import {
|
|
238
|
+
import { defineVirtualUsers } from '#pikku/scopes/pikku-virtual-users.gen.js'
|
|
239
239
|
import { defineSystemRole } from '#pikku/scopes'
|
|
240
240
|
|
|
241
241
|
defineSystemRole({
|
|
@@ -251,7 +251,7 @@ defineSystemRole({
|
|
|
251
251
|
},
|
|
252
252
|
})
|
|
253
253
|
|
|
254
|
-
|
|
254
|
+
defineVirtualUsers({
|
|
255
255
|
visitor: {
|
|
256
256
|
name: 'Visitor',
|
|
257
257
|
jobTitle: 'Synthetic health-check user',
|
|
@@ -282,18 +282,18 @@ definePersonas({
|
|
|
282
282
|
})
|
|
283
283
|
```
|
|
284
284
|
|
|
285
|
-
- **Keep `visitor`.** The shipped scenarios name `
|
|
286
|
-
requires a browser step's
|
|
287
|
-
stops `
|
|
285
|
+
- **Keep `visitor`.** The shipped scenarios name `virtualUsers.visitor`, and PKU677
|
|
286
|
+
requires a browser step's virtual user to be a literal `virtualUsers.<name>`. Removing it
|
|
287
|
+
stops `virtualUsers.visitor` type-checking, which fails `pikku all`, and nothing you
|
|
288
288
|
write after that registers.
|
|
289
|
-
- **One `
|
|
290
|
-
`
|
|
289
|
+
- **One `defineVirtualUsers` call for the whole project.** Codegen builds the
|
|
290
|
+
`VirtualUserId` union from it, materialises one scenario virtual user per person, and
|
|
291
291
|
seeds a user row each. A second call site is a second answer to "who uses this
|
|
292
292
|
app".
|
|
293
293
|
- **`roles` is typechecked against `defineSystemRole`.** An undeclared role is a
|
|
294
294
|
build error rather than a runtime surprise.
|
|
295
|
-
- **Never write an email address.** Each is derived from the
|
|
296
|
-
`scenarios.emailDomain` in `pikku.config.json` — `visitor@
|
|
295
|
+
- **Never write an email address.** Each is derived from the virtual user id and
|
|
296
|
+
`scenarios.emailDomain` in `pikku.config.json` — `visitor@virtualuser.local`.
|
|
297
297
|
Hand-writing one is how a run signs in as somebody who was never created.
|
|
298
298
|
- **Declare a second person of the same kind** whenever the rule is ownership
|
|
299
299
|
(`bilal` above). "You see yours, not theirs" is not testable with one owner,
|
|
@@ -305,14 +305,14 @@ definePersonas({
|
|
|
305
305
|
in the function's `permissions` field (§6), never in the body. Read the
|
|
306
306
|
`pikku-auth` skill.
|
|
307
307
|
|
|
308
|
-
`pikku
|
|
308
|
+
`pikku virtual-user list` shows who is declared and `pikku roles audit` reports roles
|
|
309
309
|
the database still holds that code no longer declares — both need §0's bootstrap
|
|
310
310
|
to have run, and both are worth a look once it has.
|
|
311
311
|
|
|
312
312
|
**One warning about the scaffold's own notes:** `knowledge/index.md` may claim
|
|
313
|
-
the people live in `pikku.config.json`, put there by a
|
|
314
|
-
That is stale. In this template they live in `
|
|
315
|
-
`pikku.config.json` carries no
|
|
313
|
+
the people live in `pikku.config.json`, put there by a virtual user command.
|
|
314
|
+
That is stale. In this template they live in `virtual-users.ts` as above, and
|
|
315
|
+
`pikku.config.json` carries no virtual users key at all — only `scenarios.emailDomain`.
|
|
316
316
|
Trust the file you can read over the note describing it.
|
|
317
317
|
|
|
318
318
|
## 4. Declare the apps — one API, several frontends
|
|
@@ -374,7 +374,7 @@ What a change is:
|
|
|
374
374
|
- **One commit's worth.** "The owner sees this month's arrears" is a change,
|
|
375
375
|
or a few. "Add the database schema" is not; it is a step inside one.
|
|
376
376
|
- **It ends in something a person can do**, in a browser, signed in as a named
|
|
377
|
-
|
|
377
|
+
virtual user. If you cannot say what they see when it is done, that is a
|
|
378
378
|
`questions/` note, not a change.
|
|
379
379
|
|
|
380
380
|
If §1's screen mock was made — approved, or drawn because nobody answered —
|
|
@@ -454,7 +454,7 @@ while the plan is short.
|
|
|
454
454
|
configuration and belongs in a migration, not here.
|
|
455
455
|
Do this generously and do it now: an empty app demos badly and critiques
|
|
456
456
|
badly, and you cannot judge a screen's hierarchy, overflow, or truncation
|
|
457
|
-
against zero rows. Seed rows each
|
|
457
|
+
against zero rows. Seed rows each virtual user sees differently — with an ownership
|
|
458
458
|
rule that means seeding rows for the _second_ owner too.
|
|
459
459
|
3. **Functions.** One `pikkuFunc` per `*.function.ts`. Mark it `expose: true` and
|
|
460
460
|
Pikku generates the typed RPC client and the React Query hooks the UI calls;
|
|
@@ -537,28 +537,26 @@ Rules that are not optional:
|
|
|
537
537
|
tables. A domain table with one of those names — a class _session_, a drop-in
|
|
538
538
|
_session_ — collides in the migration. Name it for the domain instead
|
|
539
539
|
(`evening`, `class_meeting`) and keep the word in the UI copy.
|
|
540
|
-
- The template's `/` redirects to `/app`, so the login screen
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
public layout as well, or a reviewer lands on a site with no way in.
|
|
540
|
+
- The template's `/` redirects to `/app`, so the login screen is what a
|
|
541
|
+
signed-out visitor sees first. Replace `/` with a public homepage and a
|
|
542
|
+
reviewer lands on a site with no way in: link it to the login screen.
|
|
544
543
|
|
|
545
|
-
Before the first run, make sure `.env` at the project root holds the
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
544
|
+
Before the first run, make sure `.env` at the project root holds the secret
|
|
545
|
+
the local stack needs. `bun run dev` appends it if missing, but check anyway —
|
|
546
|
+
a project scaffolded from an older template, or a `.env` copied in from
|
|
547
|
+
elsewhere, can lack it, and the failure does not name the variable:
|
|
549
548
|
|
|
550
549
|
- `BETTER_AUTH_SECRET` — without it the first sign-up is a 500.
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
550
|
+
|
|
551
|
+
Virtual user sign-in needs nothing in `.env`: `pikku dev` creates its own key
|
|
552
|
+
pair and hands the app the public half.
|
|
554
553
|
|
|
555
554
|
```sh
|
|
556
555
|
grep -q '^BETTER_AUTH_SECRET=' .env 2>/dev/null || echo "BETTER_AUTH_SECRET=$(openssl rand -base64 32)" >> .env
|
|
557
|
-
grep -q '^SCENARIO_ACTOR_SECRET=' .env 2>/dev/null || echo "SCENARIO_ACTOR_SECRET=$(openssl rand -base64 32)" >> .env
|
|
558
556
|
```
|
|
559
557
|
|
|
560
558
|
`.env` is gitignored and local only — never commit it. A deployed stage gets its
|
|
561
|
-
secrets from the platform
|
|
559
|
+
secrets from the hosting platform, not from this file.
|
|
562
560
|
|
|
563
561
|
Then run it:
|
|
564
562
|
|
|
@@ -571,13 +569,10 @@ frontend running against a dead API looks exactly like an app bug, so if every
|
|
|
571
569
|
request fails, check that both halves came up.
|
|
572
570
|
|
|
573
571
|
**Start the stack through `bun run dev`, not by launching `vite` or `pikku dev`
|
|
574
|
-
yourself.** The
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
project
|
|
578
|
-
`http://localhost:3000`, so beside another project's server the switcher lists
|
|
579
|
-
_its_ personas, or none, which reads like a missing switcher rather than the
|
|
580
|
-
wrong server.
|
|
572
|
+
yourself.** The frontend needs to reach _your_ API. If you start the
|
|
573
|
+
frontend on its own (say :3000 is taken by another project), point
|
|
574
|
+
`VITE_API_PROXY` at your API: the dev proxy defaults to `http://localhost:3000`,
|
|
575
|
+
so beside another project's server every request goes to the wrong API.
|
|
581
576
|
|
|
582
577
|
The `--bun` in `bunx --bun pikku …` is load-bearing — keep it. Without it the
|
|
583
578
|
CLI's `#!/usr/bin/env node` shebang hands the process to whatever Node is on
|
|
@@ -661,8 +656,8 @@ only when there is nothing to hang a check on.
|
|
|
661
656
|
|
|
662
657
|
## 7. Prove it — scenarios
|
|
663
658
|
|
|
664
|
-
A scenario is a user journey run as one of your
|
|
665
|
-
transport, with that
|
|
659
|
+
A scenario is a user journey run as one of your virtual users, over the real
|
|
660
|
+
transport, with that virtual user's session. It is the only kind of test worth writing
|
|
666
661
|
here, because a passing one proves the app works the way a signed-in person
|
|
667
662
|
experiences it. Three ship in `packages/functions/test/scenarios/` — keep them
|
|
668
663
|
green — and every scenario a changeset's plan names becomes one more.
|
|
@@ -674,7 +669,7 @@ proves nothing:
|
|
|
674
669
|
|
|
675
670
|
- the shape of a scenario, and `do` vs `given`/`when`/`then`
|
|
676
671
|
- what survives DSL extraction — a `then` in `return` position asserts nothing,
|
|
677
|
-
and a shared setup helper credits its steps to the wrong
|
|
672
|
+
and a shared setup helper credits its steps to the wrong virtual user
|
|
678
673
|
- what to assert — refusals must read the REASON, totals must be deltas, and the
|
|
679
674
|
assertion nobody writes is the row count
|
|
680
675
|
- living without a state reset: the suite must be green on its SECOND run
|
|
@@ -688,7 +683,7 @@ bunx --bun pikku scenario run local --spawn # server-side, the
|
|
|
688
683
|
bunx --bun pikku scenario run local --spawn --run browser # the same journeys, driven as a human
|
|
689
684
|
```
|
|
690
685
|
|
|
691
|
-
In a multi-app project that one run covers both frontends: each
|
|
686
|
+
In a multi-app project that one run covers both frontends: each virtual user carries
|
|
692
687
|
its own `app` and `@pikku/playwright` resolves the base url from the
|
|
693
688
|
environment's `appUrls` map, so there is no second environment to run.
|
|
694
689
|
|
|
@@ -738,7 +733,7 @@ Three separate jobs, and conflating them is why open-source builds come out
|
|
|
738
733
|
looking like the template:
|
|
739
734
|
|
|
740
735
|
- **Direction** — deciding what it should look like. **No open-source tool does
|
|
741
|
-
this.**
|
|
736
|
+
this.** A hosting platform may offer one; you have §1's answer and 8a below.
|
|
742
737
|
- **Execution** — whether the screens actually realise that direction, or
|
|
743
738
|
default to whatever component was nearest. No theme does this, and it is where
|
|
744
739
|
"works but looks like nobody decided anything" comes from.
|
|
@@ -831,13 +826,13 @@ seven copies deep. Run `pikku db reset` and restart the dev server before you
|
|
|
831
826
|
screenshot anything — a screen judged against that data gets designed for a
|
|
832
827
|
problem it does not have.
|
|
833
828
|
|
|
834
|
-
## 9. Ship it, and stay
|
|
829
|
+
## 9. Ship it, and stay deploy-ready
|
|
835
830
|
|
|
836
831
|
When the queue is empty, `pikku knowledge gaps` lists nothing and the scenarios are green, read
|
|
837
832
|
the ship reference (see SKILL.md). It carries the open-source deploy paths (`--provider
|
|
838
833
|
standalone`, `cloudflare`, `aws`), how to serve several frontends behind one
|
|
839
|
-
API, the pre-release gate to run, and the contract that keeps
|
|
840
|
-
a
|
|
834
|
+
API, the pre-release gate to run, and the contract that keeps the project
|
|
835
|
+
importable by a hosting platform later rather than a migration.
|
|
841
836
|
|
|
842
837
|
The app is not handed over without its user guide. The scenarios you wrote are
|
|
843
838
|
already its skeleton: read **pikku-guide**, write one page per audience citing
|
|
@@ -864,8 +859,8 @@ says when to open each one:
|
|
|
864
859
|
- design — committing to a design direction, and how to tell whether the
|
|
865
860
|
screens realise it. Read BEFORE the first screen (§6), not at §8
|
|
866
861
|
- theming — authoring the theme (§8a)
|
|
867
|
-
- ship — deploying, and the
|
|
862
|
+
- ship — deploying, and the deploy-readiness contract (§9)
|
|
868
863
|
- Sibling skills: `pikku-knowledge` (§2), `pikku-auth` (§3),
|
|
869
|
-
`pikku-scenario` (§7, §7a),
|
|
864
|
+
`pikku-scenario` (§7, §7a), and `pikku-deploy` (§9)
|
|
870
865
|
- Project conventions written by the template: `AGENTS.md`
|
|
871
866
|
- Doing less than this: the quick reference. Doing more: the platform reference (both in SKILL.md).
|
|
@@ -28,7 +28,7 @@ nearest to hand, on every screen, in every app. Not because it lacks taste, but
|
|
|
28
28
|
because there is nothing to be wrong against. A direction fixes that: state one,
|
|
29
29
|
in words, before any screen exists.
|
|
30
30
|
|
|
31
|
-
Say what this product
|
|
31
|
+
Say what this product _is_ — its register, its reference points, what it feels
|
|
32
32
|
like to use, what it is deliberately not. "A warm, food-forward thing you use
|
|
33
33
|
standing at an open fridge door, closer to a recipe card than to a dashboard, and
|
|
34
34
|
never clinical" is a direction. "Clean and modern" is not — it rules nothing out,
|
|
@@ -46,13 +46,13 @@ chosen — it has been defaulted to in words instead of in components.
|
|
|
46
46
|
|
|
47
47
|
Being ambitious is easier against a list of specific things to not be. Generated
|
|
48
48
|
interfaces cluster hard, and these are the attractors — not because any is ugly,
|
|
49
|
-
but because arriving at one
|
|
49
|
+
but because arriving at one _by default_ means no choice was made:
|
|
50
50
|
|
|
51
51
|
- **Stock component-library defaults.** The strongest pull, and the hardest to see: a component
|
|
52
52
|
library's untouched defaults do not look broken, they look finished. Blue
|
|
53
53
|
accent, grey borders, one radius on everything, `Card` + stacked text
|
|
54
54
|
down every page. An app can be entirely this and never trip a critique, because
|
|
55
|
-
nothing on any screen is
|
|
55
|
+
nothing on any screen is _wrong_.
|
|
56
56
|
- Warm cream ground with a serif display face and a terracotta accent.
|
|
57
57
|
- Near-black with one acid-green or vermilion pop.
|
|
58
58
|
- A purple-to-blue gradient header on white.
|
|
@@ -98,7 +98,7 @@ material behind the content, what overlaps what — is plain HTML and your own
|
|
|
98
98
|
CSS, arranged however the layout decision demands; that half is free, and it is
|
|
99
99
|
where the design actually happens. The COMPONENTS — anything a person would
|
|
100
100
|
point at and call a control, and that the app will adopt as itself: buttons,
|
|
101
|
-
inputs, selects, tables, badges, menus, modals — are drawn as
|
|
101
|
+
inputs, selects, tables, badges, menus, modals — are drawn as _shadcn's_, at the
|
|
102
102
|
metrics shadcn actually uses: its control heights, its input shapes, its table
|
|
103
103
|
and menu behaviour. The test is the one the build will apply too: is this thing
|
|
104
104
|
the SHAPE OF THE PAGE, or a COMPONENT someone would point at?
|
|
@@ -138,8 +138,8 @@ stating:
|
|
|
138
138
|
mock was wrong. Update it, and say you did. Do not let the app and the mock
|
|
139
139
|
drift and then quietly prefer the app.
|
|
140
140
|
- The knowledge graph still owns the domain — objects, roles, rules. The mock
|
|
141
|
-
owns what the screens look like. When they disagree about a
|
|
142
|
-
wins; when they disagree about a
|
|
141
|
+
owns what the screens look like. When they disagree about a _fact_, knowledge
|
|
142
|
+
wins; when they disagree about a _layout_, the mock wins.
|
|
143
143
|
|
|
144
144
|
**Building it is then a transcription, not a translation.** Because the theme
|
|
145
145
|
already exists and the mock was drawn from it, the screen is shadcn components
|
|
@@ -204,9 +204,9 @@ the thing that made it is always yes. Use evidence.
|
|
|
204
204
|
Then drive it over CDP from a script: `Page.navigate`,
|
|
205
205
|
`Emulation.setDeviceMetricsOverride` for the two widths,
|
|
206
206
|
`Page.captureScreenshot`, write the PNG, and open it. Sign in the way a person
|
|
207
|
-
does —
|
|
208
|
-
|
|
209
|
-
|
|
207
|
+
does — through the login page — rather than reaching for the secret the
|
|
208
|
+
client uses; the UI path also proves the login screen works. Keep the script; you will run it at every changeset.
|
|
209
|
+
|
|
210
210
|
- **Run `impeccable`** (`npx impeccable install`, Node 22.18+) and feed it the
|
|
211
211
|
screenshots. It is external, it does not flatter, and it scores execution
|
|
212
212
|
against interaction heuristics. But it audits how well you executed the design
|
|
@@ -240,7 +240,7 @@ inventory, and it produces the wall of identical boxes further down this file.
|
|
|
240
240
|
list.** A product has objects of its own, and the ones that carry its meaning are
|
|
241
241
|
the ones no generic kit ships:
|
|
242
242
|
|
|
243
|
-
- the thing the product is
|
|
243
|
+
- the thing the product is _about_, rendered as itself — a funding meter, a
|
|
244
244
|
streak, a seat map, a run's status over time. If a decision note says the
|
|
245
245
|
progress toward a goal is the primary object, then a component that draws that
|
|
246
246
|
progress has to exist, or the note is describing an app you did not build.
|
|
@@ -255,7 +255,7 @@ Adding them is not hand-rolling, and they are the difference between an app that
|
|
|
255
255
|
uses a design system and an app that looks like one.
|
|
256
256
|
|
|
257
257
|
The tell that you skipped this: your `components/` directory maps one-to-one onto
|
|
258
|
-
your data model and contains nothing that names a
|
|
258
|
+
your data model and contains nothing that names a _quality_ of the product.
|
|
259
259
|
|
|
260
260
|
## Design is a gate on the changeset, not a phase at the end
|
|
261
261
|
|
|
@@ -281,7 +281,7 @@ repaint is a rewrite.
|
|
|
281
281
|
### The seed is part of the gate
|
|
282
282
|
|
|
283
283
|
A screenshot is only evidence if the screen has something on it. Before the gate
|
|
284
|
-
runs, the dev seed must populate what each screen
|
|
284
|
+
runs, the dev seed must populate what each screen _is for_ — not one row, and not
|
|
285
285
|
an empty state.
|
|
286
286
|
|
|
287
287
|
This is a real and quiet failure: a list app whose seed has no list, a countdown
|
|
@@ -292,7 +292,7 @@ designing and is not what the changeset is about.
|
|
|
292
292
|
Seed enough to be judged against: several rows, not three identical ones, and a
|
|
293
293
|
deliberate spread of the cases the screen has to hold — a long title that wraps,
|
|
294
294
|
a missing optional field, a picture and no picture, one item in each state the
|
|
295
|
-
screen can show. Anything derived from
|
|
295
|
+
screen can show. Anything derived from _today_ — a countdown, "3 days ago", an
|
|
296
296
|
expiry — is seeded as an interval from `now`, never as a fixed date: fixed dates
|
|
297
297
|
are correct on the afternoon they are written and meaningless a month later.
|
|
298
298
|
|
|
@@ -365,5 +365,5 @@ first one.
|
|
|
365
365
|
accent is not — the surest sign the neutrals were inherited rather than
|
|
366
366
|
chosen. The theming reference (see SKILL.md) covers it.
|
|
367
367
|
- It looks like the last app you built.
|
|
368
|
-
- It looks like stock shadcn. Not
|
|
368
|
+
- It looks like stock shadcn. Not _built with_ shadcn, which it is and should be —
|
|
369
369
|
but indistinguishable from a component gallery with the brand hue swapped in.
|
|
@@ -271,8 +271,8 @@ Do not push without explicit confirmation. Do not merge.
|
|
|
271
271
|
## Report what fought you
|
|
272
272
|
|
|
273
273
|
When pikku itself is what cost you time — a wrong generated type, a check that
|
|
274
|
-
passed when it should not, a skill that misled you — file it
|
|
275
|
-
|
|
274
|
+
passed when it should not, a skill that misled you — file it as the
|
|
275
|
+
`pikku-report` skill describes. That skill owns the ladder, the two kinds, the JSON-on-
|
|
276
276
|
stdin form and asking the user at hand-over; read it before filing.
|
|
277
277
|
|
|
278
278
|
Filing is permitted here (see **Hard constraints**), and sending what was filed
|
|
@@ -290,7 +290,7 @@ The skill's `allowed-tools` does **not** permit:
|
|
|
290
290
|
- `pikku deploy apply` (never deploy)
|
|
291
291
|
- secret writes
|
|
292
292
|
- network calls beyond what the implementation requires, except
|
|
293
|
-
`pikku
|
|
293
|
+
filing a finding as `pikku-report` describes, which is explicitly permitted
|
|
294
294
|
|
|
295
295
|
If the feature genuinely needs any of these, **stop and ask** with a clear
|
|
296
296
|
explanation of why and what would change.
|
|
@@ -8,6 +8,12 @@ Read this when a screen needs data no function returns yet, or returns in a diff
|
|
|
8
8
|
- Mocks are files, not code: `.mocks/<rpc.name>/<scenario>.json` is exactly what the RPC answers, and `<scenario>.meta.json` beside it holds `label`, `description`, `state` (healthy, empty, error or slow), `default`, `delayMs` and `status`. Exactly one scenario per RPC has `default: true`. Add an empty and an error scenario for every list and every mutation.
|
|
9
9
|
- `usePikkuQueryStub('rpc:name')` and `usePikkuMutationStub('rpc:name')` always answer from the mock, and their output type is inferred from the mock files. Use a stub only when the function's types are not enough: no function exists, or the shape the screen needs differs from the function's output. If the mock already fits the function, use `usePikkuQuery`.
|
|
10
10
|
|
|
11
|
+
The app loads its mocks with `registerPikkuMocks()` from the generated `mocks.gen.ts` beside `api.gen.ts`, called when `import.meta.env.DEV || import.meta.env.VITE_MOCK`. It covers the app's `.mocks/` and every wired addon's, so never glob `.mocks` or `node_modules` by hand.
|
|
12
|
+
|
|
13
|
+
## Addon mocks
|
|
14
|
+
|
|
15
|
+
An addon ships default mocks in its own package as `.mocks/<function>/<scenario>.json`, named without a namespace, and lists `.mocks` in `files`. An app that wires the addon as `studio` gets them as `studio:<function>`. The app's `.mocks/studio.<function>/` adds scenarios; one with the same name replaces the addon's, and a `default: true` in the app wins over the addon's default. Put mocks for an addon's functions in the addon, and only the app's own variations in the app.
|
|
16
|
+
|
|
11
17
|
With `VITE_MOCK` set the plain hooks answer from the default mock too, so every state of every screen can be looked at without a backend.
|
|
12
18
|
|
|
13
19
|
## Publishing
|
|
@@ -21,7 +21,7 @@ The split you are acting on should already be recorded, but this is the reasonin
|
|
|
21
21
|
behind it — and the place people get it wrong is the third case at the bottom.
|
|
22
22
|
|
|
23
23
|
**A group that comes in through its own front door gets its own app.** A role
|
|
24
|
-
|
|
24
|
+
_inside_ an app is not that: it changes which nav items and which buttons a person
|
|
25
25
|
sees, and lives in `useNavItems()` and the `permissions` on the function, not in a
|
|
26
26
|
route subtree.
|
|
27
27
|
|
|
@@ -31,7 +31,7 @@ across the counter with an account of their own gets their own — the customer,
|
|
|
31
31
|
tenant, the patient. One app is a real answer and often the right one.
|
|
32
32
|
|
|
33
33
|
**The asymmetry that forces a split is sign-up.** Where staff accounts are created
|
|
34
|
-
|
|
34
|
+
_for_ people and customers create their own, the two need different sign-up, different
|
|
35
35
|
onboarding and different session shape, and bending one app around both costs more
|
|
36
36
|
than the second app does. Do not collapse two audiences into one app to save a build.
|
|
37
37
|
|
|
@@ -41,7 +41,7 @@ Never invent a person the notes do not name in order to reach two.
|
|
|
41
41
|
|
|
42
42
|
Some people use the product with no account at all — ordering from a menu, booking a
|
|
43
43
|
table, opening an invitation. They are not a third case, and **they do not get their
|
|
44
|
-
own frontend**: an app is built around the
|
|
44
|
+
own frontend**: an app is built around the virtual users who sign into it. What they get is
|
|
45
45
|
the public route space every app already has.
|
|
46
46
|
|
|
47
47
|
- **`/app/*` is the signed-in application.** One `beforeLoad` on `/app` bounces a
|
|
@@ -67,19 +67,19 @@ and someone else's record: if the invoices nav item is hidden but `listAllInvoic
|
|
|
67
67
|
has no `permissions`, the app is wide open and the nav is decoration.
|
|
68
68
|
|
|
69
69
|
Worth a scenario each, because they are two different claims: that a mechanic cannot
|
|
70
|
-
|
|
70
|
+
_see_ the invoices nav item, and that their call to an invoices RPC is _refused_. The
|
|
71
71
|
second is the one that catches a `permissions` field nobody wired.
|
|
72
72
|
|
|
73
73
|
## The second app
|
|
74
74
|
|
|
75
75
|
```bash
|
|
76
|
-
pikku app new admin --serves staff --
|
|
76
|
+
pikku app new admin --serves staff --virtual-users manager,mechanic
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
One command does every step this section used to list by hand: it fetches
|
|
80
80
|
`pikkujs/starter-template`'s `apps/app`, re-points its `package.json` at the
|
|
81
81
|
new name, its own dev/preview port and its own `--tsBuildInfoFile`, stamps
|
|
82
|
-
`app: '<slug>'` onto each named
|
|
82
|
+
`app: '<slug>'` onto each named virtual user in `defineVirtualUsers({…})`, adds the
|
|
83
83
|
`frontends` entry to `pikku.config.json`, and re-runs `bun install`. `pikku app
|
|
84
84
|
list` shows every app afterwards.
|
|
85
85
|
|
|
@@ -108,12 +108,12 @@ second app nobody needed, so the command will not create one when:
|
|
|
108
108
|
- **An existing app already serves that audience.** People sharing an audience
|
|
109
109
|
share ONE app and differ by nav and permitted actions. A new app is for a
|
|
110
110
|
group the first app is not for.
|
|
111
|
-
- **A named
|
|
111
|
+
- **A named virtual user already signs into another app.** A person signs into one
|
|
112
112
|
app; move them out first if they really belong here.
|
|
113
113
|
- **The slug is what the plan calls an app that already exists.** The plan's
|
|
114
114
|
FIRST app is the one the project starts with — only the apps after it get
|
|
115
115
|
created.
|
|
116
|
-
- **A
|
|
116
|
+
- **A virtual user is not in `defineVirtualUsers({…})`.** The app is built around who
|
|
117
117
|
signs into it, so it is not created for people who do not exist yet.
|
|
118
118
|
|
|
119
119
|
It also repairs its own half-states: a run that died between writing the
|
|
@@ -133,9 +133,9 @@ Packaging it as a desktop or Android app is a separate step, on the same
|
|
|
133
133
|
|
|
134
134
|
### Scenarios across the two apps
|
|
135
135
|
|
|
136
|
-
`pikku app new` stamps `app: '<slug>'` onto each
|
|
137
|
-
field — not the `
|
|
138
|
-
`@pikku/playwright` resolves a
|
|
136
|
+
`pikku app new` stamps `app: '<slug>'` onto each virtual user it is given, and that
|
|
137
|
+
field — not the `virtualUsers` array on the `frontends` entry — is what
|
|
138
|
+
`@pikku/playwright` resolves a virtual user's base url from at sign-in. A virtual user
|
|
139
139
|
with no `app` lands on the fallback frontend, which is the other app's screens
|
|
140
140
|
with the right session on them.
|
|
141
141
|
|
|
@@ -152,15 +152,15 @@ single `appUrl` fallback, and the CLI takes `--app-url <app>=<url>,<app>=<url>`:
|
|
|
152
152
|
|
|
153
153
|
Three things follow, and each one has cost a run:
|
|
154
154
|
|
|
155
|
-
- **
|
|
156
|
-
at sign-in. A staff
|
|
157
|
-
something is absent from it — drive that assertion with a
|
|
155
|
+
- **A virtual user is bound to ONE app for the whole run.** The resolution happens once,
|
|
156
|
+
at sign-in. A staff virtual user cannot be walked through the customer app to check
|
|
157
|
+
something is absent from it — drive that assertion with a virtual user who lives
|
|
158
158
|
there, and say in the scenario's docblock why the witness is who it is. This
|
|
159
159
|
binds a SIGNED-OUT scenario too, and that is where it bites: 'a visitor with no
|
|
160
|
-
session cannot open the admin desk' driven by a customer-app
|
|
160
|
+
session cannot open the admin desk' driven by a customer-app virtual user points the
|
|
161
161
|
browser at a router with no such route at all, so what the assertion measures is
|
|
162
162
|
a 404, not the guard — and on an app with a catch-all redirect to login it
|
|
163
|
-
passes while proving nothing. Pick the
|
|
163
|
+
passes while proving nothing. Pick the virtual user by which app owns the ROUTE, not by
|
|
164
164
|
who should be refused, and drop their session instead.
|
|
165
165
|
- **Every frontend needs its API proxy pointed at the API you are actually
|
|
166
166
|
running.** `vite.config.ts` reads a variable (`VITE_API_PROXY`) with a default
|