@orkestrel/scaffold 0.0.81 → 0.0.83

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/agents/skills/orkestrel-publish/scripts/wave.js +14 -3
  2. package/dist/bin/main.js +295 -13
  3. package/dist/bin/main.js.map +1 -1
  4. package/dist/host/AGENTS.md +5 -3
  5. package/dist/host/agents/orchestration.md +1 -0
  6. package/dist/host/agents/skills/orkestrel-harden/references/centralization.md +2 -2
  7. package/dist/host/agents/skills/orkestrel-journey/SKILL.md +43 -39
  8. package/dist/host/agents/skills/orkestrel-journey/references/captures.md +3 -3
  9. package/dist/host/agents/skills/orkestrel-journey/references/decide.md +10 -9
  10. package/dist/host/agents/skills/orkestrel-journey/references/layer.md +5 -5
  11. package/dist/host/agents/skills/orkestrel-journey/references/recorded.md +77 -0
  12. package/dist/host/agents/skills/orkestrel-journey/references/statechart.md +3 -3
  13. package/dist/host/agents/skills/orkestrel-journey/references/styles.md +9 -9
  14. package/dist/host/agents/skills/orkestrel-publish/references/wave.md +4 -0
  15. package/dist/host/agents/skills/orkestrel-publish/scripts/wave.ts +22 -4
  16. package/dist/host/claude/agents/orkestrel.md +10 -10
  17. package/dist/host/claude/rules/application.md +20 -6
  18. package/dist/host/claude/rules/architecture.md +2 -2
  19. package/dist/host/claude/rules/browser.md +9 -0
  20. package/dist/host/claude/rules/documentation.md +2 -1
  21. package/dist/host/claude/rules/quality.md +5 -0
  22. package/dist/host/claude/rules/styles.md +35 -12
  23. package/dist/host/claude/rules/tests.md +15 -10
  24. package/dist/host/claude/rules/workspace.md +128 -98
  25. package/dist/host/claude/skills/orkestrel-journey/SKILL.md +1 -1
  26. package/dist/host/configs/helpers.ts +157 -9
  27. package/dist/host/configs/policy.ts +64 -61
  28. package/dist/host/dotfiles/oxlintrc.json +132 -16
  29. package/dist/host/dotfiles/prettierignore +1 -1
  30. package/dist/host/guides/README.md +9 -5
  31. package/dist/host/guides/guide.md +4 -1
  32. package/dist/host/guides/scaffold.md +482 -115
  33. package/dist/host/manifest.json +38 -32
  34. package/dist/host/scripts/codex.sh +0 -0
  35. package/dist/host/scripts/cursor.sh +0 -0
  36. package/dist/host/scripts/deps.sh +0 -0
  37. package/dist/host/scripts/ollama.sh +0 -0
  38. package/dist/host/tests/config.test.ts +996 -39
  39. package/dist/host/tests/policy.test.ts +11 -0
  40. package/dist/host/tests/setupPolicy.ts +396 -22
  41. package/dist/src/core/index.cjs +1289 -164
  42. package/dist/src/core/index.cjs.map +1 -1
  43. package/dist/src/core/index.d.cts +313 -35
  44. package/dist/src/core/index.d.ts +313 -35
  45. package/dist/src/core/index.js +1268 -165
  46. package/dist/src/core/index.js.map +1 -1
  47. package/package.json +7 -6
@@ -16,14 +16,16 @@
16
16
  ## Project model
17
17
 
18
18
  ```text
19
- src/ published library: core, browser, server, optional styles
20
- app/ application: core, browser, server
19
+ src/ published library: core, browser, server, optional styles, extension faces
20
+ app/ application: core, browser, server, extension faces
21
21
  tests/ mirrors source; setup*.ts owns shared test infrastructure
22
22
  configs/ thin target wrappers around root Vite/TypeScript configuration
23
23
  ```
24
24
 
25
25
  - `core` is host-independent. Browser and server may import core; core imports neither; browser and server never import each other.
26
26
  - `app/core` is host-independent. `app/server` may import `app/core`, `src/core`, and `src/server`, never browser code. `app/browser` may import `app/core`, `src/core`, and `src/browser`, and reaches server behavior through shared contracts and transports only.
27
+ - The browser surface is `src/browser`, `app/browser`, the journey, and the showcase; the styles surface is `src/styles` and its themes. An extension adds a face to a surface: the `vue` browser extension adds `src/vue` and `app/vue`, and a named styles extension adds `src/<name>`.
28
+ - `src/vue` and each `src/<name>` may import `src/core` and `src/browser`. `app/vue` may import what `app/browser` may import, plus `app/browser` and `src/vue`. No extension face imports server code or another extension's face.
27
29
  - Published source never imports private app code.
28
30
  - Enforce boundaries with the toolchain (Oxlint import restrictions, scoped TypeScript projects, Vite graphs). Add no second parser for TypeScript, Oxlint, Vue, HTML, CSS, or Vite.
29
31
  - `tsconfig.json`, `vite.config.ts`, and each `*/types.ts` are their sources of truth. Keep structural files even when empty.
@@ -57,7 +59,7 @@ configs/ thin target wrappers around root Vite/TypeScript configuration
57
59
  - **Named discriminants.** Name the axis (`relationship`, `command`, `category`), never `kind` or `type`.
58
60
  - **Centralize by kind.** Types, constants, helpers, validators, parsers, factories, and errors live in their kind file. An implementation file holds one class plus imports.
59
61
  - **Export and test reusable logic.** Fold a trivial one-use helper into its caller or export it from its kind file and test it.
60
- - **No nested functions**, except an anonymous callback passed as an argument or returned as the result.
62
+ - **No nested functions**, except a callback passed as an argument or returned as the result; `.claude/rules/architecture.md` § Functions and orchestration bounds the literal positions it climbs.
61
63
  - **Functional core, imperative shell.** Pure exported leaves; stateful orchestration as class methods. A method never forwards 1:1 to a helper.
62
64
  - **No superfluous wrappers.** A wrapper adds a boundary, invariant, composition, translation, lifecycle, or materially narrower contract, or it goes.
63
65
  - **Minimal public API.** Create or substantively expand a capability with its first real consumer; this gate applies at creation, never later. Expose an existing reusable capability through its environment barrel regardless of consumer count. Remove a symbol only when the capability itself must not exist. Prefer one minimal interface and one shared engine, with a native backend override only for a faster path.
@@ -105,6 +105,7 @@ Every working file lives under the checkout's gitignored `tmp/`, in the director
105
105
  | `tmp/probes/` | runtime probes the `probe` Vitest project collects and the `probe` MCP server arms |
106
106
  | `tmp/type/` | the type stage's workspace mirror, owned by `@orkestrel/probe` |
107
107
  | `tmp/captures/` | screenshots, resolved-style snapshots, and other capture portfolios |
108
+ | `tmp/browsers/` | the journeys a `browse` server saves, their runs and captures, and its profiles under `.profiles/` |
108
109
  | `tmp/worktrees/` | worktrees the Orchestrator creates for a parallel writer |
109
110
 
110
111
  ## .orkestrel layout
@@ -28,8 +28,8 @@ execution and must explain why sibling imports cannot work.
28
28
  | Defining recursive or compositional spine | Class method, after extracting its pure leaves |
29
29
  | Trivial and genuinely one-use | Inline it into the caller |
30
30
 
31
- Never move logic into a nested function to evade centralization. An anonymous callback
32
- passed directly to another operation stays a callback, not a hidden helper declaration.
31
+ Apply `.claude/rules/architecture.md` § Functions and orchestration to nested functions and
32
+ callback positions.
33
33
 
34
34
  ## Hunt the wrapper
35
35
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: orkestrel-journey
3
- description: Prove a browser application the way a person uses it — real keystrokes, clicks, and Tab/Enter against only what is visible and reachable — through the journey layer @orkestrel/test/browser publishes, and generate the capture portfolio, the resolved-style matrix, and the statechart outcome from those same journeys. Use when accepting a UI build, proving an application end to end, deciding whether a surface is reachable by keyboard alone, proving what a screen refuses as well as what it does, proving the styles a browser actually resolved under each theme and viewport, driving a transition table through the interface and watching it run, auditing whether the interface speaks the user's vocabulary rather than the engine's, producing the screenshots a design review judges, routing a rendered question to an artifact a model can read, or whenever the only evidence a screen works is a test that drove it through JavaScript instead of through the interface.
3
+ description: Prove a browser application the way a person uses it — real keystrokes, clicks, and Tab/Enter against only what is visible and reachable — through the journey layer @orkestrel/test/browser publishes, and generate the capture portfolio, the resolved-style matrix, and the statechart outcome from those same journeys. Use when accepting a UI build, proving an application end to end, deciding whether a screen is reachable by keyboard alone, proving what a screen refuses as well as what it does, proving the styles a browser actually resolved under each theme and viewport, driving a transition table through the interface and watching it run, auditing whether the interface speaks the user's vocabulary rather than the engine's, producing the screenshots a design review judges, routing a rendered question to an artifact a model can read, or whenever the only evidence a screen works is a test that drove it through JavaScript instead of through the interface.
4
4
  ---
5
5
 
6
6
  # Prove an application through human journeys
@@ -14,7 +14,8 @@ description: Prove a browser application the way a person uses it — real keyst
14
14
  3. [styles.md](references/styles.md) before asserting anything the browser resolved.
15
15
  4. [statechart.md](references/statechart.md) before declaring a transition or mounting the harness.
16
16
  5. [decide.md](references/decide.md) before routing a question to an instrument.
17
- 6. The `*/types.ts` of every environment the journeys drive, plus the application's root component,
17
+ 6. [recorded.md](references/recorded.md) before judging a browser recording's run as evidence.
18
+ 7. The `*/types.ts` of every environment the journeys drive, plus the application's root component,
18
19
  route entry, and store contract.
19
20
 
20
21
  Treat a retained readiness verdict as evidence to re-verify against the current tip, never as a plan
@@ -23,25 +24,27 @@ each ruling was taken at.
23
24
 
24
25
  ## Declare the families
25
26
 
26
- Declare in the browser environment's `integration.test.ts` which families that surface carries, and
27
+ Declare in the browser environment's `integration.test.ts` which families that screen carries, and
27
28
  assert in the always-on proofs that every declared family is present. A declaration names which
28
- families a surface owes. It never switches what a declared family proves.
29
+ families a screen owes. It never switches what a declared family proves.
29
30
 
30
31
  | Family | Declared | Proves |
31
32
  | ---------- | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
32
33
  | Journey | Always | Each user intent reaches its outcome through the interface |
33
- | Refusal | Always | Each control the surface withholds, through one exact failure voice |
34
- | Matrix | Where the surface ships more than one variant | The values the browser resolved under each declared variant |
34
+ | Refusal | Always | Each control the screen withholds, through one exact failure voice |
35
+ | Matrix | Where the journey wrapper declares more than one variant | The values the browser resolved under each declared variant |
35
36
  | Statechart | Where a journey drives a transition of an entity carrying its own state and event vocabulary | Each declared transition, driven through the interface where it can be |
36
- | Transport | Where the surface persists or restarts | Persistence, restart, and storage failure through real implementations |
37
+ | Transport | Where the screen persists or restarts | Persistence, restart, and storage failure through real implementations |
37
38
  | Capture | Under the capture flag | The registry times the variants, each registered file written to disk |
38
39
 
39
- - Refuse a declaration that omits a family whose trigger the surface meets. Report the omission as a
40
+ - Prove Matrix in the arrival journey `scaffold new` seeds while its wrapper declares more than one
41
+ variant: read one resolved value under each declared variant.
42
+ - Refuse a declaration that omits a family whose trigger the screen meets. Report the omission as a
40
43
  scope finding and stop; never prove the remaining families around it.
41
44
  - Assert the declaration itself: a family listed with no proof, and a proof belonging to no listed
42
45
  family, each fail the run.
43
46
  - Bind the journey laws to every declared family, not to the journey family alone. A matrix reading
44
- and a transport assertion reach their surface through the same verbs a journey reaches it through.
47
+ and a transport assertion reach their screen through the same verbs a journey reaches it through.
45
48
  - Change route and theme through the interface in the matrix family and the transport family. A
46
49
  family that navigates by calling the application's own router proves the router, and says nothing
47
50
  about the screen it reads afterwards.
@@ -92,9 +95,10 @@ start, and let it choose the capture destination, the matrix row, and the statec
92
95
  - Run the axis with `npm run test:journey`, which runs that wrapper and joins the `test` chain. Set
93
96
  `CAPTURE` to `1` in your own shell and run `npm run test:journey` again to write the frames; the
94
97
  root configuration reads that variable and provides it as `capture`.
95
- - Keep the journeys in `tests/app/browser/integration.test.ts`. Each variant project collects that
96
- file alone, and the ordinary `app:browser` project excludes it while the axis is on, so a journey
97
- written anywhere else runs in no variant.
98
+ - Keep the journeys in `tests/app/browser/integration.test.ts`, and the journeys of the `vue`
99
+ extension's application in `tests/app/vue/integration.test.ts`, which `npm run test:journey:vue`
100
+ runs. Each variant project of a mode collects that file alone, and the application's ordinary
101
+ project excludes it while the axis is on, so a journey written anywhere else runs in no variant.
98
102
  - Loop every declared variant inside one run for the matrix family
99
103
  ([styles.md](references/styles.md) → Run per variant).
100
104
  - Render exactly one variant per run for the capture family
@@ -120,7 +124,7 @@ whose text names `vitest`, so a script naming another runner's configuration rai
120
124
  to `createPortfolio` when capture needs no additional synchronous document change. Never attach
121
125
  an asynchronous action to `CaptureVariant.apply` or re-resolve a covered theme control at capture
122
126
  time.
123
- - Where the surface has no theme control, prepare the theme through the attribute the surface
127
+ - Where the screen has no theme control, prepare the theme through the attribute the screen
124
128
  reads. Use the optional `CaptureVariant.apply` hook only for a synchronous document change,
125
129
  such as setting that attribute. Treat its `() => void` contract as synchronous: `createPortfolio`
126
130
  invokes the hook without awaiting a returned promise.
@@ -194,15 +198,15 @@ await PORTFOLIO.place('home')
194
198
  5. **Generate the portfolio from the acceptance journeys.** Place each registered state inside the
195
199
  journey that reaches it, and never register a state no journey reaches.
196
200
  6. **Commit a value through an act a person performs:** `pressKeys('{Enter}')` on the focused
197
- control, a Tab away, or a named button. Report a surface that commits on a timer, on an
198
- unpredictable event, or only after work the person cannot observe as a surface finding, and never
201
+ control, a Tab away, or a named button. Report a screen that commits on a timer, on an
202
+ unpredictable event, or only after work the person cannot observe as a screen finding, and never
199
203
  work around it in the layer.
200
204
  7. **Type only what a person would.** Journeys carry trusted input; adversarial payloads belong to
201
205
  the transport family and the parser suites.
202
206
  8. **Perform every interaction step unconditionally.** Never gate a step on whether the control it
203
207
  is about to drive exists or is reachable, and never branch a journey on `readRefusal`. Let the
204
208
  resolver's failure voice name what the interface withheld. A guarded step passes whether or not
205
- the control was there, so the run goes green on a surface that removed the control.
209
+ the control was there, so the run goes green on a screen that removed the control.
206
210
 
207
211
  ## Import the journey layer
208
212
 
@@ -231,7 +235,7 @@ placement and scope `.claude/rules/tests.md` fixes.
231
235
 
232
236
  - Enter through the real entry: mount the shipped root component with a real store and the route a
233
237
  person lands on, and let the application load itself.
234
- - Reach each surface's own controls through forward Tab traversal in at least one journey.
238
+ - Reach each screen's own controls through forward Tab traversal in at least one journey.
235
239
  - Type keystroke by keystroke where the keystrokes are the subject; fill in one operation where the
236
240
  text is only a payload the person pastes.
237
241
  - Poll every fact the application produces asynchronously until it converges. Never assert one from
@@ -243,38 +247,38 @@ placement and scope `.claude/rules/tests.md` fixes.
243
247
  sentence and carry the replaced one in `absent`, so the reading that resolves carries one and not
244
248
  the other ([layer.md](references/layer.md) → The waits).
245
249
  - Assert the state a control announces beside every drive that sets it, and on an unselected
246
- sibling. A control announcing state owes this assertion whether or not the surface carries the
250
+ sibling. A control announcing state owes this assertion whether or not the screen carries the
247
251
  statechart family.
248
252
  - After a confirmed destructive action, assert through trusted input that focus landed on a visible,
249
253
  announced location.
250
254
  - Assert the whole page's perception never matches the vocabulary the product does not speak —
251
255
  engine, schema, and implementation words the interface is supposed to translate.
252
- - Report a bare accessible name that answers for more than one reachable element on one screen as a surface
253
- finding, and target through role or region until the surface is fixed.
256
+ - Report a bare accessible name that answers for more than one reachable element on one screen as a screen
257
+ finding, and target through role or region until the screen is fixed.
254
258
 
255
- ### The intents every surface owes
259
+ ### The intents every screen owes
256
260
 
257
- Write a journey for each of the following wherever the surface has that state. Take the expected
261
+ Write a journey for each of the following wherever the screen has that state. Take the expected
258
262
  outcome from the product guide; this skill supplies the mechanism and invents no copy, no redirect,
259
263
  and no title scheme.
260
264
 
261
- | Intent | The journey proves |
262
- | ----------------- | ------------------------------------------------------------------------------- |
263
- | Arrival | The entry route renders its own screen, read through a named region |
264
- | An unknown route | What the application does with a route it does not carry, and what it says |
265
- | An empty result | What a query matching nothing renders, in the product's own words |
266
- | The document name | The title each screen publishes, asserted per screen |
267
- | A render failure | What a person reads when a component throws, rather than an unexplained surface |
265
+ | Intent | The journey proves |
266
+ | ----------------- | ------------------------------------------------------------------------------ |
267
+ | Arrival | The entry route renders its own screen, read through a named region |
268
+ | An unknown route | What the application does with a route it does not carry, and what it says |
269
+ | An empty result | What a query matching nothing renders, in the product's own words |
270
+ | The document name | The title each screen publishes, asserted per screen |
271
+ | A render failure | What a person reads when a component throws, rather than an unexplained screen |
268
272
 
269
273
  - Report a missing outcome as a product finding, with its evidence site, rather than inventing the
270
- copy the surface owes.
274
+ copy the screen owes.
271
275
  - Assert the title from `document.title` per screen, against the title the product guide names for
272
276
  that screen. Report a screen the guide gives no title as a product finding.
273
277
 
274
278
  ## Prove the refusals
275
279
 
276
- - Give every surface a refusal family: the controls a person must not reach in the state the
277
- journey has put the surface in.
280
+ - Give every screen a refusal family: the controls a person must not reach in the state the
281
+ journey has put the screen in.
278
282
  - Assert the exact failure voice the case means. Never write an assertion that accepts more than
279
283
  one voice ([layer.md](references/layer.md) → The failure voices).
280
284
  - Cover the restrictions the interface imposes on itself: a collapsed panel's field, a verb
@@ -296,11 +300,11 @@ and no title scheme.
296
300
  `QuotaExceededError`.
297
301
  - Prove the visible half in a journey: the failure sentence a person reads, and the retry control
298
302
  that clears it. A storage failure whose visible half is a control that silently does nothing is a
299
- surface finding.
303
+ screen finding.
300
304
  - Assert restart by starting a second session over the same store and polling the restored value.
301
305
  - Take a stalled read to the application's own asynchronous store contract, never to `Storage`.
302
306
  `Storage` is synchronous, so a hanging read is not expressible against it and a fixture that fakes
303
- one is proving a surface the application does not have.
307
+ one is proving a store contract the application does not have.
304
308
 
305
309
  ## Prove the styles
306
310
 
@@ -350,16 +354,16 @@ Completion requires all of:
350
354
 
351
355
  - every in-scope user intent reaching its outcome through the interface, with no step that reaches
352
356
  past it;
353
- - the intents every surface owes present wherever the surface has the state, each outcome taken from
357
+ - the intents every screen owes present wherever the screen has the state, each outcome taken from
354
358
  the product guide and every missing one reported as a product finding;
355
- - keyboard-only reachability proven on every surface the journeys cover;
356
- - a refusal family per surface, each asserting one exact failure voice;
359
+ - keyboard-only reachability proven on every screen the journeys cover;
360
+ - a refusal family per screen, each asserting one exact failure voice;
357
361
  - the transport family declared separately, driven through real implementations, and convergent;
358
362
  - the declared families each proven, and the declaration itself asserted;
359
363
  - the matrix family read once per declared variant, each style reading carrying its published
360
364
  control from [styles.md](references/styles.md) → The published controls in the same run, and the
361
365
  contrast reading's control straddling its declared bar;
362
- - the authored-class census and the `extractStyles` reading taken on the mounted surface, each
366
+ - the authored-class census and the `extractStyles` reading taken on the mounted screen, each
363
367
  reporting the population it walked;
364
368
  - the statechart table driven to a terminal status with no failed row, and the harness tally read
365
369
  from the object and from its markup;
@@ -378,5 +382,5 @@ block is the home of that limit and of the condition that reopens it. Cite that
378
382
  neither into a verdict.
379
383
 
380
384
  Report each journey by the intent it proves, the refusals it establishes, the states it placed, the
381
- variants it read, the statechart outcome it reached, and every surface finding the layer's refusals
385
+ variants it read, the statechart outcome it reached, and every screen finding the layer's refusals
382
386
  exposed.
@@ -51,12 +51,12 @@ The package already refuses these, so assert none of them again:
51
51
 
52
52
  Declare the state names once in the journey file, and build the portfolio from that declaration.
53
53
 
54
- - Name a state for its surface and its condition — `answer-partial`, `start-storage-failure`,
54
+ - Name a state for its screen and its condition — `answer-partial`, `start-storage-failure`,
55
55
  `case-delete-confirmation`.
56
56
  - Register the states the design work actually needs, and place every registered one. Never leave a
57
57
  registered state unplaced.
58
58
  - Place a capture state from inside the journey that reaches it, immediately after the assertion
59
- that proves the surface is in the condition that state names.
59
+ that proves the screen is in the condition that state names.
60
60
  - Record each placed name in the suite's own set in the same step that calls `place`. That set, not
61
61
  `placements`, is what the always-on placement proof reads.
62
62
 
@@ -117,7 +117,7 @@ after the click returns.
117
117
  - Take each frame after the paint settles. Reach for `waitForAnimations` on the element whose
118
118
  transition was running ([layer.md](layer.md) → The waits); a frame shot mid-transition pictures a
119
119
  state the interface never rests in.
120
- - Regenerate the whole matrix from the journeys after any surface change. Never judge a round
120
+ - Regenerate the whole matrix from the journeys after any screen change. Never judge a round
121
121
  against a portfolio that is part old and part new.
122
122
  - Route review of the portfolio to the `orkestrel-polish` campaign, which owns preflight,
123
123
  verdicts, and reconciliation. This reference owns only how the journeys generate it.
@@ -3,12 +3,13 @@
3
3
  Route a question by what judges the claim, before spending a round on it. Report a question no
4
4
  instrument here answers as open, and never answer it with the nearest instrument instead.
5
5
 
6
- | The claim is judged by | Route it to |
7
- | -------------------------------------- | -------------------------------------------- |
8
- | A compiler, a linter, or a Node runner | The `prove` tool, with its negative control |
9
- | A person's eye | The run's written artifact, named by variant |
10
- | A person watching a widget move | The harness run's frames and its artifact |
11
- | The browser's own resolved value | The matrix family ([styles.md](styles.md)) |
6
+ | The claim is judged by | Route it to |
7
+ | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
8
+ | A compiler, a linter, or a Node runner | The `prove` tool, with its negative control |
9
+ | A person's eye | The run's written artifact, named by variant |
10
+ | A person watching a widget move | The harness run's frames and its artifact |
11
+ | The browser's own resolved value | The matrix family ([styles.md](styles.md)) |
12
+ | A flow a model drove through the browser toolset | A recorded journey replayed through `@orkestrel/browser`, judged by the run's steps ([recorded.md](recorded.md)), never by the transcript |
12
13
 
13
14
  Never ask `prove` about pixels, and never ask a screenshot about types.
14
15
 
@@ -53,8 +54,8 @@ so a decision cites the exact file and reads it in one call.
53
54
 
54
55
  Compose each file from what the run already holds:
55
56
 
56
- - `describeTree` of the mounted surface, for the roles, names, and states a person meets.
57
- - `describeFocus` of the mounted surface, for the focus order the keyboard walk took.
57
+ - `describeTree` of the mounted screen, for the roles, names, and states a person meets.
58
+ - `describeFocus` of the mounted screen, for the focus order the keyboard walk took.
58
59
  - The resolved-style rows the matrix family read for that variant — the property, the element, and
59
60
  the value the browser returned.
60
61
  - The journal's `steps` and `output`, for what the run did and what the page said while it did it.
@@ -66,7 +67,7 @@ Rules the artifact obeys:
66
67
  combination nobody can reproduce.
67
68
  - Write one file per variant, never one file per journey. A decision is taken per variant, and a
68
69
  reader opening one file per journey pays a round trip per journey.
69
- - Regenerate the whole set after any surface change. Never judge a round against a set that is part
70
+ - Regenerate the whole set after any screen change. Never judge a round against a set that is part
70
71
  old and part new.
71
72
  - Keep it out of version control.
72
73
 
@@ -185,13 +185,13 @@ the capture voices in [captures.md](captures.md).
185
185
 
186
186
  - Report an absent control and a present-but-unreachable one as different findings: absence names
187
187
  a missing control, and unreachability names the interface gating one that exists.
188
- - Report ambiguity as a finding about the surface. Quote the match count from the message, and
188
+ - Report ambiguity as a finding about the screen. Quote the match count from the message, and
189
189
  re-target the journey by role or region.
190
190
  - Assert a `DOMException` on its `name` as well as its message. A withheld storage operation raises
191
191
  `SecurityError` and a write past the quota raises `QuotaExceededError`, which is the pair a denied
192
192
  or full origin raises.
193
193
  - Never assert a `could not be resolved` sentence. Each one is narrowing the resolver needs under
194
- `noUncheckedIndexedAccess`, and no surface reaches it.
194
+ `noUncheckedIndexedAccess`, and no screen reaches it.
195
195
  - Read a refused bound as a test defect rather than a finding. The wait family validates its budget
196
196
  and its interval and names the argument the caller passed, under the subject `Wait` or
197
197
  `Animation`.
@@ -211,7 +211,7 @@ the capture voices in [captures.md](captures.md).
211
211
  `fillAccessible` where the text is only a payload the person pastes.
212
212
  - Bring focus about first — through `traverseAccessible`, `clickAccessible`, or `typeAccessible` —
213
213
  and then press. The refusal is what `pressKeys` adds over the provider's own keyboard call: a key
214
- sent to the body reaches no control, and every assertion after it reads a surface the key never
214
+ sent to the body reaches no control, and every assertion after it reads a screen the key never
215
215
  touched.
216
216
  - Escape the sequence yourself where the sequence is the subject. Reach for `typeAccessible` where
217
217
  the text is the subject and the key syntax is in the way.
@@ -245,7 +245,7 @@ the capture voices in [captures.md](captures.md).
245
245
  - Pass `absent: true` to `waitForState` where the claim is that the state went away, and read the
246
246
  returned states rather than taking a second reading afterwards.
247
247
  - Take `waitForState` as the published replacement for a settle keyed to a framework's own class
248
- names. Where the control announces nothing, the finding is the surface's: give it `aria-expanded`,
248
+ names. Where the control announces nothing, the finding is the screen's: give it `aria-expanded`,
249
249
  `aria-pressed`, or `aria-busy` rather than reading the classes a stylesheet happens to use.
250
250
  - Take every style, contrast, and capture reading after `waitForAnimations` on the element whose
251
251
  paint was moving. A reading taken mid-transition reports an interpolated frame no state of the
@@ -274,7 +274,7 @@ the capture voices in [captures.md](captures.md).
274
274
  locators do not, because the platform exposes a native disclosure rather than a role.
275
275
  - Drive an ARIA disclosure as what it is: a button. Click it with `clickAccessible`, then settle it
276
276
  with `waitForState` on the state it announces.
277
- - Report a control that opens a panel and announces no state as a surface finding. Author
277
+ - Report a control that opens a panel and announces no state as a screen finding. Author
278
278
  `aria-expanded` on it rather than settling on the classes its framework toggles.
279
279
  - Read a native summary's expansion with `readStates`, which reads the parent `details` element's
280
280
  own `open` where the summary declares no `aria-expanded`.
@@ -0,0 +1,77 @@
1
+ # Recorded browser journeys
2
+
3
+ Judge a journey a model recorded through `@orkestrel/browser` by the steps its replay ran, never by
4
+ the transcript of the session that recorded it. Translate those steps into the journey layer before
5
+ a run of them counts toward a family this skill declares.
6
+
7
+ ## The vocabulary
8
+
9
+ ```ts
10
+ import { waitForText } from '@orkestrel/test'
11
+ import {
12
+ clickAccessible,
13
+ fillAccessible,
14
+ pressKeys,
15
+ readPage,
16
+ readPerception,
17
+ } from '@orkestrel/test/browser'
18
+ ```
19
+
20
+ ## What a recording holds
21
+
22
+ - Read a recorded journey as one user intent kept as JSON: a `name`, a one-sentence `description`,
23
+ the `parameters` it declares, and `steps` that mirror the browser toolset's own tool calls.
24
+ - Take an acting step's target from its role and exact accessible name. Read its record-time
25
+ `reference` and its `css` selector only as evidence for a refused resolution; a replay reads
26
+ neither, and a translated step carries neither.
27
+ - Name each journey tool for its act: `record` starts a recording, `save` keeps it under its name,
28
+ `journeys` reads the listing, `edit` changes steps by id, `replay` runs, and `forget` discards the
29
+ journey with its runs.
30
+ - Find a `browse` server's files under `tmp/browsers/`: the journey at `NAME/journey.json`, each
31
+ run at `NAME/runs/ID/run.json` beside that run's step captures, and the server's browser profile
32
+ under `.profiles/`. `NAME` is the journey's name and `ID` is the run id the store minted.
33
+ - Register a `browse` server per `.claude/rules/quality.md` § Instruments.
34
+
35
+ ## Translate a step
36
+
37
+ Translate each step through the following table. [layer.md](layer.md) owns each verb's contract.
38
+
39
+ | Recorded step | Journey layer |
40
+ | --------------------------------------------------------- | ---------------------------------------------------------------------------- |
41
+ | `click` | `clickAccessible(role, name)` with the step's role and name |
42
+ | `type` | `fillAccessible(name, text)`, refused for a `combobox` or a `listbox` target |
43
+ | `press` | `pressKeys(keys)`, with the chord translated |
44
+ | `wait` | `waitForText(description, read, text)` |
45
+ | `navigate`, `switch`, `dialog`, a page tool, `unresolved` | Refused |
46
+
47
+ - Refuse a `type` step whose target is a `combobox` or a `listbox`. The toolset selected an option
48
+ there, and `fillAccessible` replaces a field's text.
49
+ - When a `type` step carries `submit`, follow the fill with `pressKeys('{Enter}')` on the same field.
50
+ - Translate a `press` chord into the provider's key syntax: hold each modifier with `{Name>}`, send
51
+ the terminal key, and release the modifiers with `{/Name}` in reverse order. `Enter` becomes
52
+ `{Enter}`, and `Control+Shift+P` becomes `{Control>}{Shift>}P{/Shift}{/Control}`.
53
+ - Scope the reader a `wait` step passes to `waitForText` per [layer.md](layer.md) § The waits:
54
+ `readPerception` over the region the sentence lands in, and `readPage` only where the claim is
55
+ about the whole page.
56
+ - Refuse `navigate`: reach the page through the visible link or control that navigates, per
57
+ [layer.md](layer.md) § The named bans. Refuse `switch`, `dialog`, a page tool, and an `unresolved`
58
+ step, and report each as a step the layer has no verb for.
59
+
60
+ ## Judge a run
61
+
62
+ - Feed a run's `steps` and `output` into the variant artifact ([decide.md](decide.md) § The
63
+ rendered artifact) as automation evidence, labelled with the journey name and the run id.
64
+ - Discharge none of this skill's laws with a run until the workspace holds a trusted-input adapter
65
+ that drives the steps through the published verbs. A family stays open until a journey test
66
+ drives the translated steps.
67
+ - Read a run whose `outcome` is `stopped` or `aborted` as the step it stopped at, and never as the
68
+ journey's outcome.
69
+
70
+ ## Review a recording
71
+
72
+ Review a model's recording before a replay or a translation. The recorder keeps every action that
73
+ completed, so the recording carries each detour and each repeated submission the model made.
74
+
75
+ 1. Read the listing `journeys` returns for the journey.
76
+ 2. Remove each detour and each repeated step through `edit` with the `remove` operation, by step id.
77
+ 3. Discard a recording that does not carry the intent through `forget`, and record it again.
@@ -36,7 +36,7 @@ import {
36
36
  `assert`. Each phase receives the context and the part of the transition it owns.
37
37
  - Put the scenarios and the table in the workspace's browser test setup module, and import them from
38
38
  there.
39
- - Declare a transition for every event the surface accepts in every state it accepts it, including
39
+ - Declare a transition for every event the screen accepts in every state it accepts it, including
40
40
  the event that leaves the state unchanged. A table that lists only the moves the happy path takes
41
41
  proves the happy path.
42
42
  - Write the phases as module functions the whole table shares. Each phase reads its subject from its
@@ -184,7 +184,7 @@ it('walks the disclosure statechart', async () => {
184
184
  - Drive `act` through the journey verbs — `clickAccessible`, `clickDisclosure`, `typeAccessible`,
185
185
  `traverseAccessible`, `pressKeys` — for every transition a person can cause.
186
186
  - Drive `act` through the entity's own API only where the transition is the entity's rather than the
187
- person's: a lifecycle event, a transport reply, a timer the surface owns.
187
+ person's: a lifecycle event, a transport reply, a timer the screen owns.
188
188
  - Say which door each row used, in the row's `name`. A table that mixes the doors silently reads as
189
189
  a set of user transitions and proves something else.
190
190
 
@@ -286,7 +286,7 @@ import it.
286
286
 
287
287
  - Name only the attribute contract such a page must honour: the names in `STATECHART_ATTRIBUTES`,
288
288
  the readings in `STATECHART_STATUSES`, and the tally on one root node.
289
- - Report the page itself as the repository owner's decision — which transitions a surface owes,
289
+ - Report the page itself as the repository owner's decision — which transitions a screen owes,
290
290
  where the page is linked, and whether it ships at all.
291
291
  - Route a person who must watch the widget move to the harness run's own frames and its written
292
292
  artifact ([decide.md](decide.md) → The harness run), and name a deep link only where the workspace
@@ -1,6 +1,6 @@
1
1
  # Proving what the browser resolved
2
2
 
3
- Prove a style from what the browser resolved on the mounted surface. The `enterprise-bootstrap`
3
+ Prove a style from what the browser resolved on the mounted screen. The `enterprise-bootstrap`
4
4
  skill's [instruments reference](../../enterprise-bootstrap/references/inspection.md) names each
5
5
  instrument's property, its population, and its coverage. Take those from there, the reading from
6
6
  here, and the control from the builder this layer publishes for it.
@@ -75,12 +75,12 @@ readings follow.
75
75
  reads the whole matrix, where the capture family renders one variant per run.
76
76
  - Compose each variant's `apply` in the test, and apply it with the variant's `width` and `height`
77
77
  before the readings. Take every reading for that variant before moving to the next.
78
- - Name the attribute the surface actually reads in `apply`; a Bootstrap surface switches on
78
+ - Name the attribute the screen actually reads in `apply`; a Bootstrap screen switches on
79
79
  `data-bs-theme`. An `apply` that sets another attribute leaves the run in the default theme, where
80
80
  every reading passes.
81
- - Reach for the application's own theme control where the surface ships one, and assert the state it
81
+ - Reach for the application's own theme control where the screen ships one, and assert the state it
82
82
  announces. Setting the attribute directly proves the stylesheet; driving the control proves the
83
- surface.
83
+ screen.
84
84
  - Assert that the run read every declared variant. A matrix that silently walked one variant reports
85
85
  a pass for the theme nobody exercised.
86
86
  - Report which variants a result covers beside it. A pairing that appears only in a state the run
@@ -102,7 +102,7 @@ provided.
102
102
  `pressKeys`, or a real click. Pass `worn` where the chrome is painted onto a second element such
103
103
  as a label. It reports `undefined` for a control not matching `:focus-visible`, for the browser's
104
104
  own automatic ring, and for a focus style that only repaints the fill — treat each as a finding
105
- about the surface rather than as a pass.
105
+ about the screen rather than as a pass.
106
106
  - Reach for `measureContrast`, `measureLuminance`, `blendColor`, `readLayers`, and `readBackdrop`
107
107
  only where the composite itself is the subject. Never re-derive `readContrast` from them.
108
108
 
@@ -117,7 +117,7 @@ Take each reading's control from the builder this layer publishes for it.
117
117
  | `extractStyles` | `buildEscapes(permitted)` | An inline declaration, an embedded `<style>` element, and the sheet the id exempts |
118
118
  | `readCensus` | `buildCensus()` | An HTML token and an SVG token, neither declared by any loaded stylesheet |
119
119
 
120
- - Append each control's `root` to the same surface root the reading walks, take the reading, and
120
+ - Append each control's `root` to the same screen root the reading walks, take the reading, and
121
121
  remove it afterwards. Every builder returns detached nodes and mounts nothing, so where the
122
122
  control is read is the caller's decision.
123
123
  - Assert on the fields the builder returns rather than on a token or a selector written down in the
@@ -134,12 +134,12 @@ Take each reading's control from the builder this layer publishes for it.
134
134
  Take the property, the population, and the coverage from the instruments reference → Authored class
135
135
  in the shipped cascade. This is the reading.
136
136
 
137
- - Read the census with `readCensus(root)`, which walks the mounted surface, reports `elements` as
137
+ - Read the census with `readCensus(root)`, which walks the mounted screen, reports `elements` as
138
138
  the population it read, lists every `tokens` value the markup carries, and lists as `undeclared`
139
139
  the tokens no loaded stylesheet declares. It refuses a walk that read no element.
140
140
  - Assert on `elements` as well as on `undeclared`. An empty walk reports no undeclared token, and so
141
141
  does markup whose every class the cascade declares.
142
- - Take `root` from the mounted surface, so the census covers what rendered rather than what a
142
+ - Take `root` from the mounted screen, so the census covers what rendered rather than what a
143
143
  template file spells.
144
144
  - Read `readClasses` and `readCascade` directly only where one side of the difference is the
145
145
  subject. `readCensus` is the reading, and re-deriving it drops the population it reports.
@@ -152,7 +152,7 @@ Take the property, the population, the named exemptions, and the coverage from t
152
152
  reference → Style escapes. This is the reading.
153
153
 
154
154
  - Read escapes with `extractStyles(root)`, which returns the markup of every hit it found.
155
- - Take the reading before any journey drives the surface, because the population is the undriven
155
+ - Take the reading before any journey drives the screen, because the population is the undriven
156
156
  tree.
157
157
  - Append `buildEscapes(permitted).root` to that same `root`, so the control reaches the reading
158
158
  through `extractStyles`.
@@ -57,6 +57,10 @@ online audit reports the floor-restored files as stale until the release. The `-
57
57
  skips the catalog step and exits `1` with a note naming that refusal, so run the full
58
58
  `scaffold overwrite` after the release.
59
59
 
60
+ When the target is the `@orkestrel/scaffold` package, expect the runner to record the overwrite step
61
+ as skipped and to read the verify audit's foreign-path findings as expected; never run the `scaffold repair` or `scaffold overwrite` command
62
+ against that checkout.
63
+
60
64
  Run visits in parallel slices of disjoint repositories, each slice strictly serial inside itself,
61
65
  reporting per target. Refuse a failed target, name it, repair it, and re-run it alone.
62
66
 
@@ -6,8 +6,11 @@
6
6
  // re-pin every @orkestrel range to the registry caret and install), commit (`git commit --only` of
7
7
  // the manifest and lockfile as the preparation commit), overwrite (`scaffold overwrite --json`, then
8
8
  // `scaffold audit` exiting 0; offline, an exit of 1 whose note names the catalog refusal is
9
- // accepted), verify (every declared range matches the registry), install, pins (the self-pin sweep
10
- // with --prior or the manifest version and every range the visit moved; hits are reported, not
9
+ // accepted; on @orkestrel/scaffold itself both are skipped with a note, because the vendored host
10
+ // is staged from that checkout), verify (`scaffold audit --json` prints the releases envelope and
11
+ // every declared range matches the registry; on @orkestrel/scaffold itself an audit exit of 1 is
12
+ // read as the expected foreign-path findings of its own canonical sources), install,
13
+ // pins (the self-pin sweep with --prior or the manifest version and every range the visit moved; hits are reported, not
11
14
  // fatal), format, gates (format:check, lint:check, check, build, test), and compare (the rebuilt
12
15
  // dist against the published tarball, and the final runtime dependency set against the published
13
16
  // manifest `npm view NAME --json` serves; both readings are reported, not fatal). The summary
@@ -46,6 +49,9 @@ const RUNTIME_FIELDS: readonly string[] = ['dependencies', 'peerDependencies']
46
49
  const RANGE_FIELDS: readonly string[] = [...RUNTIME_FIELDS, 'devDependencies']
47
50
  const CATALOG = '.claude/agents/orkestrel.md'
48
51
  const OFFLINE_REFUSAL = "'catalog' does not take --offline"
52
+ // The vendored host is staged from this package's checkout, so overwriting it from the host deletes
53
+ // and replaces its own canon.
54
+ const SCAFFOLD = '@orkestrel/scaffold'
49
55
  const VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u
50
56
  // The checkout runs the `.ts` sources and a built twin under `dist/agents` runs `.js` siblings.
51
57
  const EXTENSION = extname(fileURLToPath(import.meta.url))
@@ -358,6 +364,7 @@ function runVerify(
358
364
  scaffoldFile: string,
359
365
  scaffoldPrefix: readonly string[],
360
366
  offline: readonly string[],
367
+ own: boolean,
361
368
  ): void {
362
369
  const audit = runStep(
363
370
  runner,
@@ -367,6 +374,10 @@ function runVerify(
367
374
  runner.target,
368
375
  )
369
376
  if (audit === undefined) return
377
+ // The audit reads the canonical sources of scaffold's own checkout as foreign paths and exits 1.
378
+ if (own && audit.status === 1) {
379
+ replaceLastStep(runner, 0, 'foreign-path findings are expected on the scaffold checkout')
380
+ }
370
381
  const releases = readReleases(audit.stdout)
371
382
  if (releases === undefined) {
372
383
  replaceLastStep(
@@ -490,10 +501,17 @@ function runVisit(argv: readonly string[]): Visit | undefined {
490
501
  if (selected.has('pin')) runPin(runner, scaffoldFile, scaffoldPrefix, offline)
491
502
  if (selected.has('commit') && !failed(runner)) runCommit(runner)
492
503
  if (selected.has('overwrite') && !failed(runner)) {
493
- runOverwrite(runner, scaffoldFile, scaffoldPrefix, offline)
504
+ if (name === SCAFFOLD) {
505
+ recordStep(
506
+ runner,
507
+ 'overwrite',
508
+ describeCommand(scaffoldFile, [...scaffoldPrefix, 'overwrite', '--json', ...offline]),
509
+ `skipped with its audit: ${SCAFFOLD} is the package whose checkout the vendored host is staged from`,
510
+ )
511
+ } else runOverwrite(runner, scaffoldFile, scaffoldPrefix, offline)
494
512
  }
495
513
  if (selected.has('verify') && !failed(runner)) {
496
- runVerify(runner, scaffoldFile, scaffoldPrefix, offline)
514
+ runVerify(runner, scaffoldFile, scaffoldPrefix, offline, name === SCAFFOLD)
497
515
  }
498
516
  if (selected.has('install') && !failed(runner)) runNpmStep(runner, 'install', ['install'])
499
517
  if (selected.has('pins') && !failed(runner)) {