@pikku/skills 0.12.49 → 0.12.50
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/dist/skills.gen.js +1 -1
- package/package.json +1 -1
- package/skills/pikku-addon/references/openapi.md +1 -1
- package/skills/pikku-architect/SKILL.md +64 -55
- package/skills/pikku-architect/references/plan-defects.md +14 -14
- package/skills/pikku-build/SKILL.md +17 -16
- package/skills/pikku-build/references/app.md +86 -122
- package/skills/pikku-build/references/design.md +13 -13
- package/skills/pikku-build/references/multi-app.md +1 -1
- package/skills/pikku-build/references/platform.md +7 -7
- package/skills/pikku-build/references/quick.md +5 -5
- package/skills/pikku-build/references/scenarios.md +19 -19
- package/skills/pikku-build/references/ship.md +8 -8
- package/skills/pikku-changes/SKILL.md +73 -27
- package/skills/pikku-knowledge/SKILL.md +41 -81
package/package.json
CHANGED
|
@@ -126,5 +126,5 @@ on their tracker is a public post.
|
|
|
126
126
|
## Then
|
|
127
127
|
|
|
128
128
|
Go back to the mode you were building in (`pikku-build`). The addon is a
|
|
129
|
-
dependency of the app, not the app: plan
|
|
129
|
+
dependency of the app, not the app: plan changes around what the user wants
|
|
130
130
|
to do with the API, and reach the operations through `ref()`.
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pikku-architect
|
|
3
3
|
description: >-
|
|
4
|
-
Use to turn one
|
|
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
|
|
7
|
-
`pikku knowledge plan progress` divides by, so it is written BEFORE any of the
|
|
8
|
-
exists and never edited afterwards to match what got built. TRIGGER when:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
edit to a working app.
|
|
6
|
+
written through `pikku knowledge plan set <changeset>`. The plan is the denominator
|
|
7
|
+
`pikku knowledge plan progress` divides by, so it is written BEFORE any of the changeset's code
|
|
8
|
+
exists and never edited afterwards to match what got built. TRIGGER when: `pikku changes
|
|
9
|
+
claim` says a changeset needs a plan, `changes done` or `pikku changes next` refuses one for having no
|
|
10
|
+
plan, or the user asks to plan or architect a changeset. DO NOT TRIGGER when: the knowledge notes
|
|
11
|
+
themselves are still being written (use pikku-knowledge), the plan already exists and the job is
|
|
12
|
+
to build it (use pikku-changes), or the changeset was claimed with no plan needed.
|
|
14
13
|
installGroups: [core]
|
|
15
14
|
agent:
|
|
16
15
|
tools: read, write, edit, bash, grep
|
|
@@ -21,45 +20,51 @@ agent:
|
|
|
21
20
|
verify:
|
|
22
21
|
- id: knowledge-consistent
|
|
23
22
|
command: pikku knowledge validate
|
|
24
|
-
- id: plan-accepted
|
|
25
|
-
command: pikku knowledge next --require dispatch,idle
|
|
26
23
|
|
|
27
24
|
---
|
|
28
25
|
|
|
29
|
-
# Plan one
|
|
26
|
+
# Plan one changeset
|
|
30
27
|
|
|
31
|
-
A
|
|
32
|
-
|
|
28
|
+
A changeset's changes say what the app must DO, in the words of the person who filed them, and the
|
|
29
|
+
knowledge notes say what the app is. Neither says how. This is where how gets decided, once, in writing, before any of
|
|
33
30
|
it is built.
|
|
34
31
|
|
|
35
32
|
**Why the plan comes first and stays fixed.** A builder who plans after seeing its own work can
|
|
36
33
|
build a fraction, plan only that fraction, and certify itself complete — `pikku knowledge plan
|
|
37
34
|
progress` then divides by a denominator chosen after the answer. The defence is the ORDER: the plan
|
|
38
|
-
is written against the
|
|
35
|
+
is written against the changes, in its own turn, before a single migration for it exists, and is never
|
|
39
36
|
edited afterwards. An item that will not land is deferred with its reason through `plan defer`,
|
|
40
37
|
not rewritten out of the plan. The same agent plans and then builds; nothing hands off.
|
|
41
38
|
|
|
42
|
-
**One
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
**One changeset, one plan, then build it.** Do not plan another changeset "while you are here" —
|
|
40
|
+
its changes are still allowed to move, and a plan written against work that later moves is worse
|
|
41
|
+
than no plan.
|
|
42
|
+
|
|
43
|
+
**When a changeset needs one.** `changes claim` decides: a changeset that creates or alters a table,
|
|
44
|
+
or one with many changes, is planned; anything else is put to the configured judge, and a judge that
|
|
45
|
+
fails says plan. The claim prints which, and why. `changes done` then refuses the first change of a
|
|
46
|
+
planned changeset until its plan reads, and the last until the plan's first pass exists; `pikku changes next`
|
|
47
|
+
will not merge it without the plan on its branch.
|
|
45
48
|
|
|
46
49
|
---
|
|
47
50
|
|
|
48
51
|
## Write nothing by hand
|
|
49
52
|
|
|
50
|
-
The plan reaches disk through `pikku knowledge plan set <
|
|
53
|
+
The plan reaches disk through `pikku knowledge plan set <changeset> <file>` and nowhere else —
|
|
54
|
+
`knowledge/plans/<changeset>.plan.json`, where `<changeset>` is the group id the claim printed.
|
|
55
|
+
Commit it on the changeset's branch, before the first change, so it merges with the code it
|
|
56
|
+
describes. It
|
|
51
57
|
validates first and names the field that is wrong if it refuses; a plan file written with an editor
|
|
52
58
|
is a plan nothing checked, and the place that discovers that is a finished build.
|
|
53
59
|
|
|
54
|
-
It is also written ONCE. `plan set` refuses a
|
|
60
|
+
It is also written ONCE. `plan set` refuses a changeset that already has a plan: the plan is the
|
|
55
61
|
order the build is measured against, and an order that can be rewritten measures nothing. The one
|
|
56
|
-
way down from a written plan is `pikku knowledge plan defer <
|
|
62
|
+
way down from a written plan is `pikku knowledge plan defer <changeset> <item> --reason <why>`,
|
|
57
63
|
which records what was left out and why.
|
|
58
64
|
|
|
59
65
|
It is JSON rather than a note on purpose. Everything else under `knowledge/` is prose a human
|
|
60
66
|
reads; this one is consumed field-by-field, and a markdown parser is one more place a misspelt
|
|
61
|
-
heading silently passes.
|
|
62
|
-
its status leaves `proposed`, so rewriting it would change what the builder was told.
|
|
67
|
+
heading silently passes.
|
|
63
68
|
|
|
64
69
|
## Send it. Do not go looking.
|
|
65
70
|
|
|
@@ -87,38 +92,41 @@ pikku meta context --json # what the app already declares
|
|
|
87
92
|
pikku knowledge plan schema # the only spec for what you are about to write
|
|
88
93
|
```
|
|
89
94
|
|
|
90
|
-
Then read
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
Then read the changeset's changes in full (`pikku changes show <n>` for each), every
|
|
96
|
+
knowledge note they touch — the entity notes, and the note a change's `Knowledge:` line names — the
|
|
97
|
+
decisions that constrain them, and the migrations already in `db/sqlite/`: those say whether your
|
|
98
|
+
tables are new or an alter.
|
|
93
99
|
|
|
94
|
-
**Do not re-interview.** If
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
**Do not re-interview.** If a change leaves something that alters the schema or a screen genuinely
|
|
101
|
+
undecided, `pikku changes ask` on it and plan the rest; otherwise plan the reading that
|
|
102
|
+
builds LESS. A smaller changeset that ships is worth more than a complete one that does not, and
|
|
103
|
+
what you leave out is named in `covers` for the next changeset to pick up.
|
|
97
104
|
|
|
98
105
|
### 2. Decide the passes
|
|
99
106
|
|
|
100
|
-
A pass is a slice of the
|
|
107
|
+
A pass is a slice of the changeset that stands up on its own. **Pass 1 is a walking skeleton**: it
|
|
101
108
|
reaches a real screen, with real functions behind it, proved by a real browser scenario. Everything
|
|
102
109
|
else waits behind it.
|
|
103
110
|
|
|
104
111
|
This is enforced, not advisory — `plan set` refuses a plan whose pass 1 has no `ui` item, no
|
|
105
112
|
`functions` item, or a pass-1 route with nothing proving it works. The reason is the failure it was
|
|
106
|
-
written against: a
|
|
113
|
+
written against: a changeset that built four unwired functions and no page, and reported itself
|
|
107
114
|
finished. A build that runs out of time in pass 2 has shipped something; one that runs out of time
|
|
108
115
|
having built pass 1 across four half-finished layers has shipped nothing.
|
|
109
116
|
|
|
110
117
|
**Only pass 1 blocks.** `pikku knowledge plan progress` reports a later pass under `deferred` and
|
|
111
118
|
never refuses on it. That is what stops plan size from being fatal — but it is not licence to plan
|
|
112
|
-
a
|
|
119
|
+
a changeset nobody could finish. The question that decides a plan's size is not "what does this
|
|
113
120
|
note imply" but **"could a build finish all of this if pass 1 took twice as long as I expect"** — if
|
|
114
|
-
not, it is two
|
|
121
|
+
not, it is two changesets. Plan the first, and say in `covers` what you left behind.
|
|
115
122
|
|
|
116
|
-
**A screen is what pass 1 reaches only when the
|
|
117
|
-
|
|
123
|
+
**A screen is what pass 1 reaches only when the changeset reaches people through an app.** The
|
|
124
|
+
plan's `surface` says which — `app` by default, and `cli`, `mcp`, `agent` and `backend` are the
|
|
125
|
+
others. On those,
|
|
118
126
|
`ui` is legitimately `n/a` (with its reason, like any slot), and pass 1 proves itself one level
|
|
119
127
|
down: a pass-1 function that is actually wired, and a `scenarios.backend` item carrying that
|
|
120
|
-
function's name in its `fn` field. The obligation never lifts, it only moves —
|
|
121
|
-
|
|
128
|
+
function's name in its `fn` field. The obligation never lifts, it only moves — decide the surface
|
|
129
|
+
before you decide the passes.
|
|
122
130
|
|
|
123
131
|
### 3. Say what each slot is, or say why it is nothing
|
|
124
132
|
|
|
@@ -133,7 +141,7 @@ app is the same kind of person" and "nobody thought about roles" must not look a
|
|
|
133
141
|
### 4. Write it
|
|
134
142
|
|
|
135
143
|
```sh
|
|
136
|
-
pikku knowledge plan set <
|
|
144
|
+
pikku knowledge plan set <changeset> /tmp/plan.json
|
|
137
145
|
```
|
|
138
146
|
|
|
139
147
|
Write the JSON to a file first — the command takes a path, not inline JSON, which is what keeps an
|
|
@@ -144,7 +152,7 @@ not read.
|
|
|
144
152
|
Then confirm what the builder will be handed:
|
|
145
153
|
|
|
146
154
|
```sh
|
|
147
|
-
pikku knowledge plan show <
|
|
155
|
+
pikku knowledge plan show <changeset> --for-build
|
|
148
156
|
```
|
|
149
157
|
|
|
150
158
|
---
|
|
@@ -156,19 +164,19 @@ inventories every function, wire, scope, role, workflow, agent and scenario. Not
|
|
|
156
164
|
duplicates that — only what codegen cannot infer: **why a thing exists, which pass it belongs to,
|
|
157
165
|
and which knowledge note it discharges.**
|
|
158
166
|
|
|
159
|
-
### `covers` — which notes this
|
|
167
|
+
### `covers` — which notes this changeset discharges
|
|
160
168
|
|
|
161
169
|
Every plan claims at least one knowledge note: `note` (its path under `knowledge/`), `hash` (what
|
|
162
170
|
that note's body hashes to right now) and `complete`.
|
|
163
171
|
|
|
164
|
-
`complete: false` is the honest answer for a note whose claims span several
|
|
165
|
-
whole of a note only when this
|
|
172
|
+
`complete: false` is the honest answer for a note whose claims span several changesets — claim the
|
|
173
|
+
whole of a note only when this changeset genuinely leaves nothing of it unbuilt, because a note
|
|
166
174
|
marked complete is a note nobody looks at again.
|
|
167
175
|
|
|
168
176
|
**You do not have to compute the hash.** Write anything twelve characters long and send the plan:
|
|
169
177
|
`plan set` refuses a hash that is not the note's current one and names the correct one, so one
|
|
170
178
|
round trip gets you every hash in the plan. That refusal is the point of the field — a hash that
|
|
171
|
-
was never right makes the note read as edited-since from the moment the
|
|
179
|
+
was never right makes the note read as edited-since from the moment the changeset ships, and it
|
|
172
180
|
drops back into a backlog nobody planned.
|
|
173
181
|
|
|
174
182
|
### `model` — tables, and what their columns HOLD
|
|
@@ -197,7 +205,7 @@ parallel lists are two lists that drift.
|
|
|
197
205
|
and the client calls it by name, so for nearly every function there is nothing to decide — leave the
|
|
198
206
|
field out. A `wire` is for the exceptions: its own HTTP path via `wireHTTP` (a webhook, a payment
|
|
199
207
|
callback, a public URL another system posts to), a queue job, a channel, a scheduled task, or a
|
|
200
|
-
workflow entry point. Those last two are not alternate URLs — they are what the
|
|
208
|
+
workflow entry point. Those last two are not alternate URLs — they are what the changeset IS, and a
|
|
201
209
|
plan that omits them ships a `status` column nothing advances or a job nobody runs.
|
|
202
210
|
|
|
203
211
|
A wire is also a constraint on the function's SHAPE, not only an address for it. `wireScheduler`
|
|
@@ -221,7 +229,7 @@ permission scenario naming it in `fn`** — a rule with no failing case is a cla
|
|
|
221
229
|
A scope depends ONLY on the session: "may this kind of user do this at all" — `admin:invoices:void`,
|
|
222
230
|
`billing`. It is declared with `wireScope` and granted in `mapSession`, so every name here has to
|
|
223
231
|
end up in pikku's generated scope meta. One that cannot be declared is one the build can never
|
|
224
|
-
finish, and `plan progress` refuses the
|
|
232
|
+
finish, and `plan progress` refuses the changeset for as long as it stands.
|
|
225
233
|
|
|
226
234
|
Ownership is not a scope. "Only the owner of the house may read it" depends on the row being asked
|
|
227
235
|
for, and a scope never sees the row — that is the function's `permission` sentence and lives nowhere
|
|
@@ -240,7 +248,7 @@ reach two, and never give a slug to someone who never signs in: a guest checking
|
|
|
240
248
|
slug as the seller they buy from, on that app's public routes outside `/app`. Once there is more
|
|
241
249
|
than one app, every `ui` item carries its `app` too.
|
|
242
250
|
|
|
243
|
-
Adding the second frontend is the BUILD's job, at the
|
|
251
|
+
Adding the second frontend is the BUILD's job, at the changeset that first needs it —
|
|
244
252
|
pikku-build's multi-app reference. Your part is recording which app each person is in.
|
|
245
253
|
|
|
246
254
|
### `ui` — routes, and what is on them
|
|
@@ -254,7 +262,7 @@ counts — an unlinked browser scenario reads as a route nobody proved, and the
|
|
|
254
262
|
|
|
255
263
|
`backend`, `browser`, `permission`, each its own slot. Keyed rather than tagged so that a plan with
|
|
256
264
|
four backend scenarios and no browser scenario fails on its SHAPE — a flat list lets that through,
|
|
257
|
-
and that is exactly the
|
|
265
|
+
and that is exactly the changeset that builds an API and ships no screen.
|
|
258
266
|
|
|
259
267
|
**Every scenario needs `name`: the `pikkuScenario` export it becomes** (`saveEntryScenario`).
|
|
260
268
|
`feature` and `scenario` are prose for a reader, and prose cannot be matched against codegen.
|
|
@@ -262,7 +270,7 @@ and that is exactly the milestone that builds an API and ships no screen.
|
|
|
262
270
|
cannot see.
|
|
263
271
|
|
|
264
272
|
Permission scenarios default to pass 2 — they harden a journey that has to exist before they can
|
|
265
|
-
cover it — so a role × resource cross product there costs the
|
|
273
|
+
cover it — so a role × resource cross product there costs the changeset nothing.
|
|
266
274
|
|
|
267
275
|
---
|
|
268
276
|
|
|
@@ -270,10 +278,10 @@ cover it — so a role × resource cross product there costs the milestone nothi
|
|
|
270
278
|
|
|
271
279
|
`plan set` catches the mechanical failures — a missing slot, a bad hash, a pass 1 with no `ui` item.
|
|
272
280
|
Read your draft back against these six questions, which it cannot ask. Each has cost a real
|
|
273
|
-
|
|
281
|
+
changeset; **[references/plan-defects.md](references/plan-defects.md)** carries the case behind every
|
|
274
282
|
one, and is worth opening for any question you cannot answer with a flat yes.
|
|
275
283
|
|
|
276
|
-
1. **Is this a plan for
|
|
284
|
+
1. **Is this a plan for THESE changes?** Every entity the changes and their notes name appears in a function or a table.
|
|
277
285
|
2. **Does pass 1 slice, and does the model fit inside it?** Not "pass 1: the data model, pass 2: the
|
|
278
286
|
API" — and `model` holds only the tables pass 1 or 2 actually migrates, because the model slot has
|
|
279
287
|
no passes and a later table is a PROBLEM from the first day.
|
|
@@ -283,7 +291,7 @@ one, and is worth opening for any question you cannot answer with a flat yes.
|
|
|
283
291
|
4. **Does something produce every state and field the plan reads?** For each clause of a description,
|
|
284
292
|
each screen the opening paragraph names, each field you filter or badge on, and each state a
|
|
285
293
|
scenario waits in — name the function that gets the world there. A producer you are reusing is
|
|
286
|
-
checked against code that already exists; a producer this
|
|
294
|
+
checked against code that already exists; a producer this changeset is adding is checked against
|
|
287
295
|
the plan that adds it. What is never allowed is a state with no named producer at all.
|
|
288
296
|
5. **Can two sentences in the plan both be true?** Write a state machine out once as a table in
|
|
289
297
|
`model`, name who sets and reads every clock in it, and say whether saving a child collection
|
|
@@ -295,7 +303,8 @@ one, and is worth opening for any question you cannot answer with a flat yes.
|
|
|
295
303
|
|
|
296
304
|
## When you are done
|
|
297
305
|
|
|
298
|
-
The accepted `plan set` is the end of planning.
|
|
299
|
-
read the plan with `plan show --for-build`, build it
|
|
300
|
-
|
|
301
|
-
|
|
306
|
+
The accepted `plan set` is the end of planning. Commit the plan file on the changeset's branch, then
|
|
307
|
+
go straight on to the build in `pikku-changes`: read the plan with `plan show --for-build`, build it
|
|
308
|
+
one commit per change, and mark the last change done only when `pikku knowledge plan progress` is
|
|
309
|
+
clean — `changes done` checks the same thing and refuses until it is. What you wrote is what you are
|
|
310
|
+
measured against, so do not touch it once the first migration is open.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# What makes a plan wrong
|
|
2
2
|
|
|
3
3
|
`plan set` catches the mechanical failures — a missing slot, a bad hash, a pass 1 with no `ui`
|
|
4
|
-
item. These are the ones it cannot, each carried back from a
|
|
4
|
+
item. These are the ones it cannot, each carried back from a changeset that shipped it. They are
|
|
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
|
|
|
@@ -27,11 +27,11 @@ questions and only open the group that worries you.
|
|
|
27
27
|
screens" is three passes of nothing working.
|
|
28
28
|
|
|
29
29
|
**Put in `model` only the tables pass 1 — or at worst pass 2 — actually migrates.** The model
|
|
30
|
-
slot has no passes: every item is checked from the moment the
|
|
30
|
+
slot has no passes: every item is checked from the moment the changeset starts, so a table whose
|
|
31
31
|
migration belongs to pass 3 is a PROBLEM from pass 1, and `plan progress` refuses on a problem
|
|
32
|
-
rather than deferring it. A
|
|
32
|
+
rather than deferring it. A changeset that finishes pass 1 green then cannot be closed, and every
|
|
33
33
|
fix is outside the build's hands. When a later pass needs its own tables, that is the signal it is
|
|
34
|
-
a second
|
|
34
|
+
a second changeset — say so in `covers`.
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
@@ -46,7 +46,7 @@ scenario cannot perform. Signed-out is a BROWSER fact.
|
|
|
46
46
|
`saveEntry` returns 200 proves the wire. The one worth planning is the one where a person writes
|
|
47
47
|
something, comes back, and it is still there. A browser scenario that opens a page and asserts it
|
|
48
48
|
is still on it proves the route loads and nothing else — `plan progress` names it a problem and
|
|
49
|
-
refuses the
|
|
49
|
+
refuses the changeset.
|
|
50
50
|
|
|
51
51
|
**Write summary and dashboard scenarios as deltas.** The suite runs against a live database nobody
|
|
52
52
|
resets, so "the payment-failed count is zero, then one" is a claim about every run that came
|
|
@@ -63,7 +63,7 @@ same way you read it against the functions.
|
|
|
63
63
|
**Write every refusal as "X, which is `<state>`, is refused because `<rule>`" — then check that
|
|
64
64
|
state against the rule you wrote in the same plan.** A refusal claims two things at once: that the
|
|
65
65
|
rule shuts, and that the described situation reaches the shut state. One plan asked for "a paused
|
|
66
|
-
licence is refused the academy" in the same
|
|
66
|
+
licence is refused the academy" in the same changeset whose gate admitted `active_until_expired`,
|
|
67
67
|
which is exactly what pausing inside the paid period produces. The scenario could only ever have
|
|
68
68
|
asserted a bug.
|
|
69
69
|
|
|
@@ -77,7 +77,7 @@ decision that nothing records.
|
|
|
77
77
|
**Every clause of a `feature` or `scenario` description has to be performed by a function** — one
|
|
78
78
|
in this plan, or one already in the meta. Write "when the distributor is removed its companies
|
|
79
79
|
fall back to the direct catalog" with no function that removes a distributor and `plan set`
|
|
80
|
-
accepts it, `plan progress` passes, and the
|
|
80
|
+
accepts it, `plan progress` passes, and the changeset ships a sentence nothing proves. Read each
|
|
81
81
|
description back asking *which function does this*, and cut the half you cannot name.
|
|
82
82
|
|
|
83
83
|
**Where a scenario calls the same function twice, check it for a per-period guard first.** A
|
|
@@ -86,9 +86,9 @@ cannot be performed in a single run however correct the code is. One planned "sh
|
|
|
86
86
|
second lesson and is nudged about the third" against a function that refuses a second completion
|
|
87
87
|
the same calendar day.
|
|
88
88
|
|
|
89
|
-
**Every screen the
|
|
89
|
+
**Every screen the changeset's changes name is either a `ui` item or a sentence to
|
|
90
90
|
cut.** This trap is easier to miss in the summary prose than in the scenarios: a screen named
|
|
91
|
-
there and carried by no `ui` item is invisible to `plan progress`, so the
|
|
91
|
+
there and carried by no `ui` item is invisible to `plan progress`, so the changeset closes green
|
|
92
92
|
with a sentence of itself unbuilt. One promised "the interval control on the admin product form"
|
|
93
93
|
for a form that does not exist anywhere in the app.
|
|
94
94
|
|
|
@@ -99,7 +99,7 @@ mark a product's countries, cannot be proven without amending the plan mid-build
|
|
|
99
99
|
**Every state a scenario waits in needs the function that leaves the world like that — checked
|
|
100
100
|
against the code ALREADY built.** Three failure shapes, in rising order of cost:
|
|
101
101
|
|
|
102
|
-
- *Stale*: a staff queue of "paid orders with no invoice yet", where an earlier
|
|
102
|
+
- *Stale*: a staff queue of "paid orders with no invoice yet", where an earlier changeset had
|
|
103
103
|
moved invoicing to checkout. No order a customer could place was ever in that state; three
|
|
104
104
|
scenarios passed once against old rows, then failed.
|
|
105
105
|
- *Unreachable*: staff asked to "capture the payment on an authorised order" when no function in
|
|
@@ -108,13 +108,13 @@ against the code ALREADY built.** Three failure shapes, in rising order of cost:
|
|
|
108
108
|
- *Wrong person*: a distributor salon owner planned to buy from a catalogue that is scoped by
|
|
109
109
|
distributor and does not show her the product. Actors are not interchangeable.
|
|
110
110
|
|
|
111
|
-
**A `model` slot saying "this
|
|
111
|
+
**A `model` slot saying "this changeset adds no table" has to be true of the DATA the scenarios
|
|
112
112
|
read, not only of the entities they name.** One promised a checkout priced by delivery country — a
|
|
113
113
|
shipping rate per country, a VAT rate per country — against an `n/a` model, while the only shipping
|
|
114
114
|
table in the tree (an addon's) carries no country column at all. The builder then chooses between
|
|
115
115
|
altering someone else's table and amending the plan, with neither choice recorded. Wherever a
|
|
116
116
|
description prices, rates or tiers something BY a dimension, say in `model` where that lookup lives:
|
|
117
|
-
a table this
|
|
117
|
+
a table this changeset adds, a column on one that exists, or config-as-code — and if it is config,
|
|
118
118
|
say so and why, exactly as for seed data.
|
|
119
119
|
|
|
120
120
|
---
|
|
@@ -125,7 +125,7 @@ A plan is read one field at a time, so a contradiction between two descriptions
|
|
|
125
125
|
check `plan set` makes and is discovered mid-build, with the code already written one of the two
|
|
126
126
|
ways.
|
|
127
127
|
|
|
128
|
-
**Where the
|
|
128
|
+
**Where the changeset has a state machine — anything with more than two states and a clock — write
|
|
129
129
|
the transitions out once, in the model slot, as the table they are.** Every scenario description
|
|
130
130
|
then quotes that table instead of restating it from memory. One plan said a cancel on a paid-up
|
|
131
131
|
licence lands in `canceled` in one scenario and `active_until_expired` in the next.
|
|
@@ -142,7 +142,7 @@ you look next: either the same reasoning applies to them, or the plan says why i
|
|
|
142
142
|
|
|
143
143
|
**Wherever a function writes a set under a parent, say whether a save REPLACES that set or ADDS to
|
|
144
144
|
it.** A product's variants, an order's lines, a company's members: the two produce identical tables
|
|
145
|
-
and identical scenarios, and differ only on the second save. Left unsaid, one
|
|
145
|
+
and identical scenarios, and differ only on the second save. Left unsaid, one changeset shipped an
|
|
146
146
|
input schema that could not carry a variant's id, so every save re-added the variants it was given
|
|
147
147
|
to update — 1, 2, 4, 8, and by the nineteenth save 262,144 rows, with the suite green throughout.
|
|
148
148
|
Say it in the model slot in the same breath as `onDelete`: that field settles what happens when the
|
|
@@ -4,7 +4,7 @@ description: >-
|
|
|
4
4
|
Use to build on Pikku — turning a fresh scaffold into a working app (quick spike, real product,
|
|
5
5
|
or a showcase that exercises every surface), adding a feature to an app that already exists, and
|
|
6
6
|
the one-off cleanup right after a template is cloned. Covers the knowledge base, personas and
|
|
7
|
-
roles,
|
|
7
|
+
roles, filing the work as changes and planning each changeset, the scenario that proves each one, theming, multi-app layouts and
|
|
8
8
|
deploying. TRIGGER when: the user asks for an app to be built on Pikku, a freshly scaffolded
|
|
9
9
|
project needs turning into a product, the user asks to add a feature or wire up a new endpoint
|
|
10
10
|
in a working app, or a template was just cloned or scaffolded. DO NOT TRIGGER when: the user
|
|
@@ -36,7 +36,7 @@ agent:
|
|
|
36
36
|
| A real product, meant to be picked up by someone else | `references/app.md` — the default |
|
|
37
37
|
| A spike, a throwaway demo, an idea nobody has committed to | `references/quick.md` |
|
|
38
38
|
| A showcase meant to exercise every Pikku surface | `references/platform.md`, which is a delta on top of `references/app.md` |
|
|
39
|
-
| A feature added to an app that already has its knowledge base
|
|
39
|
+
| A feature added to an app that already has its knowledge base | `references/feature.md` |
|
|
40
40
|
|
|
41
41
|
**App is the default.** A small or toy-sounding app does not make it Quick;
|
|
42
42
|
only an explicit signal of speed or throwaway-ness does. Platform is not "App
|
|
@@ -79,14 +79,14 @@ Say it at once, in one line, and start: this is the obvious first move, not a
|
|
|
79
79
|
question for the user. Generate the whole spec, however large.
|
|
80
80
|
|
|
81
81
|
Neither is the app. When the conversion compiles, come back here and carry on
|
|
82
|
-
in the mode the request calls for — App by default — planning
|
|
82
|
+
in the mode the request calls for — App by default — planning changesets around
|
|
83
83
|
what the user wants to do with the API or the workflow, and reaching the
|
|
84
84
|
generated functions through `ref()`.
|
|
85
85
|
|
|
86
86
|
## What holds in every mode
|
|
87
87
|
|
|
88
88
|
- **The branch and the diff are the contract.** A reviewer sees real, compiled,
|
|
89
|
-
working code: apply is a merge, reject is a `git branch -D`. The
|
|
89
|
+
working code: apply is a merge, reject is a `git branch -D`. The changeset's
|
|
90
90
|
plan is your own denominator, measured by `pikku knowledge plan progress` —
|
|
91
91
|
never something a reviewer is handed instead of the code.
|
|
92
92
|
- **Discover before editing, and there are two questions, not one.** What THIS
|
|
@@ -102,7 +102,7 @@ generated functions through `ref()`.
|
|
|
102
102
|
- **`capabilities.<type>` reports what this app USES, not what pikku offers.**
|
|
103
103
|
A `false` there is "no wire of this type is declared yet", so the rule below
|
|
104
104
|
about not introducing one is about not widening an app's surface on a whim —
|
|
105
|
-
it is not a statement that the surface is unavailable. When a
|
|
105
|
+
it is not a statement that the surface is unavailable. When a changeset's plan
|
|
106
106
|
calls for a scheduled task and `capabilities.scheduler` is `false`, check
|
|
107
107
|
`pikku doc` for the door before concluding you cannot build it.
|
|
108
108
|
- **`metaLocale` in `pikku.config.json` is the language of authored meta** —
|
|
@@ -116,7 +116,7 @@ generated functions through `ref()`.
|
|
|
116
116
|
because codegen refuses before it gets there. Delete those contracts' entries
|
|
117
117
|
from `versions.pikku.json` and re-run; they are re-recorded. Fix the real
|
|
118
118
|
diagnostic first, or you will chase the echo instead. Deleting is right only
|
|
119
|
-
for a contract first recorded INSIDE the
|
|
119
|
+
for a contract first recorded INSIDE the changeset you are building — nothing
|
|
120
120
|
has consumed it, so there is no version to keep. A contract that shipped and
|
|
121
121
|
then genuinely changed shape gets `version: N+1` on its `pikkuFunc({...})`
|
|
122
122
|
followed by `pikku versions update`; delete its entry and you erase a version
|
|
@@ -130,10 +130,11 @@ generated functions through `ref()`.
|
|
|
130
130
|
`addressLine1`, so a value saved through the query builder comes back missing
|
|
131
131
|
with no error anywhere. Follow the columns already in `db/sqlite/`
|
|
132
132
|
(`address_line1`), and check a new one round-trips before building on it.
|
|
133
|
-
- **A
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
133
|
+
- **A changeset that needs a plan is planned before it is built, and the plan
|
|
134
|
+
then stays fixed.** `changes claim` says whether it needs one. The plan —
|
|
135
|
+
tables, functions, wires, roles, scopes, screens, scenarios, in passes — is
|
|
136
|
+
written through `pikku knowledge plan set <changeset>` (how: `pikku-architect`)
|
|
137
|
+
in its own turn before any of that changeset's code exists, and
|
|
137
138
|
`pikku knowledge plan progress` measures the build against it from the
|
|
138
139
|
generated meta. You plan it and you build it; what you never do is edit the
|
|
139
140
|
plan afterwards to match what you built — that is grading yourself.
|
|
@@ -205,17 +206,17 @@ listed here.
|
|
|
205
206
|
|
|
206
207
|
## What NOT to do
|
|
207
208
|
|
|
208
|
-
- **Do not skip ahead in App mode.** Knowledge, then people, then
|
|
209
|
-
then one
|
|
210
|
-
closed against its plan before
|
|
211
|
-
- **Do not close a
|
|
209
|
+
- **Do not skip ahead in App mode.** Knowledge, then people, then changes,
|
|
210
|
+
then one changeset at a time — planned, built, proven by a scenario, and
|
|
211
|
+
closed against its plan before it merges. The order is the method.
|
|
212
|
+
- **Do not close a changeset your plan says is unfinished.** Build the missing
|
|
212
213
|
item, or defer it with a reason through `pikku knowledge plan defer`. Never
|
|
213
214
|
edit the plan to match what you built, and never drop an item silently.
|
|
214
215
|
- **Do not let a Quick build be mistaken for a real one.** It skips
|
|
215
|
-
`knowledge/`,
|
|
216
|
+
`knowledge/`, changesets and plans, design direction and refusal scenarios — say
|
|
216
217
|
so out loud to the user when you finish, and point at the way out.
|
|
217
218
|
- **Do not introduce a wire of a type whose `capabilities.<type>` is `false`**
|
|
218
|
-
unless the user asked for it — and an approved
|
|
219
|
+
unless the user asked for it — and an approved changeset plan counts as them
|
|
219
220
|
asking. A plan the user signed off on authorizes the wires it names, so build
|
|
220
221
|
them and flip the capability, rather than refusing planned work because the
|
|
221
222
|
flag still reads `false` from before the plan.
|