@orkestrel/scaffold 0.0.72 → 0.0.74
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/bin/main.js +75 -34
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/skills/orkestrel-prove-journey/SKILL.md +175 -29
- package/dist/host/agents/skills/orkestrel-prove-journey/references/captures.md +42 -15
- package/dist/host/agents/skills/orkestrel-prove-journey/references/decide.md +34 -15
- package/dist/host/agents/skills/orkestrel-prove-journey/references/layer.md +248 -52
- package/dist/host/agents/skills/orkestrel-prove-journey/references/statechart.md +255 -49
- package/dist/host/agents/skills/orkestrel-prove-journey/references/styles.md +111 -23
- package/dist/host/agents/transports/codex.md +7 -0
- package/dist/host/claude/agents/orkestrel.md +46 -46
- package/dist/host/claude/rules/documentation.md +2 -0
- package/dist/host/claude/rules/tests.md +5 -3
- package/dist/host/claude/rules/workspace.md +26 -15
- package/dist/host/dotfiles/gitignore +3 -0
- package/dist/host/guides/README.md +5 -0
- package/dist/host/guides/scaffold.md +97 -13
- package/dist/host/guides/test.md +788 -206
- package/dist/host/manifest.json +22 -32
- package/dist/host/tests/config.test.ts +272 -121
- package/dist/host/tests/policy.test.ts +11 -1
- package/dist/host/tests/setupPolicy.ts +571 -7
- package/dist/src/core/index.cjs +109 -18
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +19 -6
- package/dist/src/core/index.d.ts +19 -6
- package/dist/src/core/index.js +109 -19
- package/dist/src/core/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -19,7 +19,7 @@ Read the current files in this order:
|
|
|
19
19
|
3. [layer.md](references/layer.md) before importing, extending, or debugging the journey layer.
|
|
20
20
|
4. [captures.md](references/captures.md) before registering a state or placing a capture.
|
|
21
21
|
5. [styles.md](references/styles.md) before asserting anything the browser resolved.
|
|
22
|
-
6. [statechart.md](references/statechart.md) before declaring a transition or
|
|
22
|
+
6. [statechart.md](references/statechart.md) before declaring a transition or mounting the harness.
|
|
23
23
|
7. [decide.md](references/decide.md) before routing a question to an instrument.
|
|
24
24
|
8. `guides/README.md`, the governing guide for the surface, and `ROADMAP.md` when present.
|
|
25
25
|
9. The `*/types.ts` of every environment the journeys drive, plus the application's root component,
|
|
@@ -48,21 +48,105 @@ families a surface owes. It never switches what a declared family proves.
|
|
|
48
48
|
scope finding and stop; never prove the remaining families around it.
|
|
49
49
|
- Assert the declaration itself: a family listed with no proof, and a proof belonging to no listed
|
|
50
50
|
family, each fail the run.
|
|
51
|
+
- Bind the journey laws to every declared family, not to the journey family alone. A matrix reading
|
|
52
|
+
and a transport assertion reach their surface through the same verbs a journey reaches it through.
|
|
53
|
+
- Change route and theme through the interface in the matrix family and the transport family. A
|
|
54
|
+
family that navigates by calling the application's own router proves the router, and says nothing
|
|
55
|
+
about the screen it reads afterwards.
|
|
56
|
+
- Let the transport family construct the store it hands the application, and drive the application
|
|
57
|
+
from the interface after that. The constructed store is the fixture; it is never the drive.
|
|
58
|
+
- Take an entity read or a storage read as corroboration beside a rendered assertion, never in place
|
|
59
|
+
of one.
|
|
60
|
+
|
|
61
|
+
## Resolve the population
|
|
62
|
+
|
|
63
|
+
Resolve every target and every population by ARIA role and accessible name as rendered.
|
|
64
|
+
|
|
65
|
+
- Admit a selector only where the population carries no role at all — a paragraph set, a token
|
|
66
|
+
census, a style-escape walk. Declare that selector in the workspace's browser test setup module
|
|
67
|
+
with the reason beside it, and never in a test file.
|
|
68
|
+
- Read a painted population through the platform: `element.checkVisibility()` reports what the box
|
|
69
|
+
tree renders, and a non-zero `getBoundingClientRect()` reports what occupies space. The layer
|
|
70
|
+
publishes no painted-population predicate.
|
|
71
|
+
- Reach for `isRendered` and `isReachable` where the subject is one element's own reachability
|
|
72
|
+
([layer.md](references/layer.md) → The resolver).
|
|
51
73
|
|
|
52
74
|
## Read the variant once
|
|
53
75
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
76
|
+
A browser application born by `scaffold new` carries the journey axis: the
|
|
77
|
+
`configs/app/vite.journey.config.ts` wrapper, the root `appJourney` factory, the `test:journey`
|
|
78
|
+
script, and that script's place in the `test` chain. That wiring fans the journey suite out into one
|
|
79
|
+
Vitest project per variant, and each project provides its own variant. Read the variant once at run
|
|
80
|
+
start, and let it choose the capture destination, the matrix row, and the statechart run together.
|
|
81
|
+
|
|
82
|
+
- Read `inject('variant')` for this run's variant name, `inject('variants')` for the declared list,
|
|
83
|
+
and `inject('capture')` for whether this run writes frames.
|
|
84
|
+
- Declare the provided types once, by augmenting Vitest's own `ProvidedContext` in the workspace's
|
|
85
|
+
browser test setup module, so `inject` is typed rather than narrowed at each call.
|
|
86
|
+
- Declare the variant list in `configs/app/vite.journey.config.ts`, the birth-owned wrapper a
|
|
87
|
+
browser application is born with. Its presence is the journey axis. Rename and extend the seeded
|
|
88
|
+
viewports for this application.
|
|
89
|
+
- Activate the axis in a workspace born before it: write `configs/app/vite.journey.config.ts`, run
|
|
90
|
+
`scaffold repair`, then add `npm run test:journey` to the `test` script after `npm run test:app`.
|
|
91
|
+
The repair defines `appJourney` in the root configuration, emits the `test:journey` script, and
|
|
92
|
+
excludes the journey suite from the ordinary `app:browser` project. It does not rewrite the `test`
|
|
93
|
+
chain, so the chain entry is yours to add. Add that entry before the repair instead where you
|
|
94
|
+
prefer: `test:journey` names a configuration rather than a project, so neither order blocks the
|
|
95
|
+
repair.
|
|
96
|
+
- Name each variant for the theme and the viewport it renders, such as `dark-390`. Never split the
|
|
97
|
+
theme from the viewport; a split writes a filename naming a combination the run did not render.
|
|
98
|
+
- Compose each variant's theme `apply` inside the test, from the variant's name. Vitest `provide`
|
|
99
|
+
carries serializable values, so `name`, `width`, and `height` cross that channel and a function
|
|
100
|
+
does not.
|
|
101
|
+
- Apply the theme through the application's own interface wherever the application ships a theme
|
|
102
|
+
control, and through the attribute the surface reads where it does not.
|
|
103
|
+
- Run the axis with `npm run test:journey`, which runs that wrapper and joins the `test` chain. Set
|
|
104
|
+
`CAPTURE` to `1` in your own shell and run `npm run test:journey` again to write the frames; the
|
|
105
|
+
root configuration reads that variable and provides it as `capture`.
|
|
106
|
+
- Keep the journeys in `tests/app/browser/integration.test.ts`. Each variant project collects that
|
|
107
|
+
file alone, and the ordinary `app:browser` project excludes it while the axis is on, so a journey
|
|
108
|
+
written anywhere else runs in no variant.
|
|
61
109
|
- Loop every declared variant inside one run for the matrix family
|
|
62
110
|
([styles.md](references/styles.md) → Run per variant).
|
|
63
111
|
- Render exactly one variant per run for the capture family
|
|
64
112
|
([captures.md](references/captures.md) → Variants).
|
|
65
113
|
|
|
114
|
+
`scaffold audit` reports a manifest and a wrapper that disagree as one of the following questions.
|
|
115
|
+
Settle the one it reports before trusting a green run.
|
|
116
|
+
|
|
117
|
+
| The question `scaffold audit` reports | Settle it by |
|
|
118
|
+
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
|
|
119
|
+
| `The manifest at <target> names a Vitest configuration the plan does not emit and the target does not hold: test:journey --config configs/app/vite.journey.config.ts. Add the configuration, or remove the script that names it and its invocation from the test chain.` | Writing the wrapper and running `scaffold repair`, or removing the `test:journey` script and its chain invocation |
|
|
120
|
+
| `The manifest at <target> does not invoke npm run test:journey from its test chain. Add npm run test:journey after npm run test:app.` | Adding `npm run test:journey` to the `test` script after `npm run test:app` |
|
|
121
|
+
|
|
122
|
+
`scaffold audit` reports the invocation question only when no chain from `test` reaches
|
|
123
|
+
`npm run test:journey` through literal `npm run` calls, so a chain reaching it through an
|
|
124
|
+
intermediate script raises none. It reports the configuration question only for a `test:*` script
|
|
125
|
+
whose text names `vitest`, so a script naming another runner's configuration raises none either.
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import type { JourneyVariant } from '@orkestrel/test'
|
|
129
|
+
import type { CaptureVariant } from '@orkestrel/test/browser'
|
|
130
|
+
import { inject } from 'vitest'
|
|
131
|
+
// `applyTheme` is the workspace's own setup-module export, named for the act it performs.
|
|
132
|
+
import { applyTheme } from '../../setupBrowser.js'
|
|
133
|
+
|
|
134
|
+
declare module 'vitest' {
|
|
135
|
+
interface ProvidedContext {
|
|
136
|
+
readonly variant: string
|
|
137
|
+
readonly variants: readonly JourneyVariant[]
|
|
138
|
+
readonly capture: boolean
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const VARIANT = inject('variant')
|
|
143
|
+
const CAPTURE = inject('capture')
|
|
144
|
+
const VARIANTS: readonly CaptureVariant[] = inject('variants').map((variant) => ({
|
|
145
|
+
...variant,
|
|
146
|
+
apply: () => applyTheme(variant.name),
|
|
147
|
+
}))
|
|
148
|
+
```
|
|
149
|
+
|
|
66
150
|
## Apply the journey laws
|
|
67
151
|
|
|
68
152
|
1. **Drive only what a person can see and reach.** Resolve every interactive target by its ARIA
|
|
@@ -80,9 +164,10 @@ naming a combination the run did not render.
|
|
|
80
164
|
by an identity read of one frame.
|
|
81
165
|
5. **Generate the portfolio from the acceptance journeys.** Place each registered state inside the
|
|
82
166
|
journey that reaches it, and never register a state no journey reaches.
|
|
83
|
-
6. **Commit a value through an act a person performs:** Enter
|
|
84
|
-
|
|
85
|
-
observe as a surface finding, and never
|
|
167
|
+
6. **Commit a value through an act a person performs:** `pressKeys('{Enter}')` on the focused
|
|
168
|
+
control, a Tab away, or a named button. Report a surface that commits on a timer, on an
|
|
169
|
+
unpredictable event, or only after work the person cannot observe as a surface finding, and never
|
|
170
|
+
work around it in the layer.
|
|
86
171
|
7. **Type only what a person would.** Journeys carry trusted input; adversarial payloads belong to
|
|
87
172
|
the transport family and the parser suites.
|
|
88
173
|
8. **Perform every interaction step unconditionally.** Never gate a step on whether the control it
|
|
@@ -92,15 +177,21 @@ naming a combination the run did not render.
|
|
|
92
177
|
|
|
93
178
|
## Import the journey layer
|
|
94
179
|
|
|
95
|
-
- Import every journey verb, reader, and fixture builder from `@orkestrel/test/browser
|
|
96
|
-
|
|
97
|
-
([layer.md](references/layer.md) → Import, never
|
|
180
|
+
- Import every journey verb, reader, wait, and fixture builder from `@orkestrel/test/browser`, and
|
|
181
|
+
every host-independent wait and table type from `@orkestrel/test`. Write one of your own only
|
|
182
|
+
where those entries publish none for the act ([layer.md](references/layer.md) → Import, never
|
|
183
|
+
implement).
|
|
184
|
+
- Send every key sequence through `pressKeys`, which refuses a sequence sent while nothing but the
|
|
185
|
+
document body holds focus. Never reach past it to the provider's own keyboard function from a
|
|
186
|
+
journey, a matrix reading, or a statechart phase.
|
|
98
187
|
- Place a helper you must write in the workspace's browser test setup module, name it for the act,
|
|
99
188
|
and export it from there under `.claude/rules/tests.md`. Never declare a resolver inside a test
|
|
100
189
|
file.
|
|
190
|
+
- Prove that setup module with `tests/setupBrowser.test.ts`, and activate its `setup:browser`
|
|
191
|
+
project in the order [layer.md](references/layer.md) → Import, never implement states.
|
|
101
192
|
- Drive every step through the published verbs, and never dispatch a constructed event
|
|
102
193
|
([layer.md](references/layer.md) → What it drives).
|
|
103
|
-
- Re-verify every target against what the application renders
|
|
194
|
+
- Re-verify every target against what the application renders after any markup change
|
|
104
195
|
([layer.md](references/layer.md) → Role vocabulary).
|
|
105
196
|
|
|
106
197
|
## Derive journeys from intents
|
|
@@ -119,8 +210,9 @@ placement and scope `.claude/rules/tests.md` fixes.
|
|
|
119
210
|
action it observes; a predicate already true when the poll starts binds nothing.
|
|
120
211
|
- Assert the state the flow must reach, never the transient path taken to it. A criterion that bans
|
|
121
212
|
a harmless transient over-refuses and breaks on the next honest implementation.
|
|
122
|
-
- Assert the negative beside the positive whenever a value replaces another: the
|
|
123
|
-
|
|
213
|
+
- Assert the negative beside the positive whenever a value replaces another: name the arriving
|
|
214
|
+
sentence and carry the replaced one in `absent`, so the reading that resolves carries one and not
|
|
215
|
+
the other ([layer.md](references/layer.md) → The waits).
|
|
124
216
|
- Assert the state a control announces beside every drive that sets it, and on an unselected
|
|
125
217
|
sibling. A control announcing state owes this assertion whether or not the surface carries the
|
|
126
218
|
statechart family.
|
|
@@ -131,6 +223,25 @@ placement and scope `.claude/rules/tests.md` fixes.
|
|
|
131
223
|
- Report a bare accessible name that answers for more than one reachable element on one screen as a surface
|
|
132
224
|
finding, and target through role or region until the surface is fixed.
|
|
133
225
|
|
|
226
|
+
### The intents every surface owes
|
|
227
|
+
|
|
228
|
+
Write a journey for each of the following wherever the surface has that state. Take the expected
|
|
229
|
+
outcome from the product guide; this skill supplies the mechanism and invents no copy, no redirect,
|
|
230
|
+
and no title scheme.
|
|
231
|
+
|
|
232
|
+
| Intent | The journey proves |
|
|
233
|
+
| ----------------- | ------------------------------------------------------------------------------- |
|
|
234
|
+
| Arrival | The entry route renders its own screen, read through a named region |
|
|
235
|
+
| An unknown route | What the application does with a route it does not carry, and what it says |
|
|
236
|
+
| An empty result | What a query matching nothing renders, in the product's own words |
|
|
237
|
+
| The document name | The title each screen publishes, asserted per screen |
|
|
238
|
+
| A render failure | What a person reads when a component throws, rather than an unexplained surface |
|
|
239
|
+
|
|
240
|
+
- Report a missing outcome as a product finding, with its evidence site, rather than inventing the
|
|
241
|
+
copy the surface owes.
|
|
242
|
+
- Assert the title from `document.title` per screen, against the title the product guide names for
|
|
243
|
+
that screen. Report a screen the guide gives no title as a product finding.
|
|
244
|
+
|
|
134
245
|
## Prove the refusals
|
|
135
246
|
|
|
136
247
|
- Give every surface a refusal family: the controls a person must not reach in the state the
|
|
@@ -146,30 +257,38 @@ placement and scope `.claude/rules/tests.md` fixes.
|
|
|
146
257
|
## Declare the transport family
|
|
147
258
|
|
|
148
259
|
- Name the block for what it proves — persistence, restart, storage failure.
|
|
149
|
-
- Drive it through the application's real session and store contracts.
|
|
150
|
-
|
|
151
|
-
|
|
260
|
+
- Drive it through the application's real session and store contracts.
|
|
261
|
+
- Build the host's own storage conditions with `createStorage`: `reads: false` and `writes: false`
|
|
262
|
+
withhold what a browser with site data blocked withholds, `quota` caps the accepted `setItem`
|
|
263
|
+
calls, and `permit()` grants the withheld permissions the way a person allowing site data grants
|
|
264
|
+
them. It replenishes no quota, because room and permission are different refusals.
|
|
265
|
+
- Assert the withheld voice on its `name` as well as its message: a withheld operation raises a
|
|
266
|
+
`DOMException` named `SecurityError`, and a write past the quota raises one named
|
|
267
|
+
`QuotaExceededError`.
|
|
152
268
|
- Prove the visible half in a journey: the failure sentence a person reads, and the retry control
|
|
153
|
-
that clears it.
|
|
269
|
+
that clears it. A storage failure whose visible half is a control that silently does nothing is a
|
|
270
|
+
surface finding.
|
|
154
271
|
- Assert restart by starting a second session over the same store and polling the restored value.
|
|
272
|
+
- Take a stalled read to the application's own asynchronous store contract, never to `Storage`.
|
|
273
|
+
`Storage` is synchronous, so a hanging read is not expressible against it and a fixture that fakes
|
|
274
|
+
one is proving a surface the application does not have.
|
|
155
275
|
|
|
156
276
|
## Prove the styles
|
|
157
277
|
|
|
158
278
|
Follow [styles.md](references/styles.md) for the resolved-value law, the per-variant run, the
|
|
159
|
-
composited contrast reading and its
|
|
279
|
+
composited contrast reading and its published control, the authored-class census, the
|
|
160
280
|
`extractStyles` reading, and the token comparison.
|
|
161
281
|
|
|
162
282
|
## Prove the statechart
|
|
163
283
|
|
|
164
284
|
Follow [statechart.md](references/statechart.md) for the transition table, the scenario per
|
|
165
|
-
transition, the runner, the harness
|
|
166
|
-
the interface.
|
|
285
|
+
transition, the runner, the mounted harness, and the gate that reads its tally.
|
|
167
286
|
|
|
168
287
|
## Generate the portfolio
|
|
169
288
|
|
|
170
289
|
Follow [captures.md](references/captures.md) for the state registry and its placement rules, the
|
|
171
|
-
|
|
172
|
-
|
|
290
|
+
variant matrix, the always-on filename proof, the capture-run membership proof, and how a state that
|
|
291
|
+
exists only during an activation is captured.
|
|
173
292
|
|
|
174
293
|
Where a capture and a green suite disagree, take the capture as the evidence and the fixture as the
|
|
175
294
|
defect.
|
|
@@ -181,27 +300,54 @@ Route review of the portfolio to the `orkestrel-polish-surface` campaign. Do not
|
|
|
181
300
|
Follow [decide.md](references/decide.md) before spending a round on a question. It fixes which
|
|
182
301
|
instrument judges which claim, what `prove` cannot serve, and what the run's written artifact holds.
|
|
183
302
|
|
|
303
|
+
## Mutate each assertion class
|
|
304
|
+
|
|
305
|
+
Mutate each assertion class once, read the red, restore, and read the green.
|
|
306
|
+
`.claude/rules/quality.md` § Instruments owns the law this satisfies.
|
|
307
|
+
|
|
308
|
+
| Assertion class | The mutation | What must change |
|
|
309
|
+
| --------------- | -------------------------------------------------------------------- | --------------------------------- |
|
|
310
|
+
| Journey | Omit the act the journey performs, keeping collection valid | The destination assertion reddens |
|
|
311
|
+
| Refusal | Make the withheld control reachable, or present, without renaming it | The asserted voice changes |
|
|
312
|
+
|
|
313
|
+
- Omit the act rather than weakening the assertion. An assertion a missing act leaves green cannot
|
|
314
|
+
tell arrival from never having left.
|
|
315
|
+
- Change reachability rather than the name. A renamed control reddens on absence, which is a finding
|
|
316
|
+
the refusal family already carries.
|
|
317
|
+
|
|
184
318
|
## Accept
|
|
185
319
|
|
|
186
320
|
Completion requires all of:
|
|
187
321
|
|
|
188
322
|
- every in-scope user intent reaching its outcome through the interface, with no step that reaches
|
|
189
323
|
past it;
|
|
324
|
+
- the intents every surface owes present wherever the surface has the state, each outcome taken from
|
|
325
|
+
the product guide and every missing one reported as a product finding;
|
|
190
326
|
- keyboard-only reachability proven on every surface the journeys cover;
|
|
191
327
|
- a refusal family per surface, each asserting one exact failure voice;
|
|
192
328
|
- the transport family declared separately, driven through real implementations, and convergent;
|
|
193
329
|
- the declared families each proven, and the declaration itself asserted;
|
|
194
|
-
- the matrix family read once per declared variant, each style
|
|
195
|
-
control
|
|
330
|
+
- the matrix family read once per declared variant, each style reading carrying its published
|
|
331
|
+
control from [styles.md](references/styles.md) → The published controls in the same run, and the
|
|
332
|
+
contrast reading's control straddling its declared bar;
|
|
196
333
|
- the authored-class census and the `extractStyles` reading taken on the mounted surface, each
|
|
197
334
|
reporting the population it walked;
|
|
198
|
-
- the statechart table driven to a terminal
|
|
335
|
+
- the statechart table driven to a terminal status with no failed row, and the harness tally read
|
|
336
|
+
from the object and from its markup;
|
|
199
337
|
- the registry-times-variants filename proof and the state-placement proof green in an ordinary run;
|
|
200
338
|
- one capture run per variant writing every registered file, and the disk-membership proof green;
|
|
201
339
|
- the written artifact produced for every variant the run rendered, named by that variant;
|
|
202
340
|
- perception assertions quoting rendered text, and the vocabulary sweep green on the whole page;
|
|
341
|
+
- the browser test setup module proven by `tests/setupBrowser.test.ts` in the `setup:browser`
|
|
342
|
+
project `scaffold repair` registers;
|
|
343
|
+
- each assertion class mutated, with the red reading and the green reading recorded;
|
|
203
344
|
- the repository gates green, under the independent-verification law in `.agents/orchestration.md`.
|
|
204
345
|
|
|
346
|
+
State the engine bound with the verdict. The gate renders one engine, so a claim about a second
|
|
347
|
+
engine is unproven until a reading on that engine records it. The emitted `configs/browsers.ts` doc
|
|
348
|
+
block is the home of that limit and of the condition that reopens it. Cite that doc block, and copy
|
|
349
|
+
neither into a verdict.
|
|
350
|
+
|
|
205
351
|
Report each journey by the intent it proves, the refusals it establishes, the states it placed, the
|
|
206
352
|
variants it read, the statechart outcome it reached, and every surface finding the layer's refusals
|
|
207
353
|
exposed.
|
|
@@ -4,19 +4,38 @@ Take every screenshot from an acceptance journey, at the moment that journey is
|
|
|
4
4
|
picture names. Never add a test whose only purpose is a screenshot, and never stage a state for the
|
|
5
5
|
camera that a journey did not reach through the interface.
|
|
6
6
|
|
|
7
|
+
## The vocabulary
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import type { JourneyVariant } from '@orkestrel/test'
|
|
11
|
+
import type {
|
|
12
|
+
CaptureVariant,
|
|
13
|
+
FrameOptions,
|
|
14
|
+
FrameReading,
|
|
15
|
+
PortfolioInterface,
|
|
16
|
+
PortfolioOptions,
|
|
17
|
+
} from '@orkestrel/test/browser'
|
|
18
|
+
import { captureFrame, createPortfolio, expandCaptures, readFrame } from '@orkestrel/test/browser'
|
|
19
|
+
import { inject } from 'vitest'
|
|
20
|
+
```
|
|
21
|
+
|
|
7
22
|
## The hook
|
|
8
23
|
|
|
9
24
|
`createPortfolio(options)` is the capture door, and `place(state, element?)` is the hook. Build the
|
|
10
25
|
portfolio once per file from a `PortfolioOptions` value carrying the registry as `states`, every
|
|
11
|
-
declared `CaptureVariant` as `variants`, this run's `variant`, the `directory` each
|
|
12
|
-
to, and the flag as `enabled`.
|
|
26
|
+
declared `CaptureVariant` as `variants`, this run's variant name as `variant`, the `directory` each
|
|
27
|
+
file is written to, and the flag as `enabled`.
|
|
13
28
|
|
|
14
29
|
- Place a state with `place(state)` for the whole page, and `place(state, element)` where the
|
|
15
30
|
picture is one element.
|
|
16
|
-
-
|
|
17
|
-
|
|
31
|
+
- Take `enabled` from `inject('capture')`, which the generated root configuration provides from the
|
|
32
|
+
`CAPTURE` environment variable set to `1` in the shell that runs the axis. An ordinary run leaves
|
|
33
|
+
it false, so `place` returns `undefined`, resizes nothing, writes nothing, and records nothing.
|
|
18
34
|
- Read `files` for the registry expanded across every variant, `placements` for what this run placed,
|
|
19
35
|
and `paths` for what it wrote. Each hands back a snapshot.
|
|
36
|
+
- Reach for `readFrame` where the written image itself is the subject — its decoded size, or its
|
|
37
|
+
pixels. `captureFrame` already reads each file back and compares its bytes against the frame it
|
|
38
|
+
shot.
|
|
20
39
|
|
|
21
40
|
The package already refuses these, so assert none of them again:
|
|
22
41
|
|
|
@@ -30,8 +49,7 @@ The package already refuses these, so assert none of them again:
|
|
|
30
49
|
|
|
31
50
|
## The registry
|
|
32
51
|
|
|
33
|
-
Declare the state names
|
|
34
|
-
that declaration.
|
|
52
|
+
Declare the state names once in the journey file, and build the portfolio from that declaration.
|
|
35
53
|
|
|
36
54
|
- Name a state for its surface and its condition — `answer-partial`, `start-storage-failure`,
|
|
37
55
|
`case-delete-confirmation`.
|
|
@@ -44,22 +62,28 @@ that declaration.
|
|
|
44
62
|
|
|
45
63
|
## Variants
|
|
46
64
|
|
|
47
|
-
The run axis is fixed in [SKILL.md](../SKILL.md) → Read the variant once
|
|
48
|
-
|
|
49
|
-
adds these.
|
|
65
|
+
The run axis is fixed in [SKILL.md](../SKILL.md) → Read the variant once. The capture family adds
|
|
66
|
+
these.
|
|
50
67
|
|
|
51
|
-
-
|
|
68
|
+
- Declare the viewport list in the birth-owned `configs/app/vite.journey.config.ts` wrapper, as
|
|
69
|
+
`readonly JourneyVariant[]`. That file is the adopter's, so its names and its viewports are the
|
|
70
|
+
application's to set, and the presence of that file is the journey axis.
|
|
71
|
+
- Compose each variant's `apply` inside the test, from the name the project provided, and hand the
|
|
72
|
+
composed `CaptureVariant` list to `createPortfolio`. `JourneyVariant` carries the `name`, the
|
|
73
|
+
`width`, and the `height` that cross Vitest's `provide` channel; `CaptureVariant` adds the `apply`
|
|
74
|
+
that does not.
|
|
52
75
|
- Name each variant for the theme and the viewport it renders, such as `dark-390`. The name is the
|
|
53
76
|
second half of every filename the run writes, so a variant named for one alone produces a
|
|
54
77
|
portfolio nobody can tell apart.
|
|
55
|
-
- Pass the whole
|
|
56
|
-
refuses a `variant` no declared variant carries, and `files` expands the
|
|
57
|
-
list rather than across the one being rendered.
|
|
78
|
+
- Pass the whole composed list as `variants` and `inject('variant')` as `variant`.
|
|
79
|
+
`createPortfolio` refuses a `variant` no declared variant carries, and `files` expands the
|
|
80
|
+
registry across the whole list rather than across the one being rendered.
|
|
81
|
+
- Produce the portfolio — the registry times the variants — by running the axis once per variant,
|
|
82
|
+
which `npm run test:journey` does in one command.
|
|
58
83
|
|
|
59
84
|
## The proofs the suite owes
|
|
60
85
|
|
|
61
|
-
|
|
62
|
-
suite carries these.
|
|
86
|
+
Write each of the following proofs into the suite. The package asserts none of them.
|
|
63
87
|
|
|
64
88
|
| Proof | Runs | Asserts |
|
|
65
89
|
| -------------------- | -------------- | ---------------------------------------------------------------------------------- |
|
|
@@ -92,6 +116,9 @@ after the click returns.
|
|
|
92
116
|
## Hygiene
|
|
93
117
|
|
|
94
118
|
- Keep the portfolio out of version control.
|
|
119
|
+
- Take each frame after the paint settles. Reach for `waitForAnimations` on the element whose
|
|
120
|
+
transition was running ([layer.md](layer.md) → The waits); a frame shot mid-transition pictures a
|
|
121
|
+
state the interface never rests in.
|
|
95
122
|
- Regenerate the whole matrix from the journeys after any surface change. Never judge a round
|
|
96
123
|
against a portfolio that is part old and part new.
|
|
97
124
|
- Route review of the portfolio to the `orkestrel-polish-surface` campaign, which owns preflight,
|
|
@@ -7,7 +7,7 @@ instrument here answers as open, and never answer it with the nearest instrument
|
|
|
7
7
|
| -------------------------------------- | -------------------------------------------- |
|
|
8
8
|
| A compiler, a linter, or a Node runner | The `prove` tool, with its negative control |
|
|
9
9
|
| A person's eye | The run's written artifact, named by variant |
|
|
10
|
-
| A person watching a widget move |
|
|
10
|
+
| A person watching a widget move | The harness run's frames and its artifact |
|
|
11
11
|
| The browser's own resolved value | The matrix family ([styles.md](styles.md)) |
|
|
12
12
|
|
|
13
13
|
Never ask `prove` about pixels, and never ask a screenshot about types.
|
|
@@ -25,17 +25,26 @@ follow it there rather than restating it here.
|
|
|
25
25
|
|
|
26
26
|
## The limit that decides the split
|
|
27
27
|
|
|
28
|
-
`prove` cannot serve a browser project
|
|
29
|
-
|
|
28
|
+
`prove` cannot serve a browser project, and the limit belongs to the installed `@orkestrel/probe`
|
|
29
|
+
rather than to a version this file names. Confirm it against the copy this workspace holds before
|
|
30
|
+
routing a rendered question, and record the version you read beside the ruling.
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
- The runtime stage pins the `threads` pool, so a browser project's specification runs in a Node
|
|
34
|
-
worker. `@orkestrel/test/browser` imports `vitest/browser` at module scope, so the browser setup
|
|
35
|
-
file throws before the case runs and the failure reads as the claim's.
|
|
32
|
+
Read the pool pin, the project lookup, and the guide's own statement. Any of them holding is the
|
|
33
|
+
limit holding:
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
- **The pool pin.** Read the runtime stage in the installed server entry. Where it pins the
|
|
36
|
+
`threads` pool, a browser project's specification runs in a Node worker,
|
|
37
|
+
`@orkestrel/test/browser` imports `vitest/browser` at module scope, and the browser setup file
|
|
38
|
+
throws before the case runs.
|
|
39
|
+
- **The project lookup.** The runtime stage looks a project up by the name it infers from the test
|
|
40
|
+
path, and a browser project is instantiated under its browser-expanded name, so the lookup finds
|
|
41
|
+
nothing and the claim is refused as missing.
|
|
42
|
+
- **The guide's statement.** Read what `@orkestrel/probe`'s own guide says the stages serve. Where
|
|
43
|
+
it names no browser project, no stage claims one.
|
|
44
|
+
|
|
45
|
+
Never route a rendered question to `prove` while any of them holds. The pinned-pool refusal arrives
|
|
46
|
+
as a case failure, which reads exactly like a broken claim. Record the version, the pool pin you
|
|
47
|
+
read, and what the guide says, so the next round re-reads rather than re-deriving.
|
|
39
48
|
|
|
40
49
|
## The rendered artifact
|
|
41
50
|
|
|
@@ -61,8 +70,18 @@ Rules the artifact obeys:
|
|
|
61
70
|
old and part new.
|
|
62
71
|
- Keep it out of version control.
|
|
63
72
|
|
|
64
|
-
## The harness
|
|
65
|
-
|
|
66
|
-
Send a look a person decides on to
|
|
67
|
-
|
|
68
|
-
|
|
73
|
+
## The harness run
|
|
74
|
+
|
|
75
|
+
Send a look a person decides on to what the harness run itself produced, rather than to a still of a
|
|
76
|
+
screen.
|
|
77
|
+
|
|
78
|
+
- Place a capture inside the harness run at each state the decision is about, and name the frames in
|
|
79
|
+
the round. The run drove the widget through the interface, so the frames are a record of movement
|
|
80
|
+
rather than a staged pose.
|
|
81
|
+
- Name the artifact beside them. Its accessible-tree lines and its journal say what the widget
|
|
82
|
+
announced while it moved, which a frame cannot carry.
|
|
83
|
+
- Give the harness a final row or a demo step that leaves the widget in its most legible state, so
|
|
84
|
+
the last frame is the one a person wants to look at.
|
|
85
|
+
- Name a deep link only where the workspace already ships a harness page
|
|
86
|
+
([statechart.md](statechart.md) → A harness page is product). Never ask a round to open a link the
|
|
87
|
+
repository does not serve.
|