@orkestrel/scaffold 0.0.73 → 0.0.75

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 (34) hide show
  1. package/dist/bin/main.js +75 -34
  2. package/dist/bin/main.js.map +1 -1
  3. package/dist/host/agents/skills/orkestrel-prove-journey/SKILL.md +212 -29
  4. package/dist/host/agents/skills/orkestrel-prove-journey/references/captures.md +40 -15
  5. package/dist/host/agents/skills/orkestrel-prove-journey/references/decide.md +34 -15
  6. package/dist/host/agents/skills/orkestrel-prove-journey/references/layer.md +248 -52
  7. package/dist/host/agents/skills/orkestrel-prove-journey/references/statechart.md +255 -49
  8. package/dist/host/agents/skills/orkestrel-prove-journey/references/styles.md +111 -23
  9. package/dist/host/agents/transports/codex.md +7 -0
  10. package/dist/host/claude/agents/orkestrel.md +2 -2
  11. package/dist/host/claude/rules/documentation.md +2 -0
  12. package/dist/host/claude/rules/tests.md +5 -3
  13. package/dist/host/claude/rules/workspace.md +26 -15
  14. package/dist/host/codex/config.toml +4 -7
  15. package/dist/host/guides/README.md +5 -0
  16. package/dist/host/guides/scaffold.md +104 -14
  17. package/dist/host/guides/test.md +744 -184
  18. package/dist/host/manifest.json +19 -19
  19. package/dist/host/tests/config.test.ts +159 -64
  20. package/dist/host/tests/policy.test.ts +11 -1
  21. package/dist/host/tests/setupPolicy.ts +571 -7
  22. package/dist/src/core/index.cjs +112 -18
  23. package/dist/src/core/index.cjs.map +1 -1
  24. package/dist/src/core/index.d.cts +19 -6
  25. package/dist/src/core/index.d.ts +19 -6
  26. package/dist/src/core/index.js +112 -19
  27. package/dist/src/core/index.js.map +1 -1
  28. package/dist/src/server/index.cjs +6 -4
  29. package/dist/src/server/index.cjs.map +1 -1
  30. package/dist/src/server/index.d.cts +9 -7
  31. package/dist/src/server/index.d.ts +9 -7
  32. package/dist/src/server/index.js +6 -4
  33. package/dist/src/server/index.js.map +1 -1
  34. package/package.json +2 -2
@@ -3,18 +3,92 @@
3
3
  Route every journey step through the published layer. Treat a journey that works around a missing
4
4
  capability by reaching for a selector as a layer defect.
5
5
 
6
+ ## The vocabulary
7
+
8
+ Import the vocabulary from the entries that publish it: `@orkestrel/test` for what is
9
+ host-independent, and `@orkestrel/test/browser` for what drives a browser. Verify against the
10
+ installed entry any name this reference or its siblings do not carry in a fence.
11
+
12
+ ```ts
13
+ import type { TextWaitOptions, WaitOptions } from '@orkestrel/test'
14
+ import { waitForText } from '@orkestrel/test'
15
+ import type { StateOptions, StorageOptions, WebStorageInterface } from '@orkestrel/test/browser'
16
+ import {
17
+ ACCESSIBLE_ROLES,
18
+ FOCUSABLE_SELECTOR,
19
+ build,
20
+ clearStorage,
21
+ clickAccessible,
22
+ clickAccessibleWithin,
23
+ clickDisclosure,
24
+ commitInput,
25
+ createDragEvent,
26
+ createJournal,
27
+ createPointerEvent,
28
+ createStorage,
29
+ describeFocus,
30
+ describeTree,
31
+ fillAccessible,
32
+ isOutsideViewport,
33
+ isReachable,
34
+ isRendered,
35
+ mount,
36
+ pressKeys,
37
+ readFocus,
38
+ readHit,
39
+ readName,
40
+ readPage,
41
+ readPerception,
42
+ readRefusal,
43
+ readRole,
44
+ readStates,
45
+ readText,
46
+ readValue,
47
+ removeDatabase,
48
+ render,
49
+ resolveAccessible,
50
+ resolveRendered,
51
+ traverseAccessible,
52
+ typeAccessible,
53
+ typeInput,
54
+ waitForAnimations,
55
+ waitForFrame,
56
+ waitForState,
57
+ } from '@orkestrel/test/browser'
58
+ ```
59
+
6
60
  ## Import, never implement
7
61
 
8
- Import every journey helper from `@orkestrel/test/browser`. Write one of your own only where that
9
- package publishes none for the act.
62
+ Import every journey helper from `@orkestrel/test/browser`, and every host-independent wait and
63
+ table type from `@orkestrel/test`. Write one of your own only where those entries publish none for
64
+ the act.
10
65
 
11
66
  - Place a helper you write in the workspace's browser test setup module, export it from there, and
12
67
  name it for the human act it performs.
13
- - Read the package's own exports before writing anything. A helper that renames a published one is a
14
- defect under `AGENTS.md`, and a second implementation of one drifts from the first.
68
+ - Read the package's own exports before writing anything, under `AGENTS.md` § Design laws.
15
69
  - Code every journey against the vocabulary in this file, which is the published one. Diagnose a
16
70
  target that stops resolving here, and fix it in the application.
17
71
 
72
+ Prove that setup module with `tests/setupBrowser.test.ts`. A workspace born with the browser setup
73
+ runtime carries the `setup:browser` project, the `test:setup:browser` script, and that script's
74
+ place in the `test` chain. A workspace that acquires the runtime later activates the project in this
75
+ order:
76
+
77
+ 1. Write `tests/setupBrowser.test.ts`. Writing that file selects the browser setup runtime.
78
+ 2. Add `npm run test:setup:browser` to the `test` chain.
79
+ 3. Run `scaffold repair`, which registers the browser-enabled `setup:browser` project and appends
80
+ the `test:setup:browser` script.
81
+
82
+ Run `scaffold repair` before the chain invocation and it refuses the `configs` group:
83
+
84
+ ```text
85
+ The configs group is blocked because the manifest at <target> does not reach a Vitest project the planned configuration registers: setup:browser. No chain from test invokes it. test:setup:browser is not declared, so the script is missing as well as the gate: declare it and invoke it by name from the test chain. Exclude configs from --groups to write another group.
86
+ ```
87
+
88
+ Add the invocation and run `scaffold repair` again. The Node `setup` project excludes
89
+ `tests/setupBrowser.test.ts`, so a proof of a browser helper placed anywhere else runs without a
90
+ browser.
91
+
18
92
  ## What it drives
19
93
 
20
94
  - Drive the real browser through the installed Vitest browser provider. The published verbs import
@@ -23,8 +97,9 @@ package publishes none for the act.
23
97
  - Never dispatch a constructed event from a journey. The published `createPointerEvent`,
24
98
  `createDragEvent`, `typeInput`, and `commitInput` serve a unit test whose subject is the handler;
25
99
  a journey drives input through `clickAccessible`, `clickAccessibleWithin`, `clickDisclosure`,
26
- `typeAccessible`, `fillAccessible`, and `traverseAccessible`, and sends a bare key sequence — Enter,
27
- Escape, arrows, modifiers, and combinations — with `userEvent.keyboard` from `vitest/browser`.
100
+ `typeAccessible`, `fillAccessible`, `traverseAccessible`, and `pressKeys`.
101
+ - Send every key sequence — Enter, Escape, arrows, modifiers, and combinations — through
102
+ `pressKeys`, which is the published verb for it.
28
103
  - Yield with `waitForFrame` where a step needs the browser to paint before the next reading. Never
29
104
  guard a fact with a fixed delay.
30
105
 
@@ -34,15 +109,20 @@ A journey verb resolves its own target from role and accessible name, and refuse
34
109
  component instance, or a selector from the caller. A reader, a fixture builder, and a capture each
35
110
  take one, because their subject is a node the caller already holds.
36
111
 
37
- | Population | Takes an element |
38
- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
39
- | `resolveAccessible`, `resolveRendered`, `clickAccessible`, `clickAccessibleWithin`, `clickDisclosure`, `typeAccessible`, `fillAccessible`, `traverseAccessible`, `readPerception`, `readPage`, `readFocus`, `readValue` | No |
40
- | `readText`, `readRole`, `readName`, `readStates`, `describeTree`, `describeFocus`, `isReachable`, `isRendered` | Yes |
41
- | `readContrast`, `readRing`, `readLayers`, `readBackdrop`, `readStyle`, `readToken`, `readPixels`, `readClasses`, `extractStyles`, `extractOrphans`, `readRows` | Yes |
42
- | `mount`, `typeInput`, `commitInput`, `captureFrame`, and a portfolio's `place` | Yes |
43
-
44
- Never pass an element to a verb from a journey step. Read a step that would pass one as a missing
45
- verb, and add the verb instead.
112
+ | Population | Takes an element |
113
+ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
114
+ | `resolveAccessible`, `resolveRendered`, `clickAccessible`, `clickAccessibleWithin`, `clickDisclosure`, `typeAccessible`, `fillAccessible`, `traverseAccessible`, `pressKeys`, `waitForState`, `readRefusal`, `readPerception`, `readPage`, `readFocus`, `readValue` | No |
115
+ | `readText`, `readRole`, `readName`, `readStates`, `describeTree`, `describeFocus`, `isReachable`, `isRendered`, `readHit`, `waitForAnimations` | Yes |
116
+ | `readContrast`, `readRing`, `readLayers`, `readBackdrop`, `readStyle`, `readToken`, `readPixels`, `readClasses`, `readCensus`, `extractStyles`, `extractOrphans`, `readRows` | Yes |
117
+ | `mount`, `typeInput`, `commitInput`, `captureFrame`, and a portfolio's `place` | Yes |
118
+
119
+ - Never pass an element to a verb from a journey step. Read a step that would pass one as a missing
120
+ verb, and add the verb instead.
121
+ - `waitForState` resolves its control afresh on every reading, so it takes the role and the name
122
+ rather than a node. `waitForAnimations` takes the element, because an animation belongs to a
123
+ subtree rather than to a name.
124
+ - `createStorage` and `clearStorage` take neither: one returns a store the test hands the
125
+ application, and the other empties the browser's own surfaces.
46
126
 
47
127
  ## The resolver
48
128
 
@@ -59,35 +139,168 @@ act itself scrolls into view.
59
139
  honours opacity and CSS, has a box with non-zero width and height, carries a `tabIndex` of at
60
140
  least zero, matches neither `:disabled` nor `[aria-disabled="true"]`, and has no `[inert]`
61
141
  ancestor.
142
+ - Read a `true` from either predicate for a subject inside a shadow root as the element's own
143
+ answer. Each asks `closest` for the `aria-hidden` ancestor and the `[inert]` ancestor, and
144
+ `closest` never crosses a shadow boundary, so ask the host separately where the ancestor attribute
145
+ is the subject.
62
146
  - Count a control a person can scroll to as reachable, and one that stays outside the viewport after
63
- the scroll as unreachable.
147
+ the scroll as unreachable. Reach for `isOutsideViewport` where the rectangle itself is the
148
+ subject.
149
+ - Ask `readHit` where the question is what paints at a control's centre. A node it returns is no
150
+ proof of a cover: a `pointer-events: none` cover is absent from the hit test, and an element in a
151
+ shadow tree retargets to its host.
64
152
 
65
153
  ### The failure voices
66
154
 
67
- Assert the one voice the case means. Never write an assertion that accepts more than one.
68
-
69
- | Condition | The voice thrown | Thrown by |
70
- | ------------------------------------------------ | ------------------------------------------------------------------------------------- | ----------------------- |
71
- | No element carries the name | `No interactive element has the accessible name "<name>"` | `resolveRendered` |
72
- | Every match fails a reachability condition | `Interactive target "<name>" is not visible and focus-reachable` | `resolveRendered` |
73
- | Several matches are reachable | `Interactive target "<name>" is ambiguous across <n> elements` | `resolveRendered` |
74
- | Still off-viewport after being scrolled to | `Interactive target "<name>" is unreachable after scrolling` | `resolveAccessible` |
75
- | The region holds no reachable match | `Interactive target "<name>" is not reachable inside "<region>"` | `clickAccessibleWithin` |
76
- | The region holds several reachable matches | `Interactive target "<name>" is ambiguous across <n> elements inside "<region>"` | `clickAccessibleWithin` |
77
- | No native disclosure is reachable under the name | `Native disclosure "<name>" is not visible and focus-reachable` | `clickDisclosure` |
78
- | Several native disclosures carry the name | `Native disclosure "<name>" is ambiguous across <n> elements` | `clickDisclosure` |
79
- | Forward Tab never lands on the target | `Interactive target "<name>" is not reachable through forward Tab traversal: <trail>` | `traverseAccessible` |
80
- | The named region is hidden | `Named region "<name>" is not visible` | `readPerception` |
81
- | Several named regions carry the name | `Named region "<name>" is ambiguous across <n> elements` | `readPerception` |
82
- | The resolved control renders no value | `Interactive target "<name>" does not carry a value` | `readValue` |
155
+ Assert the one voice the case means. Never write an assertion that accepts more than one. This table
156
+ carries this layer's own voices. Read the statechart voices in [statechart.md](statechart.md) and
157
+ the capture voices in [captures.md](captures.md).
158
+
159
+ | Condition | The voice thrown | Thrown by |
160
+ | ------------------------------------------------------ | ---------------------------------------------------------------------------------------- | ----------------------- |
161
+ | No element carries the name | `No interactive element has the accessible name "<name>"` | `resolveRendered` |
162
+ | Every match fails a reachability condition | `Interactive target "<name>" is not visible and focus-reachable` | `resolveRendered` |
163
+ | Several matches are reachable | `Interactive target "<name>" is ambiguous across <n> elements` | `resolveRendered` |
164
+ | Still off-viewport after being scrolled to | `Interactive target "<name>" is unreachable after scrolling` | `resolveAccessible` |
165
+ | The region holds no reachable match | `Interactive target "<name>" is not reachable inside "<region>"` | `clickAccessibleWithin` |
166
+ | The region holds several reachable matches | `Interactive target "<name>" is ambiguous across <n> elements inside "<region>"` | `clickAccessibleWithin` |
167
+ | No native disclosure is reachable under the name | `Native disclosure "<name>" is not visible and focus-reachable` | `clickDisclosure` |
168
+ | Several native disclosures carry the name | `Native disclosure "<name>" is ambiguous across <n> elements` | `clickDisclosure` |
169
+ | Forward Tab never lands on the target | `Interactive target "<name>" is not reachable through forward Tab traversal: <trail>` | `traverseAccessible` |
170
+ | The named region is hidden | `Named region "<name>" is not visible` | `readPerception` |
171
+ | Several named regions carry the name | `Named region "<name>" is ambiguous across <n> elements` | `readPerception` |
172
+ | The resolved control renders no value | `Interactive target "<name>" does not carry a value` | `readValue` |
173
+ | A key sequence reached nothing | `Key sequence "<keys>" was sent with nothing focused` | `pressKeys` |
174
+ | The state never arrived or never left | `Condition "<subject>" did not hold within <n>ms (waited <n>ms) (last states: <states>)` | `waitForState` |
175
+ | The animation subject is in no document | `Animation subject is not connected` | `waitForAnimations` |
176
+ | The paint never stopped moving | `Animation "<subject>" did not settle within <n>ms (waited <n>ms): <animations>` | `waitForAnimations` |
177
+ | The sentence never arrived | `Condition "<description>" did not hold within <n>ms (waited <n>ms)` | `waitForText` |
178
+ | A host withholds `getItem`, `setItem`, or `removeItem` | `Access is denied for <operation> "<key>"` | `createStorage` |
179
+ | A host withholds `length`, `clear`, or `key` | `Access is denied for <operation>` | `createStorage` |
180
+ | A write ran past the declared quota | `No room is left for <key>` | `createStorage` |
181
+ | A style reading has no computed color | `Computed foreground color is unavailable` | `readContrast` |
182
+ | A class census walked nothing | `Class census walked no element` | `readCensus` |
183
+ | A contrast control cannot straddle its bar | `Contrast control cannot straddle the bar <bar>` | `buildContrast` |
184
+ | A database deletion was blocked | `IndexedDB database "<name>" is blocked by an open connection` | `removeDatabase` |
83
185
 
84
186
  - Report an absent control and a present-but-unreachable one as different findings: absence names
85
187
  a missing control, and unreachability names the interface gating one that exists.
86
188
  - Report ambiguity as a finding about the surface. Quote the match count from the message, and
87
189
  re-target the journey by role or region.
190
+ - Assert a `DOMException` on its `name` as well as its message. A withheld storage operation raises
191
+ `SecurityError` and a write past the quota raises `QuotaExceededError`, which is the pair a denied
192
+ or full origin raises.
193
+ - Never assert a `could not be resolved` sentence. Each one is narrowing the resolver needs under
194
+ `noUncheckedIndexedAccess`, and no surface reaches it.
195
+ - Read a refused bound as a test defect rather than a finding. The wait family validates its budget
196
+ and its interval and names the argument the caller passed, under the subject `Wait` or
197
+ `Animation`.
88
198
  - Read the package's own voice table before asserting a message this file does not list. A workspace
89
199
  that transcribes a voice by memory asserts a sentence the package does not throw.
90
200
 
201
+ ## Input and traversal
202
+
203
+ | Verb | Contract |
204
+ | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
205
+ | `typeAccessible(name, text)` | Focus the field, select all, delete, then send real keystrokes. Escape the provider's key syntax in the text. |
206
+ | `fillAccessible(name, text)` | Replace the value in one operation for text too long to type. The real element still publishes real input. |
207
+ | `pressKeys(keys)` | Send a key sequence to whatever holds focus, and refuse the sequence where the document body holds focus or nothing does. `{` opens a key name and `[` opens a code name. |
208
+ | `traverseAccessible(name)` | Move focus by forward Tab from wherever focus is, and return the target after focus lands on it. |
209
+
210
+ - Reach for `typeAccessible` where the keystrokes are part of what the journey claims, and
211
+ `fillAccessible` where the text is only a payload the person pastes.
212
+ - Bring focus about first — through `traverseAccessible`, `clickAccessible`, or `typeAccessible` —
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
215
+ touched.
216
+ - Escape the sequence yourself where the sequence is the subject. Reach for `typeAccessible` where
217
+ the text is the subject and the key syntax is in the way.
218
+ - Let `traverseAccessible` end the walk. It counts a step only where focus lands, stops at one
219
+ complete cycle of the tab order, carries the trail of what focus reached in its voice, and holds a
220
+ cap above the cycle so a page with no tab order fails instead of hanging.
221
+ - Never call the browser's focus method to place focus, and never hold a node reference across
222
+ traversal steps. A framework may replace the node between resolution and focus arrival.
223
+
224
+ ## The waits
225
+
226
+ | Wait | Parks on | Returns |
227
+ | ------------------------------------------------ | ------------------------------------------- | ----------------------------------- |
228
+ | `waitForText(description, read, text, options?)` | A reading the caller supplies | The first reading that satisfies it |
229
+ | `waitForState(name, state, options?)` | What one control announces | The states read at resolution |
230
+ | `waitForState(role, name, state, options?)` | The same, resolved within one exact role | The states read at resolution |
231
+ | `waitForAnimations(element, options?)` | Each running animation's `finished` promise | Nothing |
232
+ | `waitForFrame()` | One `requestAnimationFrame` | Nothing |
233
+
234
+ - Scope `waitForText`'s reader as narrowly as the claim. Read a named region through
235
+ `readPerception` for an arrival assertion, and reach for `readPage` only where the claim is about
236
+ the whole page. A wait over the page resolves on the sentence wherever it lands.
237
+ - Carry the replaced sentence in `absent` whenever a value replaces another. A screen swapping one
238
+ sentence for another passes through a frame carrying both, so a wait naming the arrival alone
239
+ resolves on the frame the departing sentence is still painted in.
240
+ - Pass `exact` where the reading must equal the sentence rather than contain it. An empty
241
+ expectation is refused rather than satisfied.
242
+ - Spell a state for `waitForState` the way `readStates` reports it: `expanded`, `collapsed`,
243
+ `disabled`, `current`, `invalid`, `checked`, `required`, `readonly`, `described`, `busy`, and the
244
+ valued forms `pressed=<value>`, `selected=<value>`, and `live=<value>`.
245
+ - Pass `absent: true` to `waitForState` where the claim is that the state went away, and read the
246
+ returned states rather than taking a second reading afterwards.
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`,
249
+ `aria-pressed`, or `aria-busy` rather than reading the classes a stylesheet happens to use.
250
+ - Take every style, contrast, and capture reading after `waitForAnimations` on the element whose
251
+ paint was moving. A reading taken mid-transition reports an interpolated frame no state of the
252
+ interface paints.
253
+ - Read a `waitForAnimations` timeout naming an animation that never finishes as a finding about the
254
+ reading rather than a budget to lengthen. It excludes an animation declaring infinite iterations,
255
+ a finished one filling its target, and a paused one, so a wait that resolves is a claim about the
256
+ paint that was moving.
257
+
258
+ ## Reading a refusal
259
+
260
+ `readRefusal(name)` and `readRefusal(role, name)` return the sentence `resolveRendered` raised, or
261
+ `undefined` where the target resolved. Each rethrows a value that is not an `Error`.
262
+
263
+ - Assert the exact sentence. A comparison against a substring passes for a refusal about a different
264
+ condition, and a comparison against `undefined` alone passes on every refusal the layer throws.
265
+ - Read `undefined` as the target being reachable to an acting verb, because this drives the same
266
+ resolver without acting.
267
+ - Never branch a journey on the reading. A journey performs its steps unconditionally and lets the
268
+ verb's own refusal name what the interface withheld.
269
+
270
+ ## Disclosures
271
+
272
+ - Drive a native `<summary>` with `clickDisclosure`, and only a native one. It applies the
273
+ resolver's reachability conditions, throws its own voices, and resolves what the provider's role
274
+ locators do not, because the platform exposes a native disclosure rather than a role.
275
+ - Drive an ARIA disclosure as what it is: a button. Click it with `clickAccessible`, then settle it
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
278
+ `aria-expanded` on it rather than settling on the classes its framework toggles.
279
+ - Read a native summary's expansion with `readStates`, which reads the parent `details` element's
280
+ own `open` where the summary declares no `aria-expanded`.
281
+
282
+ ## The named bans
283
+
284
+ Never reach past the interface in any of the following ways. Reach for the published replacement
285
+ beside each.
286
+
287
+ | Never | Reach for |
288
+ | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
289
+ | An element resolved by id or class | `resolveAccessible`, `resolveRendered`, and the acting verbs |
290
+ | A class-list read standing in for a settle | `waitForState`, `waitForAnimations` |
291
+ | `document.elementFromPoint` | `readHit` |
292
+ | `element.focus()` | `traverseAccessible`, `pressKeys` |
293
+ | A navigation a journey step performs by a router call | The visible link or control that navigates, through `clickAccessible` or `clickAccessibleWithin` |
294
+ | A store or route state read standing in for a rendered assertion | `readPerception`, `readValue`, `readStates`, `waitForText` |
295
+
296
+ - Admit a selector only for a population that carries no role, declared in the workspace's browser
297
+ test setup module with the reason beside it.
298
+ - Read a class-list assertion as proving nothing about the cascade. A class present in the markup
299
+ and absent from every loaded stylesheet resolves to nothing, and the assertion passes on it.
300
+ - Take a store read or a route state read as corroboration beside a rendered assertion, never in
301
+ place of one. Never take a router call as corroboration: it changes the route rather than
302
+ reporting it.
303
+
91
304
  ## Role vocabulary
92
305
 
93
306
  Never infer a role from markup. Confirm the computed role in the browser with `readRole` whenever a
@@ -99,9 +312,6 @@ target stops resolving, and read the exposed name with `readName` and the expose
99
312
  miss immediately after such a change as this before treating the element as missing.
100
313
  - Always target a tab by its role. A tab and its panel collide on a bare name by construction,
101
314
  because the panel is labelled by its tab.
102
- - Drive a `<summary>` with `clickDisclosure`, which applies the same reachability conditions and
103
- throws its own voices. The provider's role locators do not resolve it, because it is exposed as a
104
- native disclosure rather than through a role.
105
315
 
106
316
  ## Region-scoped resolution
107
317
 
@@ -109,23 +319,6 @@ Reach for `clickAccessibleWithin(region, role, name)` where a short verb such as
109
319
  across a page, and where a rendered status completes a control's accessible name. It applies the
110
320
  same reachability conditions inside the region and names the region in every voice it throws.
111
321
 
112
- ## Input and traversal
113
-
114
- | Verb | Contract |
115
- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
116
- | `typeAccessible(name, text)` | Focus the field, select all, delete, then send real keystrokes. Escape the provider's key syntax in the text. |
117
- | `fillAccessible(name, text)` | Replace the value in one operation for text too long to type. The real element still publishes real input. |
118
- | `userEvent.keyboard(keys)` | Send a key sequence to whatever holds focus, for Enter, Escape, arrows, modifiers, and combinations. Place focus with `traverseAccessible`, `clickAccessible`, or `typeAccessible` first. |
119
- | `traverseAccessible(name)` | Move focus by forward Tab from wherever focus is, and return the target after focus lands on it. |
120
-
121
- - Reach for `typeAccessible` where the keystrokes are part of what the journey claims, and
122
- `fillAccessible` where the text is only a payload the person pastes.
123
- - Let `traverseAccessible` end the walk. It counts a step only where focus lands, stops at one
124
- complete cycle of the tab order, carries the trail of what focus reached in its voice, and holds a
125
- cap above the cycle so a page with no tab order fails instead of hanging.
126
- - Never call the browser's focus method to place focus, and never hold a node reference across
127
- traversal steps. A framework may replace the node between resolution and focus arrival.
128
-
129
322
  ## Perception
130
323
 
131
324
  `readPerception(name)` returns the normalized `innerText` of exactly one visible named region,
@@ -149,6 +342,9 @@ dialog, table, tab panel, alert, or status. Quote that text in assertions.
149
342
  - Undo everything a journey changed after each test: unmount, destroy the session, reset the theme,
150
343
  return the route to its entry, and clear what the application persisted with `clearStorage` and
151
344
  `removeDatabase`. Never let a journey inherit the previous journey's state.
345
+ - Hand the application a `createStorage` store where the subject is what a host withholds, and reach
346
+ for `clearStorage` where the browser's own surfaces are the subject. The constructed store is
347
+ backed by a map of its own, patches neither browser surface, and dispatches no `storage` event.
152
348
  - Record what a journey did with `createJournal`, started inside the journey and stopped in a
153
349
  `finally`. Its `steps` and `output` are the evidence a failing journey hands back, and the input
154
350
  the run's written artifact composes.