@orkestrel/scaffold 0.0.103 → 0.0.105

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 (94) hide show
  1. package/dist/agents/skills/orkestrel-dispatch/scripts/helpers.js +84 -1
  2. package/dist/agents/skills/orkestrel-dispatch/scripts/launch.js +21 -6
  3. package/dist/agents/skills/orkestrel-harden/scripts/titles.js +338 -0
  4. package/dist/agents/skills/orkestrel-publish/scripts/swap.js +3 -39
  5. package/dist/agents/skills/orkestrel-publish/scripts/wave.js +433 -47
  6. package/dist/agents/skills/orkestrel-publish/scripts/window.js +2 -1
  7. package/dist/bin/main.js +17 -10
  8. package/dist/bin/main.js.map +1 -1
  9. package/dist/host/agents/orchestration.md +6 -6
  10. package/dist/host/agents/skills/enterprise-bootstrap/references/components.md +2 -2
  11. package/dist/host/agents/skills/enterprise-bootstrap/references/inspection.md +1 -1
  12. package/dist/host/agents/skills/orkestrel-build/SKILL.md +2 -2
  13. package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +2 -2
  14. package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +1 -1
  15. package/dist/host/agents/skills/orkestrel-debrief/references/instruction-audit.md +3 -3
  16. package/dist/host/agents/skills/orkestrel-dispatch/SKILL.md +10 -10
  17. package/dist/host/agents/skills/orkestrel-dispatch/references/launch.md +1 -0
  18. package/dist/host/agents/skills/orkestrel-dispatch/scripts/helpers.ts +100 -1
  19. package/dist/host/agents/skills/orkestrel-dispatch/scripts/launch.ts +27 -6
  20. package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +4 -4
  21. package/dist/host/agents/skills/orkestrel-falsify/agents/openai.yaml +1 -1
  22. package/dist/host/agents/skills/orkestrel-falsify/references/brief.md +8 -7
  23. package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +6 -6
  24. package/dist/host/agents/skills/orkestrel-harden/SKILL.md +6 -1
  25. package/dist/host/agents/skills/orkestrel-harden/references/bindings.md +1700 -0
  26. package/dist/host/agents/skills/orkestrel-harden/references/contract.md +2 -2
  27. package/dist/host/agents/skills/orkestrel-harden/references/equivalence.md +48 -0
  28. package/dist/host/agents/skills/orkestrel-harden/references/hardening.md +4 -2
  29. package/dist/host/agents/skills/orkestrel-harden/scripts/titles.ts +425 -0
  30. package/dist/host/agents/skills/orkestrel-journey/SKILL.md +2 -2
  31. package/dist/host/agents/skills/orkestrel-journey/references/captures.md +9 -8
  32. package/dist/host/agents/skills/orkestrel-parallel/SKILL.md +72 -0
  33. package/dist/host/agents/skills/orkestrel-parallel/agents/openai.yaml +4 -0
  34. package/dist/host/agents/skills/orkestrel-parallel/references/harnesses.md +12 -0
  35. package/dist/host/agents/skills/orkestrel-polish/SKILL.md +3 -3
  36. package/dist/host/agents/skills/orkestrel-publish/SKILL.md +2 -2
  37. package/dist/host/agents/skills/orkestrel-publish/references/release.md +1 -0
  38. package/dist/host/agents/skills/orkestrel-publish/references/wave.md +56 -13
  39. package/dist/host/agents/skills/orkestrel-publish/references/window.md +6 -6
  40. package/dist/host/agents/skills/orkestrel-publish/scripts/swap.ts +5 -49
  41. package/dist/host/agents/skills/orkestrel-publish/scripts/wave.ts +646 -49
  42. package/dist/host/agents/skills/orkestrel-publish/scripts/window.ts +2 -1
  43. package/dist/host/agents/transports/codex.md +1 -1
  44. package/dist/host/claude/agents/orkestrel.md +54 -53
  45. package/dist/host/claude/agents/reviewer.md +4 -4
  46. package/dist/host/claude/agents/verifier.md +1 -1
  47. package/dist/host/claude/rules/documentation.md +2 -1
  48. package/dist/host/claude/rules/patterns.md +1 -1
  49. package/dist/host/claude/rules/quality.md +2 -2
  50. package/dist/host/claude/rules/styles.md +1 -1
  51. package/dist/host/claude/rules/tests.md +34 -8
  52. package/dist/host/claude/rules/typescript.md +29 -0
  53. package/dist/host/claude/rules/workspace.md +32 -22
  54. package/dist/host/claude/rules/writing.md +34 -26
  55. package/dist/host/claude/settings.json +2 -0
  56. package/dist/host/claude/skills/orkestrel-debrief/SKILL.md +1 -1
  57. package/dist/host/claude/skills/orkestrel-falsify/SKILL.md +1 -1
  58. package/dist/host/claude/skills/orkestrel-parallel/SKILL.md +11 -0
  59. package/dist/host/codex/agents/analyst.toml +3 -3
  60. package/dist/host/codex/agents/reviewer.toml +4 -4
  61. package/dist/host/codex/agents/verifier.toml +3 -1
  62. package/dist/host/configs/policy.ts +13 -2
  63. package/dist/host/dotfiles/editorconfig +0 -1
  64. package/dist/host/dotfiles/gitattributes +0 -1
  65. package/dist/host/dotfiles/gitignore +0 -1
  66. package/dist/host/dotfiles/oxlintrc.json +24 -0
  67. package/dist/host/guides/agent.md +1674 -598
  68. package/dist/host/guides/budget.md +89 -29
  69. package/dist/host/guides/form.md +485 -99
  70. package/dist/host/guides/mcp.md +14 -2
  71. package/dist/host/guides/ollama.md +367 -86
  72. package/dist/host/guides/probe.md +1 -1
  73. package/dist/host/guides/scaffold.md +158 -101
  74. package/dist/host/guides/study.md +3185 -0
  75. package/dist/host/guides/table.md +239 -29
  76. package/dist/host/guides/terminal.md +22 -20
  77. package/dist/host/guides/test.md +776 -315
  78. package/dist/host/manifest.json +117 -65
  79. package/dist/host/tests/config.test.ts +136 -82
  80. package/dist/host/tests/policy.test.ts +41 -3
  81. package/dist/host/tests/setupPolicy.ts +87 -29
  82. package/dist/src/core/index.cjs +1389 -612
  83. package/dist/src/core/index.cjs.map +1 -1
  84. package/dist/src/core/index.d.cts +35 -20
  85. package/dist/src/core/index.d.ts +35 -20
  86. package/dist/src/core/index.js +1387 -613
  87. package/dist/src/core/index.js.map +1 -1
  88. package/dist/src/server/index.cjs +12 -12
  89. package/dist/src/server/index.cjs.map +1 -1
  90. package/dist/src/server/index.d.cts +12 -12
  91. package/dist/src/server/index.d.ts +12 -12
  92. package/dist/src/server/index.js +12 -12
  93. package/dist/src/server/index.js.map +1 -1
  94. package/package.json +5 -5
@@ -137,6 +137,14 @@ Each interface's call-signature members are listed under [Methods](#methods). `R
137
137
  and `Failure` come from `@orkestrel/contract` (mirrored at [`contract.md`](contract.md)) and are not
138
138
  re-exported; `retryUntil` reads `Result` internally.
139
139
 
140
+ The chooser wire contract is shared by the browser journey and its server command.
141
+
142
+ | Type | Kind | Shape | Summary |
143
+ | ---------------- | --------- | ------------------------------------------ | ------------------------------------------------------------------------------------- |
144
+ | `FixtureFile` | interface | `{ name, mime, bytes }` | Represents one fixture file as a name, media type, and immutable bytes. |
145
+ | `ChoiceOptions` | interface | `{ keyboard?, budget? }` | Configures native file-picker activation and its bounded provider wait. |
146
+ | `ChooserRequest` | interface | `ChoiceOptions` plus `{ selector, files }` | Carries a resolved file-input selector and fixture selection to the browser provider. |
147
+
140
148
  #### Constants
141
149
 
142
150
  A `Shape` cell holds the constant's declared type.
@@ -172,41 +180,52 @@ for a missing or inherited key, and for a member carrying neither. Per-key tuple
172
180
  claim the narrowing carries rather than something the reading checks, so a caller relying on it
173
181
  establishes the pairing between an event and the recorder stored under that event first;
174
182
  `createRecorders` establishes it by wiring each recorder to exactly the event it stores that recorder
175
- under. Every hostile read is contained, so a value whose keys or getters throw answers `false`
183
+ under. Every read of untrusted input is contained, so a value whose keys or getters throw answers `false`
176
184
  instead of propagating.
177
185
 
178
186
  #### Helpers
179
187
 
180
- | API | Kind | Signature | Summary |
181
- | --------------------- | -------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
182
- | `waitForCondition` | function | `(description, condition, options?) => Promise<void>` | Waits until a condition holds within an elapsed-time budget. |
183
- | `retryUntil` | function | `(description, produce, satisfied, options?) => Promise<T>` | Repeats a producer until one produced value satisfies a predicate. |
184
- | `waitForEvent` | function | `(subscribe, description, options?) => Promise<TArgs>` | Waits for the first delivery from an event subscription. |
185
- | `waitForText` | function | `(description, read, text, options?) => Promise<string>` | Waits until a reading of text carries an expected sentence. |
186
- | `checkBounds` | function | `(subject: string, budget: number, interval: number) => void` | Checks the resolved bounds one bounded wait runs under. |
187
- | `buildRetryExhausted` | function | `(description, budget, elapsed, last, cause) => Error` | Builds the error `retryUntil` raises when its elapsed-time budget runs out. |
188
- | `buildRefusal` | function | `(name: string, cause: unknown) => Error` | Builds the error a refused fixture build raises, named for the row it was building for. |
189
- | `dropRegistration` | function | `(registrations: SignalRegistration[], installed) => SignalRegistration \| undefined` | Drops the registration an instrumented signal installed for one listener. |
190
- | `decodeJSONLines` | function | `(text: string) => readonly unknown[]` | Decodes newline-delimited JSON values. |
191
- | `waitForDelay` | function | `(ms?: number) => Promise<void>` | Waits for a host timer to elapse. |
192
- | `waitForAbort` | function | `(signal: AbortSignal) => Promise<void>` | Waits until an abort signal is aborted. |
193
- | `captureError` | function | `(thunk: () => unknown) => unknown` | Captures the value thrown by a thunk. |
194
- | `requireValue` | function | `<T>(value: T \| null \| undefined, message?: string) => T` | Narrows a value away from `null` and `undefined`, throwing when it is absent. |
195
- | `collect` | function | `<T>(source: AsyncIterable<T>) => Promise<readonly T[]>` | Collects every value from an async iterable. |
196
- | `collectStream` | function | `<T>(stream: ReadableStream<T>) => Promise<readonly T[]>` | Collects every value from a readable stream. |
197
- | `roundTripJSON` | function | `<T>(value: T & JSONSafe<T>) => T` | Copies a JSON value through serialization and parsing. |
198
- | `invokeUnchecked` | function | `<T>(target: unknown, method: unknown, args: readonly unknown[]) => T` | Invokes an unknown method through an explicit unchecked result contract. |
199
- | `readProperty` | function | `<T>(target: unknown, key: PropertyKey) => T` | Reads a property from an unknown object or function. |
200
- | `flattenHeaders` | function | `(init: HeadersSource) => Readonly<Record<string, string>>` | Normalizes headers into a frozen plain record. |
201
- | `resolveRoot` | function | `(meta: ImportMeta) => URL` | Resolves the parent directory of a calling module, which is the workspace root when called from the conventional `tests/setup.ts` location. |
202
- | `executeScenario` | function | `(scenario, context) => Promise<void>` | Drives one statechart scenario through its arrange, act, and assert phases. |
203
- | `executeScenarios` | function | `(scenarios, build) => Promise<void>` | Drives a statechart table row by row, each row against a context of its own. |
188
+ | API | Kind | Signature | Summary |
189
+ | ------------------------ | -------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
190
+ | `waitForCondition` | function | `(description, condition, options?) => Promise<void>` | Waits until a condition holds within an elapsed-time budget. |
191
+ | `retryUntil` | function | `(description, produce, satisfied, options?) => Promise<T>` | Repeats a producer until one produced value satisfies a predicate. |
192
+ | `waitForEvent` | function | `(subscribe, description, options?) => Promise<TArgs>` | Waits for the first delivery from an event subscription. |
193
+ | `waitForText` | function | `(description, read, text, options?) => Promise<string>` | Waits until a reading of text carries an expected sentence. |
194
+ | `checkBounds` | function | `(subject: string, budget: number, interval: number) => void` | Checks the resolved bounds one bounded wait runs under. |
195
+ | `buildRetryExhausted` | function | `(description, budget, elapsed, last, cause) => Error` | Builds the error `retryUntil` raises when its elapsed-time budget runs out. |
196
+ | `buildRefusal` | function | `(name: string, cause: unknown) => Error` | Builds the error a refused fixture build raises, named for the row it was building for. |
197
+ | `dropRegistration` | function | `(registrations: SignalRegistration[], installed) => SignalRegistration \| undefined` | Drops the registration an instrumented signal installed for one listener. |
198
+ | `decodeJSONLines` | function | `(text: string) => readonly unknown[]` | Decodes newline-delimited JSON values. |
199
+ | `collectSelectorClasses` | function | `(selector: string) => readonly string[]` | Collects the class names one selector text names, decoded and without repeats. |
200
+ | `waitForDelay` | function | `(ms?: number) => Promise<void>` | Waits for a host timer to elapse. |
201
+ | `waitForAbort` | function | `(signal: AbortSignal) => Promise<void>` | Waits until an abort signal is aborted. |
202
+ | `captureError` | function | `(thunk: () => unknown) => unknown` | Captures the value thrown by a thunk. |
203
+ | `requireValue` | function | `<T>(value: T \| null \| undefined, message?: string) => T` | Narrows a value away from `null` and `undefined`, throwing when it is absent. |
204
+ | `collect` | function | `<T>(source: AsyncIterable<T>) => Promise<readonly T[]>` | Collects every value from an async iterable. |
205
+ | `collectStream` | function | `<T>(stream: ReadableStream<T>) => Promise<readonly T[]>` | Collects every value from a readable stream. |
206
+ | `roundTripJSON` | function | `<T>(value: T & JSONSafe<T>) => T` | Copies a JSON value through serialization and parsing. |
207
+ | `invokeUnchecked` | function | `<T>(target: unknown, method: unknown, args: readonly unknown[]) => T` | Invokes an unknown method through an explicit unchecked result contract. |
208
+ | `readProperty` | function | `<T>(target: unknown, key: PropertyKey) => T` | Reads a property from an unknown object or function. |
209
+ | `flattenHeaders` | function | `(init: HeadersSource) => Readonly<Record<string, string>>` | Normalizes headers into a frozen plain record. |
210
+ | `resolveRoot` | function | `(meta: ImportMeta) => URL` | Resolves the parent directory of a calling module, which is the workspace root when called from the conventional `tests/setup.ts` location. |
211
+ | `executeScenario` | function | `(scenario, context) => Promise<void>` | Drives one statechart scenario through its arrange, act, and assert phases. |
212
+ | `executeScenarios` | function | `(scenarios, build) => Promise<void>` | Drives a statechart table row by row, each row against a context of its own. |
204
213
 
205
214
  `dropRegistration` is the mechanic `createSignal`'s one-shot and scope-abort paths share, exported
206
215
  because both of them call it rather than because a consumer was expected to. Reaching it takes a
207
216
  `SignalRegistration` list of your own: `createSignal` hands back `SignalInterface`, whose members are
208
217
  `controller`, `signal`, and `count`, so nothing this package returns carries the list to pass.
209
218
 
219
+ `collectSelectorClasses` reads the class names out of a selector's text, and the browser's
220
+ `readCascade` reads every loaded rule through it. It reads tokens, not the selector grammar: text
221
+ inside a quoted string, a comment, or an attribute block names no class, so `[title=".card"]`
222
+ contributes nothing, and an escaped name decodes to the class it means, so `.sm\:flex` names
223
+ `sm:flex`. A name may open with an underscore, a hyphen, two hyphens, a non-ASCII letter, or an
224
+ escape. Text it cannot read whole — an open or doubled quote, comment, attribute block, or
225
+ parenthesis, a cut-short escape, a class name opening with a digit, or a character outside the
226
+ selector alphabet such as a declaration block's braces — throws an `Error` reading
227
+ `Unreadable selector: <selector>`.
228
+
210
229
  #### Factories
211
230
 
212
231
  | API | Kind | Signature | Summary |
@@ -220,10 +239,37 @@ because both of them call it rather than because a consumer was expected to. Rea
220
239
 
221
240
  ### Browser
222
241
 
223
- Imported from `@orkestrel/test/browser`. Every journey verb here resolves its own target from a
224
- role and an accessible name and drives it through the installed Vitest provider, and none of them
225
- takes an element, a component instance, or a selector for the thing it acts on. That is what keeps
226
- a journey a description of what a person does rather than of what the markup happens to be.
242
+ Imported from `@orkestrel/test/browser`. Every journey verb here resolves its own target from an
243
+ accessible name and, where the platform supplies one, a role, and drives it through the installed
244
+ Vitest provider. None of them takes an element, a component instance, or a selector for the thing it
245
+ acts on. That is what keeps a journey a description of what a person does rather than of what the
246
+ markup happens to be.
247
+
248
+ The low-level `sendTouch` and `tapTouch` instruments take an element for coordinate projection, as
249
+ `readProtocolPoint` does. `sendFileDrag` resolves a rendered role-and-name target without requiring
250
+ focus reachability, so a status or region can receive an external file drag.
251
+
252
+ Native file inputs carry no ARIA role. Their journey uses the label resolver and the same visibility,
253
+ enabled-state, focus-order, uniqueness, and active-modal checks as the other controls.
254
+
255
+ | API | Kind | Signature | Summary |
256
+ | ------------------ | -------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
257
+ | `resolveFileInput` | function | `(name: string) => HTMLInputElement` | Resolves one visible, enabled, focus-reachable native file input by its exact label. |
258
+ | `chooseAccessible` | function | `(name: string, files: readonly FixtureFile[], options?: ChoiceOptions) => Promise<void>` | Activates a labeled native file input and fulfills its observed chooser with fixture bytes. |
259
+
260
+ Register the server entry's `chooseFiles` function as the `chooseFiles` command in the workspace's
261
+ `configs/commands.ts`, the module the root configuration passes to every browser project as
262
+ Vitest's `test.browser.commands`. A package that declares `@orkestrel/test` imports `chooseFiles`
263
+ from `@orkestrel/test/server` there and freezes `{ chooseFiles }` as its exported `commands`
264
+ record. Pass fixture filenames, media types, and byte arrays to `chooseAccessible`; `keyboard: true`
265
+ reaches the input through Tab before pressing Enter, and the default activates it with a native
266
+ pointer click. Run selections sequentially on a page. The server arms its chooser observer before
267
+ activation, checks the event's input identity, and supplies the bytes through the real Playwright
268
+ chooser. A missing or wrong chooser rejects. Observers and input handles are released after
269
+ success, activation failure, timeout, and page closure. Each provider wait defaults to a positive
270
+ 1000 ms budget. An empty fixture selection clears the input; it does not prove a manual OS Cancel
271
+ gesture. This proves browser-native chooser activation and fixture selection in Chromium, not
272
+ manual OS dialog interaction, OS drag, or network upload.
227
273
 
228
274
  The fixture builders, the readers, and the field writers do take an element, and none of them is a
229
275
  journey verb. `build` creates a node, `mount` attaches one, and `render` does both from
@@ -231,17 +277,23 @@ markup or from a tag and its classes; `createHarness` mounts a whole statechart
231
277
  back the root it mounted; `buildContrast`, `buildEscapes`, and `buildCensus` each build
232
278
  a detached control the caller appends where it is reading; `clearStorage` takes nothing at all, and
233
279
  `removeDatabase` takes a database name. The predicates, the element readers, and the describers name
234
- a node the caller already has — `isRendered`, `isReachable`, `readHit`, `readText`, `readRole`,
235
- `readName`, `readStates`, `readCensus`, `describeTree`, `describeFocus`, `extractOrphans`,
236
- `readRows`, `readStyle`, `readToken`, `readPixels`, `readContrast`, `readLayers`, `readBackdrop`,
237
- and `readRing` — and each reads that node rather than acting on a target it was handed.
280
+ a node the caller already has — `isRendered`, `isReachable`, `supportsSegmentedFocus`, `readHit`, `readText`, `readRole`,
281
+ `readName`, `readStates`, `readProtocolPoint`, `readProtocolNode`, `readAccessibilityNode`,
282
+ `readActiveElement`, `countElements`, `containsNode`, `readCensus`, `describeTree`, `describeFocus`, `extractOrphans`,
283
+ `readRows`, `readStyle`, `readToken`, `readPixels`, `readContrast`, `readColorContrast`,
284
+ `readLayers`, `readBackdrop`, `readRing`, `readAnimations`, `readClippedBlock`, and
285
+ `collectScrollRegions` — and each reads that node rather than acting on a target it was handed.
238
286
  `waitForAnimations` takes one too, and waiting for a browser to stop painting it changes nothing
239
287
  about it. `captureFrame` and `place` take an
240
288
  element as well, and photographing one is a reading too: neither moves focus, dispatches an event,
241
- or changes what the element renders. `typeInput` and `commitInput` are the exception, and it stays
242
- narrow: they write into the field they are given, as the synthetic counterpart of `typeAccessible`
243
- for a component that listens for `input`. The color leaves, the cascade readers, the pane verbs,
244
- and the whole-document readers take a value or nothing at all, so they name no target either.
289
+ or changes what the element renders. `readShotPixel` takes one for the same reason and reads one
290
+ pixel of the provider's shot of it. `settleCapture` takes no element: it parks the pointer the way
291
+ `parkPointer` does and holds the document's endless animations until the resume it returns runs.
292
+ `sendTouch` and `tapTouch` act on an element through protocol coordinates. `typeInput` and
293
+ `commitInput` also take an element: they write into the field they are given, as the synthetic
294
+ counterpart of `typeAccessible` for a component that listens for `input`. The color leaves, the
295
+ cascade readers, the pane verbs, and the whole-document readers take a value or nothing at all, so
296
+ they name no target either, and neither does `stepFocus`, which takes a limit and presses Tab.
245
297
 
246
298
  #### Types
247
299
 
@@ -252,6 +304,7 @@ a union's arms escaped as `\|`.
252
304
  | Type | Kind | Shape | Summary |
253
305
  | --------------------- | --------- | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
254
306
  | `Color` | type | `readonly [red, green, blue, alpha]` | Represents one rendered color as straight sRGB channels and its alpha. |
307
+ | `ContrastOptions` | interface | `{ floor?, opacity? }` | Configures one color contrast reading: the surface the backdrop walk ends on, and the opacity the painted color is shown at. |
255
308
  | `ElementOptions` | interface | `{ classes?, text?, attributes? }` | Configures one built element: its class list, its text, and its attributes. |
256
309
  | `FrameOptions` | interface | `{ path, width, height, element? }` | Configures one captured frame: where it is written, the viewport it is shot at, and what it shoots. |
257
310
  | `FrameOffset` | interface | `{ top, left }` | Represents how far an element frame moves the tester frame from the runner window's origin for the shot, in CSS pixels. |
@@ -262,7 +315,13 @@ a union's arms escaped as `\|`.
262
315
  | `JournalStep` | interface | `{ action, trigger, result }` | Represents one scripted step a journal recorded, and what the surface did about it. |
263
316
  | `JournalInterface` | interface | `{ steps, output }` plus `start` / `stop` / `record` | Records one scenario: every step it took and everything the page said while it ran. |
264
317
  | `StateOptions` | interface | `WaitOptions` plus `{ absent? }` | Configures a bounded wait over the states a control announces. |
265
- | `MediaOptions` | interface | `{ print?, motion?, forced? }` | Configures the tester's print medium, motion preference, and forced colours. |
318
+ | `SettleOptions` | interface | `WaitOptions` plus `{ pointer? }` | Configures one capture settle: the time bounds of `WaitOptions` and whether the pointer parks first. |
319
+ | `AnimationReading` | interface | `{ finite, endless }` | Represents the animations running on one element and its subtree, split by whether each one ends. |
320
+ | `MediaOptions` | interface | `{ print?, motion?, forced?, pointer? }` | Configures the tester's print medium, motion preference, forced colours, and pointer precision. |
321
+ | `StabilityOptions` | interface | `WaitOptions` plus `{ frames? }` | Configures how long a reading must hold still: the time bounds of `WaitOptions` and the count of reads it must agree across. |
322
+ | `AccessibilityNode` | interface | `{ role, name, properties }` | Represents the accessibility node the browser computed for one element. |
323
+ | `SheetAdoption` | interface | `{ sheet: CSSStyleSheet, release() }` | Describes an adopted stylesheet and its idempotent release operation. |
324
+ | `ProtocolPoint` | interface | `{ x, y }` | Represents one point in the coordinates of the browser's top-level viewport, the space a DevTools input command addresses. |
266
325
  | `StorageOptions` | interface | `{ values?, reads?, writes?, quota? }` | Configures an inert `Storage`: its seed, which operations the host permits, and its quota. |
267
326
  | `WebStorageInterface` | interface | `Storage` plus `{}` plus `permit` | Holds a store the host can withhold and later grant. |
268
327
  | `CensusReading` | interface | `{ elements, tokens, undeclared }` | Reports an authored-class census: the population walked, the tokens found, and the undeclared. |
@@ -276,129 +335,164 @@ a union's arms escaped as `\|`.
276
335
 
277
336
  A `Shape` cell holds the constant's declared type.
278
337
 
279
- | API | Kind | Shape | Summary |
280
- | -------------------- | ----- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------- |
281
- | `ACCESSIBLE_ROLES` | const | `readonly string[]` | Names the interactive ARIA roles a bare accessible name is searched across. |
282
- | `CANVAS_COLOR` | const | `Color` | Names the color a browser paints an unstyled document with: opaque white. |
283
- | `CAPTURE_PANE` | const | `string` | Names the attribute marking the runner's tester pane, and the rule that sizes it, while a frame is staged. |
284
- | `CAPTURE_STAGINGS` | const | `number` | Bounds the restagings one capture takes before it refuses a document whose height never settles. |
285
- | `CONTENT_ROLES` | const | `readonly string[]` | Names the roles whose accessible name is the text a reader can see inside them. |
286
- | `FIELD_ROLES` | const | `Readonly<Record<string, string>>` | Names the role each `input` type carries. |
287
- | `FOCUSABLE_SELECTOR` | const | `string` | Names what sequential keyboard navigation can reach, before disabled and unrendered elements go. |
288
- | `HEADER_ROLES` | const | `Readonly<Record<string, string>>` | Names the role a `th` carries for the header axis its `scope` names. |
289
- | `IMPLICIT_ROLES` | const | `Readonly<Record<string, string>>` | Names the role each listed tag carries in the accessibility tree when it declares none of its own. |
290
- | `MEDIA_STAGE` | const | `string` | Names the tester root's attribute holding the media readings observed before the first stage. |
291
- | `POINTER_HOLD` | const | `string` | Names the tester root's attribute holding the pressed pointer's page coordinates. |
338
+ | API | Kind | Shape | Summary |
339
+ | ----------------------- | ----- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------- |
340
+ | `ACCESSIBLE_ROLES` | const | `readonly string[]` | Names the interactive ARIA roles a bare accessible name is searched across. |
341
+ | `CANVAS_COLOR` | const | `Color` | Names the color a browser paints an unstyled document with: opaque white. |
342
+ | `CAPTURE_PANE` | const | `string` | Names the attribute marking the runner's tester pane, and the rule that sizes it, while a frame is staged. |
343
+ | `CAPTURE_STAGINGS` | const | `number` | Bounds the restagings one capture takes before it refuses a document whose height never settles. |
344
+ | `CONTENT_ROLES` | const | `readonly string[]` | Names the roles whose accessible name is the text a reader can see inside them. |
345
+ | `FIELD_ROLES` | const | `Readonly<Record<string, string>>` | Names the role each `input` type carries. |
346
+ | `FOCUSABLE_SELECTOR` | const | `string` | Names what sequential keyboard navigation can reach, before disabled and unrendered elements go. |
347
+ | `SEGMENTED_INPUT_TYPES` | const | `readonly string[]` | Names the native input types whose editable segments share one focused element. |
348
+ | `FOCUS_SEGMENT_LIMIT` | const | `number` | Bounds consecutive focus landings and the Tab presses budgeted per segmented input. |
349
+ | `HEADER_ROLES` | const | `Readonly<Record<string, string>>` | Names the role a `th` carries for the header axis its `scope` names. |
350
+ | `IMPLICIT_ROLES` | const | `Readonly<Record<string, string>>` | Names the role each listed tag carries in the accessibility tree when it declares none of its own. |
351
+ | `MEDIA_STAGE` | const | `string` | Names the tester root's attribute holding the media readings observed before the first stage. |
352
+ | `POINTER_HOLD` | const | `string` | Names the tester root's attribute holding the pressed pointer's page coordinates. |
292
353
 
293
354
  #### Helpers
294
355
 
295
- | API | Kind | Signature | Summary |
296
- | -------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
297
- | `resolveAccessible` | function | `(name: string) => HTMLElement` / `(role: string, name: string) => HTMLElement` | Resolves one visible, focus-reachable interactive element by its exact accessible name. A wholly-off-viewport target is scrolled into view before reachability is measured. |
298
- | `resolveAccessibleWithin` | function | `(region: string, role: string, name: string) => HTMLElement` | Resolves one human-reachable control by role and accessible-name text inside a named region. |
299
- | `resolveRendered` | function | `(first: string, second?: string) => HTMLElement` | Resolves one rendered, focus-reachable interactive element without requiring it to intersect the viewport yet. |
300
- | `computeNamePattern` | function | `(name: string) => RegExp` | Computes the pattern that matches one accessible name a decorative glyph may sit beside. |
301
- | `isOutsideViewport` | function | `(rectangle: DOMRectReadOnly) => boolean` | Determines whether a rectangle lies wholly outside the browser viewport. |
302
- | `isRendered` | function | `(element: Element) => boolean` | Determines whether the accessibility tree presents one element at all. |
303
- | `isReachable` | function | `(element: Element) => boolean` | Determines whether a person can click one element where it sits. |
304
- | `readHit` | function | `(element: Element) => Element \| undefined` | Reads the topmost element at one element's bounding-box centre. |
305
- | `clickAccessible` | function | `(name: string) => Promise<void>` / `(role: string, name: string) => Promise<void>` | Clicks one visible, focus-reachable control by its accessible name through the browser provider. |
306
- | `clickAccessibleWithin` | function | `(region: string, role: string, name: string) => Promise<void>` | Clicks one human-reachable control by role and accessible-name text inside a named region. |
307
- | `clickDisclosure` | function | `(name: string) => Promise<void>` | Opens or closes one native details disclosure by its rendered summary. |
308
- | `sendProtocol` | function | `(method: string, params: Readonly<Record<string, unknown>>) => Promise<void>` | Sends one DevTools protocol command through the browser provider. |
309
- | `hoverAccessible` | function | `(name: string) => Promise<void>` / `(role: string, name: string) => Promise<void>` | Hovers one visible, focus-reachable control by its accessible name through the browser provider. |
310
- | `holdAccessible` | function | `(name: string) => Promise<void>` / `(role: string, name: string) => Promise<void>` | Holds the primary pointer button on one visible, focus-reachable control by its accessible name. |
311
- | `holdAccessibleWithin` | function | `(region: string, role: string, name: string) => Promise<void>` | Holds the primary pointer button on one control by role and accessible-name text inside a named region. |
312
- | `driveHold` | function | `(resolve: () => HTMLElement, name: string) => Promise<void>` | Holds the primary pointer button on the control a resolver returns, through the browser provider. |
313
- | `releasePointer` | function | `() => Promise<void>` | Releases a held pointer and parks it outside the page, clearing hover. |
314
- | `typeAccessible` | function | `(name: string, text: string) => Promise<void>` | Replaces a named field's value through focus, select-all, deletion, and real keystrokes. |
315
- | `fillAccessible` | function | `(name: string, text: string) => Promise<void>` | Replaces a named field's value in one operation, for text too long to type key by key. |
316
- | `traverseAccessible` | function | `(name: string) => Promise<HTMLElement>` | Reaches a named control only through natural forward Tab traversal from the current focus. |
317
- | `traverseAccessibleWithin` | function | `(region: string, role: string, name: string) => Promise<HTMLElement>` | Reaches a control by role and accessible-name text inside a named region, only through natural forward Tab traversal from the current focus. |
318
- | `driveTraversal` | function | `(resolve: () => HTMLElement, name: string) => Promise<HTMLElement>` | Reaches the control a resolver returns, only through natural forward Tab traversal from the current focus. |
319
- | `pressKeys` | function | `(keys: string) => Promise<void>` | Sends a key sequence to whatever holds focus, and refuses to send it to nothing. |
320
- | `readPerception` | function | `(name: string) => string` | Reads the normalized visible text of one named region, dialog, table, tab panel, or alert. |
321
- | `readPage` | function | `() => string` | Reads the normalized visible text of the whole page. |
322
- | `readFocus` | function | `() => string \| undefined` | Reads the rendered text of the element that holds focus. |
323
- | `readValue` | function | `(role: string, name: string) => string` | Reads the value a resolved control renders. |
324
- | `readRefusal` | function | `(name: string) => string \| undefined` / `(role: string, name: string) => string \| undefined` | Reads the refusal one named target answers with, or nothing when it resolves. |
325
- | `readText` | function | `(element: Element) => string` | Reads one element's rendered text the way a name computation reads it. |
326
- | `readRole` | function | `(element: Element) => string \| undefined` | Reads the role one element carries in the accessibility tree. |
327
- | `readName` | function | `(element: Element) => string` | Reads the accessible name one element is announced under. |
328
- | `readStates` | function | `(element: Element) => readonly string[]` | Reads the states one element is announced in. |
329
- | `describeTree` | function | `(element: Element) => string` | Describes the accessible tree one rendered element presents. |
330
- | `describeFocus` | function | `(element: Element) => string` | Describes the order sequential keyboard navigation visits one element's controls in. |
331
- | `waitForFrame` | function | `() => Promise<void>` | Waits for one animation frame to settle pending browser paint work. |
332
- | `waitForState` | function | `(name, state, options?) => Promise<readonly string[]>` / `(role, name, state, options?) => Promise<readonly string[]>` | Waits until one named control announces a state, or stops announcing it. |
333
- | `waitForAnimations` | function | `(element: Element, options?: WaitOptions) => Promise<void>` | Waits until every finite animation on one element and its subtree has stopped moving. |
334
- | `build` | function | `<K extends keyof HTMLElementTagNameMap>(tag: K, options?: ElementOptions) => HTMLElementTagNameMap[K]` | Builds one unmounted element of a known tag, wearing the classes, text, and attributes asked for. |
335
- | `mount` | function | `<T extends Element>(element: T) => T` | Puts one element into the document and hands it straight back. |
336
- | `render` | function | `(markup: string) => HTMLDivElement` / `(tag: K, classes: string) => HTMLElementTagNameMap[K]` | Renders one fixture into the document from trusted markup. |
337
- | `typeInput` | function | `(element: HTMLInputElement \| HTMLTextAreaElement, text: string) => void` | Sets one field's value and announces it the way typing into the field does. |
338
- | `commitInput` | function | `(element: HTMLInputElement \| HTMLTextAreaElement, text: string) => void` | Sets one field's value and commits it, the way typing and then leaving the field does. |
339
- | `clearStorage` | function | `() => void` | Clears both browser storage surfaces. |
340
- | `removeDatabase` | function | `(name: string) => Promise<void>` | Deletes one IndexedDB database and reports what the request actually did. |
341
- | `convertSRGB` | function | `(red: number, green: number, blue: number, alpha?: number) => Color` | Converts normalized encoded sRGB channels to the clipped paint scale. |
342
- | `convertLinearSRGB` | function | `(red: number, green: number, blue: number, alpha?: number) => Color` | Converts linear sRGB channels to encoded, clipped paint channels. |
343
- | `convertXYZD65` | function | `(x: number, y: number, z: number, alpha?: number) => Color` | Converts D65 XYZ coordinates to clipped sRGB paint channels. |
344
- | `convertXYZD50` | function | `(x: number, y: number, z: number, alpha?: number) => Color` | Converts D50 XYZ coordinates to clipped sRGB paint channels. |
345
- | `convertOKLab` | function | `(lightness: number, a: number, b: number, alpha?: number) => Color` | Converts OKLab coordinates to clipped sRGB paint channels. |
346
- | `convertLab` | function | `(lightness: number, a: number, b: number, alpha?: number) => Color` | Converts CIE Lab coordinates relative to D50 to clipped sRGB paint channels. |
347
- | `convertDisplayP3` | function | `(red: number, green: number, blue: number, alpha?: number) => Color` | Converts encoded Display P3 channels to clipped sRGB paint channels. |
348
- | `convertA98RGB` | function | `(red: number, green: number, blue: number, alpha?: number) => Color` | Converts encoded A98 RGB channels to clipped sRGB paint channels. |
349
- | `convertProPhotoRGB` | function | `(red: number, green: number, blue: number, alpha?: number) => Color` | Converts encoded ProPhoto RGB channels to clipped sRGB paint channels. |
350
- | `convertRec2020` | function | `(red: number, green: number, blue: number, alpha?: number) => Color` | Converts encoded Rec. 2020 channels to clipped sRGB paint channels. |
351
- | `parseColor` | function | `(value: string) => Color \| undefined` | Parses computed CSS Color 4 values into clipped straight sRGB channels. |
352
- | `parseCSSColor` | function | `(value: string) => Color \| undefined` | Resolves any CSS color expression to straight sRGB channels, by asking the browser. |
353
- | `matchesColor` | function | `(first: string \| Color, second: string \| Color) => boolean` | Determines whether two colors render the same, within the rounding a browser does. |
354
- | `blendColor` | function | `(front: Color, back: Color) => Color` | Composites one color over another. |
355
- | `measureLuminance` | function | `(color: Color) => number` | Measures one opaque color's WCAG relative luminance. |
356
- | `measureContrast` | function | `(front: Color, back: Color) => number` | Measures the WCAG 2.x contrast ratio between two opaque colors. |
357
- | `readLayers` | function | `(element: Element) => readonly Color[]` | Collects readable background color layers and refuses an unreadable painted layer. |
358
- | `readBackdrop` | function | `(element: Element, floor: Color) => Color` | Resolves the opaque color standing behind one element. |
359
- | `readContrast` | function | `(element: Element, floor?: Color) => number` | Measures the WCAG 2.x contrast ratio between an element's computed text and background colors. |
360
- | `readRing` | function | `(control: Element, worn?: Element) => number \| undefined` | Measures the contrast the focus chrome painted on one control reaches against its own backdrop. |
361
- | `readClipEdge` | function | `(element: Element) => number \| undefined` | Measures the row a clipping element cuts its content off at, in document coordinates. |
362
- | `readClipMargin` | function | `(element: Element) => number` | Measures how far past its own box a clipping element lets its content show. |
363
- | `clipsOverflow` | function | `(element: Element) => boolean` | Reports whether an element clips its descendants' overflow. |
364
- | `measureContent` | function | `() => number` | Measures the row the document's own content ends on, in document coordinates. |
365
- | `stagePane` | function | `(width: number, height: number) => Promise<void>` | Sets the tester's viewport and renders the runner's pane at the size that viewport claims. |
366
- | `releasePane` | function | `() => Promise<void>` | Hands the tester pane back to the runner's own layout, at the viewport it had before staging. |
367
- | `computeOffset` | function | `(box: DOMRectReadOnly, width: number, height: number) => FrameOffset` | Computes how far an element frame moves the tester frame so the element is shot inside the runner's window. |
368
- | `stageMedia` | function | `(options: MediaOptions) => Promise<void>` | Stages the tester's print medium, motion preference, and forced colours through the browser provider. |
369
- | `releaseMedia` | function | `() => Promise<void>` | Restores the media readings observed before the first stage as explicit emulation. With nothing staged, clears every override and waits for a stable reading, not a proved engine baseline. Checks the budget between polls, so a frame that never paints is not bounded by it. |
370
- | `captureFrame` | function | `(options: FrameOptions) => Promise<string>` | Shoots one frame at one viewport size and proves the file on disk holds this run's bytes. |
371
- | `readFrame` | function | `(path: string) => Promise<FrameReading>` | Reads one written frame back and reports its size and the color its bottom row paints. |
372
- | `readCascade` | function | `() => ReadonlySet<string>` | Collects every class token the stylesheets loaded into this document actually define. |
373
- | `readClasses` | function | `(root: ParentNode) => ReadonlySet<string>` | Collects every class token the markup under one root carries. |
374
- | `readCensus` | function | `(root: ParentNode) => CensusReading` | Takes the authored-class census of one subtree against the cascade this document loaded. |
375
- | `readRules` | function | `() => readonly CSSRule[]` | Collects every rule the stylesheets loaded into this document hold, nested grouping rules included. |
376
- | `findRule` | function | `(selector: string) => CSSStyleRule \| undefined` | Finds the first style rule in the cascade whose selector carries a fragment. |
377
- | `findKeyframes` | function | `(name: string) => CSSKeyframesRule \| undefined` | Finds the animation the cascade declares under one name. |
378
- | `readRows` | function | `(root: ParentNode, selector: string) => readonly string[]` | Reads the normalized visible text of every element a selector matches, in document order. |
379
- | `extractOrphans` | function | `(root: ParentNode, child: string, parent: string) => readonly string[]` | Collects every element carrying a component class rendered outside the container it belongs to. |
380
- | `extractStyles` | function | `(root: ParentNode) => readonly string[]` | Collects the markup of every element carrying a non-empty `style` attribute and of every `<style>` element, in document order, `root` included in both populations when it is an `Element`. |
381
- | `readStyle` | function | `(element: Element, property: string, pseudo?: string) => string` | Reads one resolved CSS property from a real browser element or a named pseudo-element. |
382
- | `readToken` | function | `(element: Element, name: string) => string` | Reads one custom property from an element's resolved style. |
383
- | `readRootToken` | function | `(name: string) => string` | Reads one custom property from the document element. |
384
- | `readPixels` | function | `(element: Element, property: string, pseudo?: string) => number` | Reads one resolved CSS length as a number of pixels. |
385
- | `expandCaptures` | function | `(states: readonly string[], variants: readonly CaptureVariant[]) => readonly string[]` | Expands a capture registry across every variant into the filenames a complete portfolio holds. |
386
- | `buildDenial` | function | `(operation: string, key?: string) => DOMException` | Builds the refusal a host withholding a storage operation raises. |
387
- | `buildContrast` | function | `(bar: number) => ContrastFixture` | Builds a detached translucent stack whose composited and flat contrast readings straddle one bar. |
388
- | `buildEscapes` | function | `(permitted: string) => EscapeFixture` | Builds detached markup carrying one style escape of each kind, plus the sheet a project allows. |
389
- | `buildCensus` | function | `() => CensusFixture` | Builds detached markup carrying one undeclared class token on HTML and another on SVG. |
356
+ | API | Kind | Signature | Summary |
357
+ | -------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
358
+ | `supportsSegmentedFocus` | function | `(element: Element) => boolean` | Determines whether a native input exposes separately editable segments under one focused element. |
359
+ | `resolveAccessible` | function | `(name: string) => HTMLElement` / `(role: string, name: string) => HTMLElement` | Resolves one visible, focus-reachable interactive element by its exact accessible name. A wholly-off-viewport target is scrolled into view before reachability is measured. |
360
+ | `resolveAccessibleWithin` | function | `(region: string, role: string, name: string) => HTMLElement` | Resolves one human-reachable control by role and accessible-name text inside a named region. |
361
+ | `resolveRendered` | function | `(first: string, second?: string) => HTMLElement` | Resolves one rendered, focus-reachable interactive element without requiring it to intersect the viewport yet. |
362
+ | `computeNamePattern` | function | `(name: string) => RegExp` | Computes the pattern that matches one accessible name a decorative glyph may sit beside. |
363
+ | `isOutsideViewport` | function | `(rectangle: DOMRectReadOnly) => boolean` | Determines whether a rectangle lies wholly outside the browser viewport. |
364
+ | `isRendered` | function | `(element: Element) => boolean` | Determines whether the accessibility tree presents one element at all. |
365
+ | `isReachable` | function | `(element: Element) => boolean` | Determines whether a person can click one element where it sits. |
366
+ | `readHit` | function | `(element: Element) => Element \| undefined` | Reads the topmost element at one element's bounding-box centre. |
367
+ | `clickAccessible` | function | `(name: string) => Promise<void>` / `(role: string, name: string) => Promise<void>` | Clicks one visible, focus-reachable control by its accessible name through the browser provider. |
368
+ | `clickAccessibleWithin` | function | `(region: string, role: string, name: string) => Promise<void>` | Clicks one human-reachable control by role and accessible-name text inside a named region. |
369
+ | `clickDisclosure` | function | `(name: string) => Promise<void>` | Opens or closes one native details disclosure by its rendered summary. |
370
+ | `sendProtocol` | function | `(method: string, params?: Readonly<Record<string, unknown>>) => Promise<unknown>` | Sends one DevTools protocol command through the browser provider. |
371
+ | `readProtocolPoint` | function | `(element: Element, offset?: ProtocolPoint) => ProtocolPoint` | Projects a point of one element through every frame that holds it into the browser's top-level viewport. |
372
+ | `readProtocolNode` | function | `(element: Element) => Promise<number>` | Resolves one element to the node the browser's DOM tree holds for it. |
373
+ | `readAccessibilityNode` | function | `(element: Element) => Promise<AccessibilityNode>` | Reads the accessibility node the browser computed for one element. |
374
+ | `hoverAccessible` | function | `(name: string) => Promise<void>` / `(role: string, name: string) => Promise<void>` | Hovers one visible, focus-reachable control by its accessible name through the browser provider. |
375
+ | `holdAccessible` | function | `(name: string) => Promise<void>` / `(role: string, name: string) => Promise<void>` | Holds the primary pointer button on one visible, focus-reachable control by its accessible name. |
376
+ | `holdAccessibleWithin` | function | `(region: string, role: string, name: string) => Promise<void>` | Holds the primary pointer button on one control by role and accessible-name text inside a named region. |
377
+ | `driveHold` | function | `(resolve: () => HTMLElement, name: string) => Promise<void>` | Holds the primary pointer button on the control a resolver returns, through the browser provider. |
378
+ | `parkPointer` | function | `() => Promise<void>` | Parks the pointer outside the page and waits for the parked pointer's frame to paint. |
379
+ | `releasePointer` | function | `() => Promise<void>` | Releases a held pointer and parks it outside the page, clearing hover. |
380
+ | `typeAccessible` | function | `(name: string, text: string) => Promise<void>` | Replaces a named field's value through focus, select-all, deletion, and real keystrokes. |
381
+ | `fillAccessible` | function | `(name: string, text: string) => Promise<void>` | Replaces a named field's value in one operation, for text too long to type key by key. |
382
+ | `traverseAccessible` | function | `(name: string) => Promise<HTMLElement>` | Reaches a named control only through natural forward Tab traversal from the current focus. |
383
+ | `traverseAccessibleWithin` | function | `(region: string, role: string, name: string) => Promise<HTMLElement>` | Reaches a control by role and accessible-name text inside a named region, only through natural forward Tab traversal from the current focus. |
384
+ | `readActiveElement` | function | `(root?: Document \| ShadowRoot) => HTMLElement \| undefined` | Reads the HTML element that holds focus, following it into open shadow roots and same-origin frames. |
385
+ | `countElements` | function | `(root?: Document \| ShadowRoot) => number` | Counts the elements a tree holds, descending into open shadow roots and same-origin frames. |
386
+ | `containsNode` | function | `(root: Node, node: Node) => boolean` | Determines whether a node sits inside a root, through open shadow roots and frames. |
387
+ | `stepFocus` | function | `(limit: number) => AsyncGenerator<HTMLElement \| undefined, void, undefined>` | Presses Tab and yields the element focus lands on, once per press, up to a limit. |
388
+ | `driveTraversal` | function | `(resolve: () => HTMLElement, name: string) => Promise<HTMLElement>` | Reaches the control a resolver returns, only through natural forward Tab traversal from the current focus. |
389
+ | `walkFocus` | function | `(stop?: (element: HTMLElement) => boolean) => Promise<readonly HTMLElement[]>` | Walks forward by real Tab presses from the focused element until a predicate accepts a stop or the Tab order ends. |
390
+ | `collectTabStops` | function | `(root: Element, start: HTMLElement) => Promise<readonly HTMLElement[]>` | Collects the elements of a region that a forward Tab walk reaches from a starting element. |
391
+ | `pressKeys` | function | `(keys: string) => Promise<void>` | Sends a key sequence to whatever holds focus, and refuses to send it to nothing. |
392
+ | `readPerception` | function | `(name: string) => string` | Reads the normalized visible text of one named region, dialog, table, tab panel, or alert. |
393
+ | `readPage` | function | `() => string` | Reads the normalized visible text of the whole page. |
394
+ | `readFocus` | function | `() => string \| undefined` | Reads the rendered text of the element that holds focus. |
395
+ | `readValue` | function | `(role: string, name: string) => string` | Reads the value a resolved control renders. |
396
+ | `readRefusal` | function | `(name: string) => string \| undefined` / `(role: string, name: string) => string \| undefined` | Reads the refusal one named target answers with, or nothing when it resolves. |
397
+ | `readText` | function | `(element: Element) => string` | Reads one element's rendered text the way a name computation reads it. |
398
+ | `readRole` | function | `(element: Element) => string \| undefined` | Reads the role one element carries in the accessibility tree. |
399
+ | `readName` | function | `(element: Element) => string` | Reads the accessible name one element is announced under. |
400
+ | `readStates` | function | `(element: Element) => readonly string[]` | Reads the states one element is announced in. |
401
+ | `describeTree` | function | `(element: Element) => string` | Describes the accessible tree one rendered element presents. |
402
+ | `describeFocus` | function | `(element: Element) => string` | Describes the order sequential keyboard navigation visits one element's controls in. |
403
+ | `waitForFrame` | function | `() => Promise<void>` | Waits for one animation frame to settle pending browser paint work. |
404
+ | `waitForState` | function | `(name, state, options?) => Promise<readonly string[]>` / `(role, name, state, options?) => Promise<readonly string[]>` | Waits until one named control announces a state, or stops announcing it. |
405
+ | `waitForAnimations` | function | `(element: Element, options?: WaitOptions) => Promise<void>` | Waits until every finite animation on one element and its subtree has stopped moving. |
406
+ | `readAnimations` | function | `(element: Element) => AnimationReading` | Reads the animations running on one element and its subtree, split by whether each one ends. |
407
+ | `settleCapture` | function | `(options?: SettleOptions) => Promise<() => void>` | Settles the page for one capture: parks the pointer, waits out every finite animation, and holds each endless animation at its first frame. |
408
+ | `collectScrollRegions` | function | `(root: Element, axis?: 'inline' \| 'block') => readonly HTMLElement[]` | Collects the rendered boxes a person can scroll. |
409
+ | `describeScrollRegions` | function | `(regions: readonly HTMLElement[]) => string` | Describes the window's scroll position and each scroll region's offsets and extents. |
410
+ | `build` | function | `<K extends keyof HTMLElementTagNameMap>(tag: K, options?: ElementOptions) => HTMLElementTagNameMap[K]` | Builds one unmounted element of a known tag, wearing the classes, text, and attributes asked for. |
411
+ | `mount` | function | `<T extends Element>(element: T) => T` | Puts one element into the document and hands it straight back. |
412
+ | `typeInput` | function | `(element: HTMLInputElement \| HTMLTextAreaElement, text: string) => void` | Sets one field's value and announces it the way typing into the field does. |
413
+ | `commitInput` | function | `(element: HTMLInputElement \| HTMLTextAreaElement, text: string) => void` | Sets one field's value and commits it, the way typing and then leaving the field does. |
414
+ | `render` | function | `(markup: string) => HTMLDivElement` / `(tag: K, classes: string) => HTMLElementTagNameMap[K]` | Renders one fixture into the document from trusted markup. |
415
+ | `clearStorage` | function | `() => void` | Clears both browser storage surfaces. |
416
+ | `removeDatabase` | function | `(name: string) => Promise<void>` | Deletes one IndexedDB database and reports what the request actually did. |
417
+ | `convertSRGB` | function | `(red: number, green: number, blue: number, alpha?: number) => Color` | Converts normalized encoded sRGB channels to the clipped paint scale. |
418
+ | `convertLinearSRGB` | function | `(red: number, green: number, blue: number, alpha?: number) => Color` | Converts linear sRGB channels to encoded, clipped paint channels. |
419
+ | `convertXYZD65` | function | `(x: number, y: number, z: number, alpha?: number) => Color` | Converts D65 XYZ coordinates to clipped sRGB paint channels. |
420
+ | `convertXYZD50` | function | `(x: number, y: number, z: number, alpha?: number) => Color` | Converts D50 XYZ coordinates to clipped sRGB paint channels. |
421
+ | `convertOKLab` | function | `(lightness: number, a: number, b: number, alpha?: number) => Color` | Converts OKLab coordinates to clipped sRGB paint channels. |
422
+ | `convertLab` | function | `(lightness: number, a: number, b: number, alpha?: number) => Color` | Converts CIE Lab coordinates relative to D50 to clipped sRGB paint channels. |
423
+ | `convertDisplayP3` | function | `(red: number, green: number, blue: number, alpha?: number) => Color` | Converts encoded Display P3 channels to clipped sRGB paint channels. |
424
+ | `convertA98RGB` | function | `(red: number, green: number, blue: number, alpha?: number) => Color` | Converts encoded A98 RGB channels to clipped sRGB paint channels. |
425
+ | `convertProPhotoRGB` | function | `(red: number, green: number, blue: number, alpha?: number) => Color` | Converts encoded ProPhoto RGB channels to clipped sRGB paint channels. |
426
+ | `convertRec2020` | function | `(red: number, green: number, blue: number, alpha?: number) => Color` | Converts encoded Rec. 2020 channels to clipped sRGB paint channels. |
427
+ | `parseColor` | function | `(value: string) => Color \| undefined` | Parses computed CSS Color 4 values into clipped straight sRGB channels. |
428
+ | `parseCSSColor` | function | `(value: string, scope?: Element) => Color \| undefined` | Resolves any CSS color expression to straight sRGB channels, by asking the browser. |
429
+ | `matchesColor` | function | `(first: string \| Color, second: string \| Color) => boolean` | Determines whether two colors render the same, within the rounding a browser does. |
430
+ | `blendColor` | function | `(front: Color, back: Color) => Color` | Composites one color over another. |
431
+ | `measureLuminance` | function | `(color: Color) => number` | Measures one opaque color's WCAG relative luminance. |
432
+ | `measureContrast` | function | `(front: Color, back: Color) => number` | Measures the WCAG 2.x contrast ratio between two opaque colors. |
433
+ | `readLayers` | function | `(element: Element) => readonly Color[]` | Collects readable background color layers and refuses an unreadable painted layer. |
434
+ | `readBackdrop` | function | `(element: Element, floor: Color) => Color` | Resolves the opaque color standing behind one element. |
435
+ | `readColorContrast` | function | `(element: Element, color: string, options?: ContrastOptions) => number` | Measures the WCAG 2.x contrast ratio one color reaches when painted over an element's backdrop. |
436
+ | `readContrast` | function | `(element: Element, floor?: Color) => number` | Measures the WCAG 2.x contrast ratio between an element's computed text and background colors. |
437
+ | `readRing` | function | `(control: Element, worn?: Element) => number \| undefined` | Measures the contrast the focus chrome painted on one control reaches against its own backdrop. |
438
+ | `readClipEdge` | function | `(element: Element) => number \| undefined` | Measures the row a clipping element cuts its content off at, in document coordinates. |
439
+ | `readClipMargin` | function | `(element: Element) => number` | Measures how far past its own box a clipping element lets its content show. |
440
+ | `readClippedBlock` | function | `(element: Element) => number` | Measures the block size an element shows after every clipping ancestor cuts it. |
441
+ | `collectClippingAncestors` | function | `(element: Element) => readonly Element[]` | Collects the ancestors of an element that clip their descendants' overflow. |
442
+ | `clipsOverflow` | function | `(element: Element) => boolean` | Reports whether an element clips its descendants' overflow. |
443
+ | `measureContent` | function | `() => number` | Measures the row the document's own content ends on, in document coordinates. |
444
+ | `stagePane` | function | `(width: number, height: number) => Promise<void>` | Sets the tester's viewport and renders the runner's pane at the size that viewport claims. |
445
+ | `releasePane` | function | `() => Promise<void>` | Hands the tester pane back to the runner's own layout, at the viewport it had before staging. |
446
+ | `computeOffset` | function | `(box: DOMRectReadOnly, width: number, height: number) => FrameOffset` | Computes how far an element frame moves the tester frame so the element is shot inside the runner's window. |
447
+ | `stageMedia` | function | `(options: MediaOptions) => Promise<void>` | Stages the tester's print medium, motion preference, forced colours, and pointer precision through the browser provider. |
448
+ | `releaseMedia` | function | `() => Promise<void>` | Restores the media readings observed before the first stage as explicit emulation. With nothing staged, clears every override and waits for a stable reading, not a proved engine baseline. Checks the budget between polls, so a frame that never paints is not bounded by it. |
449
+ | `waitForStability` | function | `(description: string, read: () => string, options?: StabilityOptions) => Promise<void>` | Waits for a reading to hold across its last `frames + 1` reads, each later read taken after a painted frame, with the interval slept between polls. Checks the budget between reads, so a frame that never paints is not bounded by it. |
450
+ | `captureFrame` | function | `(options: FrameOptions) => Promise<string>` | Shoots one frame at one viewport size and proves the file on disk holds this run's bytes. |
451
+ | `paintImage` | function | `(encoded: string) => Promise<OffscreenCanvasRenderingContext2D \| undefined>` | Decodes a base64 PNG and paints it, unscaled, onto a fresh offscreen 2D canvas. |
452
+ | `readFrame` | function | `(path: string) => Promise<FrameReading>` | Reads one written frame back and reports its size and the color its bottom row paints. |
453
+ | `readShotPixel` | function | `(element: Element, x: number, y: number) => Promise<Color>` | Reads one painted pixel of an element's screenshot. |
454
+ | `readCascade` | function | `() => ReadonlySet<string>` | Collects every class token the stylesheets loaded into this document actually define. |
455
+ | `readClasses` | function | `(root: ParentNode) => ReadonlySet<string>` | Collects every class token the markup under one root carries. |
456
+ | `readCensus` | function | `(root: ParentNode) => CensusReading` | Takes the authored-class census of one subtree against the cascade this document loaded. |
457
+ | `sendTouch` | function | `(type: 'touchStart' \| 'touchMove' \| 'touchEnd' \| 'touchCancel', element?: Element, point?: ProtocolPoint) => Promise<void>` | Sends one emulated touch event through the browser protocol. |
458
+ | `tapTouch` | function | `(element: Element, options?: StabilityOptions) => Promise<void>` | Taps an element's centre with emulated touch after its box settles. |
459
+ | `releaseTouch` | function | `() => Promise<void>` | Cancels emulated touch input, disables touch emulation, and parks the pointer. |
460
+ | `sendFileDrag` | function | `(type: 'dragEnter' \| 'dragOver' \| 'dragCancel', role: string, name: string, names: readonly string[]) => Promise<void>` | Carries files from outside the page over a rendered target named by role and accessible name. |
461
+ | `adoptSheet` | function | `(text: string) => SheetAdoption` | Adopts CSS text into the document with an idempotent release operation. |
462
+ | `readLayerNames` | function | `(source: CSSStyleSheet \| CSSGroupingRule \| CSSStyleRule, layer?: string) => readonly string[]` | Reads layer statements and blocks in declaration order, including repeated names. |
463
+ | `readRules` | function | `() => readonly CSSRule[]` | Collects every rule the stylesheets loaded into this document hold, nested grouping rules included. |
464
+ | `findRule` | function | `(selector: string) => CSSStyleRule \| undefined` | Finds the first style rule in the cascade whose selector carries a fragment. |
465
+ | `findKeyframes` | function | `(name: string) => CSSKeyframesRule \| undefined` | Finds the animation the cascade declares under one name. |
466
+ | `readRows` | function | `(root: ParentNode, selector: string) => readonly string[]` | Reads the normalized visible text of every element a selector matches, in document order. |
467
+ | `extractOrphans` | function | `(root: ParentNode, child: string, parent: string) => readonly string[]` | Collects every element carrying a component class rendered outside the container it belongs to. |
468
+ | `extractStyles` | function | `(root: ParentNode) => readonly string[]` | Collects the markup of every element carrying a non-empty `style` attribute and of every `<style>` element, in document order, `root` included in both populations when it is an `Element`. |
469
+ | `readStyle` | function | `(element: Element, property: string, pseudo?: string) => string` | Reads one resolved CSS property from a real browser element or a named pseudo-element. |
470
+ | `readToken` | function | `(element: Element, name: string) => string` | Reads one custom property from an element's resolved style. |
471
+ | `readRootToken` | function | `(name: string) => string` | Reads one custom property from the document element. |
472
+ | `readPixels` | function | `(element: Element, property: string, pseudo?: string) => number` | Reads one resolved CSS length as a number of pixels. |
473
+ | `expandCaptures` | function | `(states: readonly string[], variants: readonly CaptureVariant[]) => readonly string[]` | Expands a capture registry across every variant into the filenames a complete portfolio holds. |
474
+ | `buildDenial` | function | `(operation: string, key?: string) => DOMException` | Builds the refusal a host withholding a storage operation raises. |
475
+ | `buildContrast` | function | `(bar: number) => ContrastFixture` | Builds a detached translucent stack whose composited and flat contrast readings straddle one bar. |
476
+ | `buildEscapes` | function | `(permitted: string) => EscapeFixture` | Builds detached markup carrying one style escape of each kind, plus the sheet a project allows. |
477
+ | `buildCensus` | function | `() => CensusFixture` | Builds detached markup carrying one undeclared class token on HTML and another on SVG. |
390
478
 
391
479
  #### Factories
392
480
 
393
- | API | Kind | Signature | Summary |
394
- | -------------------- | -------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
395
- | `createPointerEvent` | function | `(name: string, options?: PointerEventInit) => PointerEvent` | Creates one real pointer event, ready to dispatch. |
396
- | `createDragEvent` | function | `(name: string, options?: DragEventInit) => DragEvent` | Creates one real drag event carrying a live data transfer, ready to dispatch. |
397
- | `createPortfolio` | function | `(options: PortfolioOptions) => PortfolioInterface` | Creates the capture portfolio one run places its screenshots through. |
398
- | `createChannel` | function | `(name: string, output: string[], forward: (...data: unknown[]) => void) => (...data: unknown[]) => void` | Creates one console channel that records every call it receives and hands that call on unchanged. |
399
- | `createJournal` | function | `() => JournalInterface` | Creates the journal one scenario records its steps and the page's own output into. |
400
- | `createStorage` | function | `(options?: StorageOptions) => WebStorageInterface` | Creates an inert `Storage` a host can withhold, grant, and run out of room in. |
401
- | `createHarness` | function | `(options: HarnessOptions<TState, TEvent, TContext>) => HarnessInterface` | Creates a mounted statechart harness that renders one transition table and drives it row by row. |
481
+ | API | Kind | Signature | Summary |
482
+ | --------------------- | -------- | --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
483
+ | `createPortfolio` | function | `(options: PortfolioOptions) => PortfolioInterface` | Creates the capture portfolio one run places its screenshots through; a disabled run still refuses. |
484
+ | `createChannel` | function | `(name: string, output: string[], forward: (...data: unknown[]) => void) => (...data: unknown[]) => void` | Creates one console channel that records every call it receives and hands that call on unchanged. |
485
+ | `createJournal` | function | `() => JournalInterface` | Creates the journal one scenario records its steps and the page's own output into. |
486
+ | `createStorage` | function | `(options?: StorageOptions) => WebStorageInterface` | Creates an inert `Storage` a host can withhold, grant, and run out of room in. |
487
+ | `createHarness` | function | `(options: HarnessOptions<TState, TEvent, TContext>) => HarnessInterface` | Creates a mounted statechart harness that renders one transition table and drives it row by row. |
488
+ | `createPointerEvent` | function | `(name: string, options?: PointerEventInit) => PointerEvent` | Creates one real pointer event, ready to dispatch. |
489
+ | `createDragEvent` | function | `(name: string, options?: DragEventInit) => DragEvent` | Creates one real drag event carrying a live data transfer, ready to dispatch. |
490
+ | `createKeyboardEvent` | function | `(name: string, options?: KeyboardEventInit) => KeyboardEvent` | Creates one real keyboard event, ready to dispatch. |
491
+
492
+ `createPointerEvent` and `createDragEvent` default to `composed: true` from 0.0.27, where 0.0.26
493
+ left it `false`. An event dispatched inside an open shadow root stayed there in 0.0.26, and a
494
+ listener outside that root heard nothing; a listener outside it hears the event from 0.0.27. Pass
495
+ `{ composed: false }` to restore the 0.0.26 boundary behavior.
402
496
 
403
497
  `resolveAccessible` counts a match as reachable only when every condition holds: it is connected; it
404
498
  passes a visibility check honouring opacity and CSS; its box has non-zero width and height; its
@@ -446,12 +540,21 @@ resolution on its own, and `holdAccessibleWithin` and `traverseAccessibleWithin`
446
540
  resolves the way `holdAccessible` and `traverseAccessible` drive a document-wide one, so a twin of
447
541
  the same name in another region is left alone.
448
542
 
449
- `traverseAccessible` charges a step only when focus actually lands on an element, ends when focus
450
- revisits one — that is a complete cycle of the tab order — and re-resolves the target by name on
451
- every step, because a framework may replace the node between resolution and focus arrival. Its hard
452
- cap is three times the page's candidate count plus ten, including disabled controls and elements
453
- with `tabindex="-1"`, so a page whose focus never settles fails instead of hanging.
454
- `driveTraversal` is that loop over any resolver, and `driveHold` is the one pointer drive every hold
543
+ `traverseAccessible` charges a step only when focus actually lands on an element and re-resolves
544
+ the target by name on every step, because a framework may replace the node between resolution and
545
+ focus arrival. Consecutive landings on the same native `date`, `time`, `datetime-local`, `month`, or
546
+ `week` input pass through its editable segments and count that control once, up to
547
+ `FOCUS_SEGMENT_LIMIT` landings. A longer run or any other revisit ends the traversal, including a
548
+ consecutive repeat on a button in a focus trap. Any different landing starts a separate run.
549
+ The `supportsSegmentedFocus` predicate is true for an input whose `type` is in `SEGMENTED_INPUT_TYPES`,
550
+ checked against the element's own document's `HTMLInputElement`, so a same-origin frame's input
551
+ counts. It is false where the document has no window. The hard cap is `FOCUS_SEGMENT_LIMIT` times
552
+ the page's candidate count plus ten, including disabled controls and elements with `tabindex="-1"`.
553
+ The limit budgets ten presses per candidate. Chromium measured nine landings on a `datetime-local`
554
+ input with `step="0.001"`: year, month, day, hour, minute, second, millisecond, and meridiem segments,
555
+ plus the picker indicator. The budget leaves one spare press per input, so a page of segmented
556
+ inputs can finish while a page whose focus never settles still fails.
557
+ `driveTraversal` implements this forward Tab traversal for any resolver. `driveHold` is the one pointer drive every hold
455
558
  verb shares, releasing the pointer before refusing when the frame wait or the pressed-state read
456
559
  fails, as well as when the press misses; each verb supplies its resolver and the name its refusals
457
560
  voice.
@@ -498,12 +601,26 @@ clipped to 0–255 after conversion; this reader does not perform perceptual gam
498
601
  Alpha is clipped to 0–1.
499
602
 
500
603
  `parseCSSColor` is the live half of the pair `parseColor` opens. `parseColor` reads text and speaks
501
- only the computed syntaxes a cascade hands back; `parseCSSColor` stages a probe element, hands the
502
- expression to the real cascade, and reads back what the engine computed — which is the only way a
503
- keyword, a hex triple, a `var()` reference, or a `color-mix()` becomes channels at all. The probe is
504
- mounted, so a `var()` reference resolves against the tokens `:root` declares, and it is removed in a
505
- `finally`. Refusal is the CSSOM's: an expression it will not parse returns `undefined`. A `var()`
506
- naming an undeclared custom property is not refused, and [Limits](#limits) states what that costs.
604
+ only the computed syntaxes a cascade hands back; `parseCSSColor` hands the expression to the real
605
+ cascade and reads back what the engine computed — which is the only way a keyword, a hex triple, a
606
+ `var()` reference, or a `color-mix()` becomes channels at all. With no scope the expression resolves
607
+ on a probe that is a child of the body, mounted so a `var()` reference resolves against the tokens
608
+ `:root` declares, and removed in a `finally`. Refusal is the CSSOM's: an expression it will not parse
609
+ returns `undefined`. A `var()` naming an undeclared custom property is not refused, and
610
+ [Limits](#limits) states what that costs. The optional `scope` argument resolves the expression on
611
+ that same body-mounted probe, which carries the scope's computed context onto its own inline style:
612
+ the computed value of every custom property the expression names, a fallback's included, the
613
+ scope's computed `color`, and its computed `color-scheme`. The expression is written into
614
+ `background-color`, so `currentColor` is the carried `color`, and a system color or `light-dark()`
615
+ answers as the scope's scheme. The element's own and inherited custom properties apply, a registered
616
+ property that does not inherit included, and an `input`, an SVG element, or any other element
617
+ resolves like any other. A custom property is named by letters, digits, `_`, and `-` after its `--`,
618
+ and a name the scope leaves undeclared is not carried, so its `var()` fallback still applies. The
619
+ scope element is never written: the probe is freshly created, so it has no earlier style to start a transition
620
+ from, a transition running on the scope keeps running and `currentColor` reads the color the scope
621
+ shows at that moment, and the scope's geometry and scroll position stay as they were. No attribute,
622
+ style, or child of the scope changes, so a `MutationObserver` on it records nothing. A disconnected
623
+ scope computes no style and returns `undefined`.
507
624
 
508
625
  `matchesColor` compares two colors as a browser renders them. Each string side resolves through
509
626
  `parseCSSColor`, so a keyword, a token reference, and the `rgb()` an engine computes for either
@@ -522,6 +639,21 @@ or the color a fragment is really mounted onto — and the same stack composites
522
639
  being refused. A detached element is refused either way, because its computed foreground color does
523
640
  not exist.
524
641
 
642
+ `readColorContrast` is the same measurement with the color handed in, for paint that is not the
643
+ element's own text: a separator grip, a placeholder, a checkbox mark. `readContrast` and `readRing`
644
+ both measure through it. A computed color value parses directly, and any other expression — a
645
+ keyword, a hex triple, a `var()` reference — resolves through `parseCSSColor` at the element's own
646
+ scope: a token reads as the element and its ancestors declare it, and `currentColor` is the
647
+ element's own text color, not the body's. The read carries the element's computed custom
648
+ properties, `color`, and `color-scheme` onto a probe, so an `input` and a registered token that does
649
+ not inherit resolve as they do on any other element, and the element itself is never written. A
650
+ disconnected element computes no style, so it refuses such an expression as unreadable; hand it a
651
+ computed color such as `rgb(255, 255, 255)`. `floor` takes the
652
+ part `readContrast`'s second argument takes, with the same refusal and the same voice when it is
653
+ omitted. `opacity` multiplies the color's own alpha before it is composited, for a mark painted
654
+ inside an element whose `opacity` is below `1`, and a value outside `0` to `1` is refused rather than
655
+ read.
656
+
525
657
  `readLayers` is the reading that refusal turns on. It hands back the painted layers themselves,
526
658
  element first and the deepest last, so a walk that ended on a real surface is the one whose last
527
659
  layer has an alpha of `1`. A composited color cannot answer that question: 64 half-transparent
@@ -533,14 +665,17 @@ An unreadable painted background layer throws an error naming its element and co
533
665
  Non-finite calculations such as `color(srgb calc(infinity) 0 0)` are deliberately unreadable.
534
666
  A layer with an explicit zero alpha paints nothing and is skipped, including an unreadable one.
535
667
  Background images remain outside this color reader. The refusal propagates through `readBackdrop`,
536
- `readContrast`, and the focused-control reading in `readRing`, even with a supplied floor.
668
+ `readContrast`, `readColorContrast`, and the reading `readRing` takes of painted focus chrome, even
669
+ with a supplied floor. `readRing` reads the backdrop for every control matching `:focus-visible`
670
+ before it looks for chrome, so a focused control whose cascade paints no chrome is refused over an
671
+ unreadable surface too.
537
672
 
538
673
  `readBackdrop` composites that stack and takes its floor as an argument rather than reaching for
539
674
  `CANVAS_COLOR` itself, so a measurement over a surface the canvas never shows through names the
540
675
  color it actually sits on. When no layer paints it hands that floor straight back.
541
676
 
542
677
  `readRing` reads and never acts. Focus arrives through the published verbs, and this measures what
543
- the browser painted once it landed: the `outline` the cascade declares, and the first color in a
678
+ the browser painted after it landed: the `outline` the cascade declares, and the first color in a
544
679
  `box-shadow`. A control that is not matching `:focus-visible`, a control left the browser's own
545
680
  `outline-style: auto` ring, and a focus style that only changes the control's own fill all report
546
681
  `undefined` — in each case no measurement taken here would be about focus. `worn` names the element
@@ -566,6 +701,16 @@ class declared inside an earlier grouping rule; 0.0.8 popped a stack and inserte
566
701
  first. Iterate the set where the order is the subject and read `has` where membership is. A
567
702
  `@keyframes` rule's own children are outside the walk, so an animation's stops define no token here.
568
703
 
704
+ Each rule's selector text is read by `collectSelectorClasses`, and that reading differs from 0.0.26,
705
+ which matched a pattern over the text. An escaped name decodes, so `.sm\:flex` defines `sm:flex`
706
+ where 0.0.26 read `sm`. Text inside an attribute value, a string, or a comment defines nothing, so
707
+ `[title=".card"]` defines no `card`. A name opening with an underscore, a hyphen, or a non-ASCII
708
+ letter counts, where 0.0.26 skipped it. A non-ASCII letter inside a name stays in it, so `.café`
709
+ defines `café` where 0.0.26 ended the class at the first code point outside ASCII letters, digits,
710
+ `_`, and `-` and defined `caf`. A census that saw any of those misreadings reports the
711
+ corrected set, so `readCensus` can move a token into or out of `undeclared`. One rule's own tokens
712
+ enter the set in code-unit order rather than in the order the selector writes them.
713
+
569
714
  `findRule` and `findKeyframes` differ in how they match, and the subject is what decides it. A
570
715
  selector is compound, so `findRule` matches its argument as a substring of the whole selector text:
571
716
  `findRule('.card')` finds `.card`, `.card:hover`, and `.panel > .card` alike, and more of the
@@ -587,7 +732,7 @@ where that distinction matters.
587
732
 
588
733
  `stagePane` unscales the runner's tester and lifts it to the window's origin, because a frame shot
589
734
  through the runner's fitting scale is a thumbnail of the surface. That couples it to Vitest's own
590
- tester layout, which is contract rather than accident: `vitest@4.1.11` is the version behind the
735
+ tester layout, which is contract rather than accident: `vitest@4.1.12` is the version behind the
591
736
  `iframe[data-vitest]` selector and the `--tester-transform`, `--tester-margin-left`,
592
737
  `--viewport-width`, and `--viewport-height` custom properties it writes. A release that renames any
593
738
  of them reddens the size check rather than writing a wrong frame. Always hand the pane back with
@@ -626,7 +771,9 @@ the body instead, taking the largest bottom edge in document coordinates plus th
626
771
  bottom margin, and adds the body's and the root's bottom padding and margin under them. An
627
772
  ancestor that clips its overflow (the `clipsOverflow` helper: an `overflow-y` value other than the
628
773
  `visible` keyword, or a paint containment) caps a descendant's edge at that ancestor's clip edge,
629
- which the `readClipEdge` helper reads. An `overflow-y` value of the `hidden` keyword, the `auto`
774
+ which the `readClipEdge` helper reads. The `collectClippingAncestors` helper finds those ancestors
775
+ through the flat tree, from an element to the slot it is assigned to, to its parent, and out of a
776
+ shadow root to its host, so a wrapper in a shadow tree around a slot clips what is slotted into it. An `overflow-y` value of the `hidden` keyword, the `auto`
630
777
  keyword, or the `scroll` keyword clips at the padding box, whatever the ancestor's
631
778
  `overflow-clip-margin` value selects. The `clip` keyword and a paint containment over a `visible`
632
779
  overflow clip at the box that value selects, the padding box by default, expanded by the length the
@@ -657,15 +804,27 @@ written at a height that is already wrong. That bound is 4: a document holding h
657
804
  fixed block settles in two restagings, one whose growth is capped part way settles in three, and
658
805
  the fourth is headroom.
659
806
 
660
- An element frame no taller than the declared height is shot in the declared pane, so its viewport
661
- lengths resolve against the declared viewport whether the element sits above the fold, below it, or
662
- fixed: a `50vh` element under a 900-row block reads back 422 rows in an 844-row pane. The re-reading
663
- takes the element's own height, so the pane grows only for an element taller than the declared
664
- height, and then to that element's height, which is what its viewport lengths resolve against.
807
+ An element that fits with its native scroll clearance is shot in the declared pane, so its viewport
808
+ lengths resolve against the declared viewport: a `50vh` element under a 900-row block reads back 422
809
+ rows in an 844-row pane. A short fixed panel that native alignment cannot move keeps that pane too.
810
+ A captured sticky element with a vertical start inset keeps the declared pane when native end
811
+ alignment places its box fully inside it. Its own inset determines that fitted placement; adding
812
+ the document's start-clearance gap can otherwise grow a viewport-bound height on every restaging.
813
+ This exception belongs to the captured sticky element, not to in-flow content it contains or a
814
+ region containing sticky children. A sticky declaration without a vertical start inset retains
815
+ the ordinary clearance behavior.
816
+ For an element whose height plus clearance exceeds the declared height, native start alignment
817
+ resolves the clearance, and the pane reserves the element's height plus the space above its aligned
818
+ top. This includes an element shorter than the declared pane. It honors authored root scroll padding
819
+ and target scroll margins, including computed lengths, without
820
+ parsing CSS or hiding a sticky header. The captured image still contains only the element's box.
821
+ An overlay without authored clearance remains visible in the image; the instrument detects no
822
+ arbitrary occlusion. Viewport-dependent clearance participates in the same bounded settling loop,
823
+ so a frame whose geometry has not settled within that bound is refused.
665
824
  Where the element's box lies outside the pane, the document is scrolled by the nearest distance that
666
825
  would bring it inside, and the box is read again. A fixed element past the pane can take a scroll
667
- that does not move it, and that scroll is handed back with the rest. The document is not scrolled for
668
- an element already inside the pane.
826
+ that does not move it, and that scroll is handed back with the rest. Other short elements that need no
827
+ additional room keep their nearest native alignment.
669
828
 
670
829
  The provider paints an element that fits the runner's window only where that window shows it. Where
671
830
  such an element lies past the window, the `captureFrame` function offsets the calling tester frame up
@@ -694,8 +853,10 @@ pixels; a file with no PNG header is refused without a size.
694
853
 
695
854
  `createPortfolio` refuses an unregistered variant name at creation, so a run cannot write a filename
696
855
  naming a combination it did not render. A portfolio left un-`enabled` is the ordinary run: `place`
697
- resizes nothing, writes nothing, and records nothing, so a journey calls it unconditionally. An
698
- enabled `place` applies the variant and writes `<directory>/<state>--<variant>.png` through
856
+ resizes nothing, writes nothing, and records nothing, so a journey calls it unconditionally. It
857
+ still refuses an unregistered state and a second placement of one state, so a journey that names a
858
+ state wrongly fails the ordinary run rather than the first capture run; `placements` and `paths`
859
+ stay empty, because they answer for what was captured. An enabled `place` applies the variant and writes `<directory>/<state>--<variant>.png` through
699
860
  `captureFrame`, so it stages the pane at the variant's size, covers the whole document, verifies the
700
861
  written bytes, and records only a path that read back as this run's own frame. The tester comes back
701
862
  at the viewport it had before the placement, so a journey that places a state carries on at its own
@@ -719,6 +880,12 @@ collision refusal before writing.
719
880
 
720
881
  Imported from `@orkestrel/test/server`.
721
882
 
883
+ The provider command consumes the browser journey's resolved selector inside Vitest's tester frame.
884
+
885
+ | API | Kind | Signature | Summary |
886
+ | ------------- | -------- | --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
887
+ | `chooseFiles` | function | `(context: ChooserContext, request: ChooserRequest) => Promise<void>` | Observes a native chooser before activating its input and supplies the verified chooser's fixture bytes. |
888
+
722
889
  #### Types
723
890
 
724
891
  A `Shape` cell holds an interface's data members as bare names in braces, `?` marking an optional
@@ -736,6 +903,7 @@ it adds after.
736
903
  | `InventoryOptions` | interface | `{ extensions?, exclude? }` | Configures a source inventory read. |
737
904
  | `UpgradeOptions` | interface | `WaitOptions` plus `{ path?, protocols? }` | Configures a client upgrade request. |
738
905
  | `UpgradeResult` | type | `{ claimed, protocol } \| { claimed, status }` | Represents what one server did with a client upgrade request. |
906
+ | `ChooserContext` | interface | `{ page, iframe }` | Supplies the real Playwright page and tester frame to a native chooser command. |
739
907
 
740
908
  #### Constants
741
909
 
@@ -753,7 +921,7 @@ A `Shape` cell holds the constant's declared type.
753
921
  | ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
754
922
  | `readInventory` | function | `(root: URL \| string, targets: readonly string[], options?: InventoryOptions) => Readonly<Record<string, string>>` | Reads files from selected targets below a root directory. |
755
923
  | `resolveContained` | function | `(root: string, target: string) => string \| undefined` | Resolves a target that stays below a root directory. |
756
- | `requireContained` | function | `(root: string, target: string) => string` | Resolves a target that stays below a root directory, refusing an escape. |
924
+ | `requireContained` | function | `(root: string, target: string) => string` | Resolves a target that stays below a root directory, refusing a path outside it. |
757
925
  | `hasScratchPath` | function | `(root: string, target: string) => boolean` | Reports whether a contained entry exists under a scratch directory without following its final link. |
758
926
  | `isExcluded` | function | `(key: string, exclusions: readonly string[]) => boolean` | Reports whether a root-relative key matches an exclusion. |
759
927
  | `readIdentity` | function | `(status: Stats) => ScratchIdentity` | Reads the identity of one allocated directory off a host status. |
@@ -792,10 +960,10 @@ delete `resolveContained` and import `resolveContainedPath` from `@orkestrel/sca
792
960
  package already carries as a `devDependency`, rather than adding a third variant.
793
961
 
794
962
  `requireContained` is that same resolution with the refusal every contained scratch operation makes
795
- of an escape: it throws `Path outside scratch directory: <target>` where `resolveContained` answers
963
+ of a path outside the root: it throws `Path outside scratch directory: <target>` where `resolveContained` answers
796
964
  `undefined`. Every `ScratchInterface` member and the `files` seeding pass through it, so the check
797
- and its one message are stated once rather than at each member. Read `resolveContained` where an
798
- escape is an answer the caller handles rather than a refusal it wants raised.
965
+ and its one message are stated once rather than at each member. Read `resolveContained` where
966
+ a path outside the root is an answer the caller handles rather than a refusal it wants raised.
799
967
 
800
968
  `isExcluded` is the exclusion rule itself, and `readInventory` applies it to a named target and a
801
969
  walked entry alike. An exclusion matches whole segments of a root-relative key, so it drops the key
@@ -806,9 +974,9 @@ is exported because a consumer walking its own tree wants that rule rather than
806
974
  what `exclude` means.
807
975
 
808
976
  `matchesIdentity` is the comparison `destroy()` makes before it removes anything: whether the
809
- identity read from the allocated path now is the identity recorded when the directory was allocated.
977
+ identity read from the allocated path is the identity recorded when the directory was allocated.
810
978
  All three fields are compared because none of them alone identifies an allocation. A device is
811
- shared by every directory on one filesystem, an index node is reused once its directory is removed,
979
+ shared by every directory on one filesystem, an index node is reused after its directory is removed,
812
980
  and a creation time repeats within the host's timestamp resolution. It is exported so a fixture that
813
981
  manages its own directory can make the same check rather than trusting a path. `readIdentity` reads
814
982
  that triple off a `node:fs` `Stats`, and it is the one reading `createScratch` takes at allocation,
@@ -867,7 +1035,7 @@ that names the mechanism it needs: an unprivileged Windows host answers `support
867
1035
  `true` through a junction while answering `supportsFileLinks` `false`, so a fixture that reads a
868
1036
  file through a link asks the second question rather than the first. `supportsMode` answers whether a
869
1037
  permission bit is stored, which is narrower than whether it is enforced — a POSIX host running as
870
- uid `0` stores every bit faithfully and bypasses the access check those bits describe, so a proof
1038
+ uid `0` stores every bit faithfully and is exempt from the access check those bits describe, so a proof
871
1039
  that needs a refusal probes that refusal itself.
872
1040
 
873
1041
  #### Factories
@@ -889,7 +1057,7 @@ starts the generated directory name, and defaults to `orkestrel-test-`; allocati
889
1057
  contains `/` or `\`, which is what stops a prefix steering the allocation out of its parent. Nothing
890
1058
  else is refused: a fragment carrying no separator is one path segment, so `release-0..2-` allocates.
891
1059
  `files` seeds files on allocation, keyed by path below the scratch directory; allocation removes the
892
- directory it recently made and rethrows when a key escapes or the host refuses a write.
1060
+ directory it recently made and rethrows when a key resolves outside it or the host refuses a write.
893
1061
 
894
1062
  `createLoopback` takes a `node:net` `Server` — `node:http`'s and `node:https`'s both extend it — and
895
1063
  never constructs one. It listens on port `0` at `127.0.0.1`, waits for the `listening` event, and
@@ -903,6 +1071,24 @@ this package never reserves a port number.
903
1071
  The call-signature members of each behavioral interface. Their `readonly` data members stay in the
904
1072
  earlier [Surface](#surface) rows.
905
1073
 
1074
+ #### `SheetAdoption`
1075
+
1076
+ The adoption releases only the sheet it owns.
1077
+
1078
+ | Method | Signature | Summary |
1079
+ | --------- | ------------ | ----------------------------------------------------------------------- |
1080
+ | `release` | `() => void` | Removes this sheet from the document without removing another adoption. |
1081
+
1082
+ Release an adoption after the fixture has read its sheet.
1083
+
1084
+ ```ts
1085
+ import type { SheetAdoption } from '@orkestrel/test/browser'
1086
+ import { adoptSheet } from '@orkestrel/test/browser'
1087
+
1088
+ const adopted: SheetAdoption = adoptSheet('@layer example {}')
1089
+ adopted.release()
1090
+ ```
1091
+
906
1092
  #### `RecorderInterface`
907
1093
 
908
1094
  | Method | Returns | Summary |
@@ -1086,51 +1272,66 @@ what the link reaches, not on what it stores.
1086
1272
  Every message `src/browser` throws. Keep them distinct: a journey asserts the one it means, and
1087
1273
  absent, present-but-gated, and ambiguous are different findings about an interface.
1088
1274
 
1089
- | Voice | Thrown by |
1090
- | ---------------------------------------------------------------------------------------- | ------------------------- |
1091
- | `No interactive element has the accessible name "<name>"` | `resolveRendered` |
1092
- | `Interactive target "<name>" is not visible and focus-reachable` | `resolveRendered` |
1093
- | `Interactive target "<name>" is ambiguous across <n> elements` | `resolveRendered` |
1094
- | `Interactive target "<name>" could not be resolved` | `resolveRendered` |
1095
- | `Interactive target "<name>" is unreachable after scrolling` | `resolveAccessible` |
1096
- | `Interactive target "<name>" is not reachable inside "<region>"` | `resolveAccessibleWithin` |
1097
- | `Interactive target "<name>" is ambiguous across <n> elements inside "<region>"` | `resolveAccessibleWithin` |
1098
- | `Interactive target "<name>" could not be resolved inside "<region>"` | `resolveAccessibleWithin` |
1099
- | `Native disclosure "<name>" is not visible and focus-reachable` | `clickDisclosure` |
1100
- | `Native disclosure "<name>" is ambiguous across <n> elements` | `clickDisclosure` |
1101
- | `Native disclosure "<name>" could not be resolved` | `clickDisclosure` |
1102
- | `Interactive target "<name>" is not reachable through forward Tab traversal: <trail>` | `driveTraversal` |
1103
- | `Named region "<name>" is not visible` | `readPerception` |
1104
- | `Named region "<name>" is ambiguous across <n> elements` | `readPerception` |
1105
- | `Named region "<name>" could not be resolved` | `readPerception` |
1106
- | `Interactive target "<name>" does not carry a value` | `readValue` |
1107
- | `Computed foreground color is unavailable` | `readContrast` |
1108
- | `Computed background color is unavailable` | `readContrast` |
1109
- | `Tester pane is unavailable for a capture` | `stagePane` |
1110
- | `Tester pane rendered <w>x<h> for a <w>x<h> viewport` | `stagePane` |
1111
- | `Capture frame at <path> never settled after <n> restagings: <h> over a <h> pane` | `captureFrame` |
1112
- | `Capture frame was written to <path> where <path> was asked for` | `captureFrame` |
1113
- | `Capture frame at <path> is not the one this run shot` | `captureFrame` |
1114
- | `Capture frame at <path> could not be read` | `readFrame` |
1115
- | `Capture frame at <path> is not an image this browser decodes` | `readFrame` |
1116
- | `Capture frame at <path> is not an image this browser decodes: <w>x<h> device pixels` | `readFrame` |
1117
- | `Capture frame at <path> cannot be measured without a 2D canvas` | `readFrame` |
1118
- | `Capture variant "<name>" is not registered` | `createPortfolio` |
1119
- | `Capture state "<state>" is not registered` | `place` |
1120
- | `Capture state "<state>" is already placed` | `place` |
1121
- | `IndexedDB database "<name>" could not be deleted` | `removeDatabase` |
1122
- | `IndexedDB database "<name>" is blocked by an open connection` | `removeDatabase` |
1123
- | `Key sequence "<keys>" was sent with nothing focused` | `pressKeys` |
1124
- | `Condition "<subject>" did not hold within <n>ms (waited <n>ms) (last states: <states>)` | `waitForState` |
1125
- | `Animation subject is not connected` | `waitForAnimations` |
1126
- | `Animation "<subject>" did not settle within <n>ms (waited <n>ms): <animations>` | `waitForAnimations` |
1127
- | `Class census walked no element` | `readCensus` |
1128
- | `Contrast control cannot straddle the bar <bar>` | `buildContrast` |
1129
- | `Access is denied for <operation> "<key>"` | `buildDenial` |
1130
- | `No room is left for <key>` | `createStorage` |
1131
- | `Storage quota must be a non-negative integer` | `createStorage` |
1132
- | `Statechart harness mounted no transition` | `createHarness` |
1133
- | `Statechart harness carries no status` | `status` |
1275
+ | Voice | Thrown by |
1276
+ | ---------------------------------------------------------------------------------------- | ---------------------------------------------- |
1277
+ | `No interactive element has the accessible name "<name>"` | `resolveRendered` |
1278
+ | `Interactive target "<name>" is not visible and focus-reachable` | `resolveRendered` |
1279
+ | `Interactive target "<name>" is ambiguous across <n> elements` | `resolveRendered` |
1280
+ | `Interactive target "<name>" could not be resolved` | `resolveRendered` |
1281
+ | `Interactive target "<name>" is unreachable after scrolling` | `resolveAccessible` |
1282
+ | `Interactive target "<name>" is not reachable inside "<region>"` | `resolveAccessibleWithin` |
1283
+ | `Interactive target "<name>" is ambiguous across <n> elements inside "<region>"` | `resolveAccessibleWithin` |
1284
+ | `Interactive target "<name>" could not be resolved inside "<region>"` | `resolveAccessibleWithin` |
1285
+ | `Native disclosure "<name>" is not visible and focus-reachable` | `clickDisclosure` |
1286
+ | `Native disclosure "<name>" is ambiguous across <n> elements` | `clickDisclosure` |
1287
+ | `Native disclosure "<name>" could not be resolved` | `clickDisclosure` |
1288
+ | `The element is not connected to a document, so it has no point to read` | `readProtocolPoint` |
1289
+ | `Interactive target "<name>" is not reachable through forward Tab traversal: <trail>` | `driveTraversal` |
1290
+ | `Named region "<name>" is not visible` | `readPerception` |
1291
+ | `Named region "<name>" is ambiguous across <n> elements` | `readPerception` |
1292
+ | `Named region "<name>" could not be resolved` | `readPerception` |
1293
+ | `Interactive target "<name>" does not carry a value` | `readValue` |
1294
+ | `Computed foreground color is unavailable` | `readContrast` |
1295
+ | `Computed background color is unavailable` | `readColorContrast` |
1296
+ | `Color "<value>" is unreadable` | `readColorContrast` |
1297
+ | `Contrast opacity must be between 0 and 1` | `readColorContrast` |
1298
+ | `The element is not connected to a document, so it has no clipping ancestors to read` | `collectClippingAncestors`, `readClippedBlock` |
1299
+ | `Tester pane is unavailable for a capture` | `stagePane` |
1300
+ | `Tester pane rendered <w>x<h> for a <w>x<h> viewport` | `stagePane` |
1301
+ | `Capture frame at <path> never settled after <n> restagings: <h> over a <h> pane` | `captureFrame` |
1302
+ | `Capture frame was written to <path> where <path> was asked for` | `captureFrame` |
1303
+ | `Capture frame at <path> is not the one this run shot` | `captureFrame` |
1304
+ | `Capture frame at <path> could not be read` | `readFrame` |
1305
+ | `Capture frame at <path> is not an image this browser decodes` | `readFrame` |
1306
+ | `Capture frame at <path> is not an image this browser decodes: <w>x<h> device pixels` | `readFrame` |
1307
+ | `Capture frame at <path> cannot be measured without a 2D canvas` | `readFrame` |
1308
+ | `Shot is not an image this browser decodes` | `readShotPixel` |
1309
+ | `Shot cannot be measured without a 2D canvas` | `readShotPixel` |
1310
+ | `Shot pixel position must be a fraction from 0 to 1` | `readShotPixel` |
1311
+ | `Capture variant "<name>" is not registered` | `createPortfolio` |
1312
+ | `Capture state "<state>" is not registered` | `place` |
1313
+ | `Capture state "<state>" is already placed` | `place` |
1314
+ | `IndexedDB database "<name>" could not be deleted` | `removeDatabase` |
1315
+ | `IndexedDB database "<name>" is blocked by an open connection` | `removeDatabase` |
1316
+ | `Key sequence "<keys>" was sent with nothing focused` | `pressKeys` |
1317
+ | `Focus sits in a frame from another origin, which cannot be read` | `readActiveElement` |
1318
+ | `Focus traversal did not end` | `walkFocus` |
1319
+ | `The start element cannot take focus` | `collectTabStops` |
1320
+ | `Stability frames must be a positive integer` | `waitForStability` |
1321
+ | `Condition "<description>" did not hold within <n>ms (waited <n>ms)` | `waitForStability` |
1322
+ | `Condition "<subject>" did not hold within <n>ms (waited <n>ms) (last states: <states>)` | `waitForState` |
1323
+ | `Animation subject is not connected` | `waitForAnimations` |
1324
+ | `Animation "<subject>" did not settle within <n>ms (waited <n>ms): <animations>` | `waitForAnimations` |
1325
+ | `Class census walked no element` | `readCensus` |
1326
+ | `Contrast control cannot straddle the bar <bar>` | `buildContrast` |
1327
+ | `Access is denied for <operation> "<key>"` | `buildDenial` |
1328
+ | `No room is left for <key>` | `createStorage` |
1329
+ | `Storage quota must be a non-negative integer` | `createStorage` |
1330
+ | `Statechart harness mounted no transition` | `createHarness` |
1331
+ | `Statechart harness carries no status` | `status` |
1332
+
1333
+ `readContrast` measures through `readColorContrast`, so its background refusal is that row's voice,
1334
+ and `readRing` never raises it, because it names `CANVAS_COLOR` as the floor on every reading.
1134
1335
 
1135
1336
  Some of those rows are not plain `Error` messages. `buildDenial` returns a `DOMException` named
1136
1337
  `SecurityError`, `createStorage` raises that one from every operation the permission withholds and a
@@ -1144,7 +1345,8 @@ states it last read, so an exhausted wait says what the control was announcing i
1144
1345
  `waitForAnimations` names the subject through `readRole` and `readName` and lists the animations
1145
1346
  still running. Both validate their bounds through `checkBounds`, which raises
1146
1347
  `Wait budget must be finite and non-negative` for the first and `Animation budget must be finite and
1147
- non-negative` for the second.
1348
+ non-negative` for the second. `settleCapture` raises the `waitForAnimations` voices unchanged, and
1349
+ because it waits on the document's root element, an exhausted settle names `html` as its subject.
1148
1350
 
1149
1351
  Some of them are narrowing rather than findings, and no input reaches them. Each `could not be
1150
1352
  resolved` is one: a preceding length check does not narrow the later lookup under
@@ -1162,27 +1364,39 @@ disk disagrees with the bytes the provider handed back — which a provider that
1162
1364
  never produces, so the suite proves that comparison discriminates with a planted file rather than by
1163
1365
  reaching the refusal. `Capture frame at <path> cannot be measured without a 2D canvas` is narrowing
1164
1366
  of the same kind: a canvas allocated for this reading and asked for no other context type hands one
1165
- back. The remaining refusals of the `readFrame` function are driven, by a path holding no file, by a
1367
+ back. `readShotPixel` carries the same pair for a shot the provider hands back rather than a file:
1368
+ `Shot cannot be measured without a 2D canvas` is that narrowing, and `Shot is not an image this
1369
+ browser decodes` fires only where the provider returns bytes that are not the image it shot. Both
1370
+ readers decode and draw through `paintImage`, which hands the decode failure and a missing context
1371
+ on to the caller to word, so the decode is driven there with bytes that are no image. The
1372
+ `Shot pixel position must be a fraction from 0 to 1` refusal is driven, by a fraction below `0`,
1373
+ above `1`, and `NaN`. The remaining refusals of the `readFrame` function are driven, by a path holding no file, by a
1166
1374
  file holding no image, and by a PNG header over no image data, and so is
1167
1375
  `Capture frame at <path> never settled after <n> restagings`, by a fixture whose full-height panel
1168
1376
  grows with every pane the capture stages.
1169
1377
 
1170
1378
  The pointer, pseudo-element, and media helpers add the following voices.
1171
1379
 
1172
- | Voice | Thrown by |
1173
- | ------------------------------------------------------------- | -------------- |
1174
- | `Browser provider exposes no DevTools session` | `sendProtocol` |
1175
- | `Pointer is already held at <x>x<y>` | `driveHold` |
1176
- | `Interactive target "<name>" did not enter the pressed state` | `driveHold` |
1177
- | `Pseudo-element "<pseudo>" must start with "::"` | `readStyle` |
1178
- | `Pseudo-element "<pseudo>" is not one this engine exposes` | `readStyle` |
1179
- | `Media emulation was staged with nothing to emulate` | `stageMedia` |
1180
- | `Media emulation did not reach the tester: <query>` | `stageMedia` |
1181
- | `Media emulation did not clear from the tester` | `releaseMedia` |
1380
+ | Voice | Thrown by |
1381
+ | ------------------------------------------------------------- | ------------------ |
1382
+ | `Browser provider exposes no DevTools session` | `sendProtocol` |
1383
+ | `Pointer is already held at <x>x<y>` | `driveHold` |
1384
+ | `Interactive target "<name>" did not enter the pressed state` | `driveHold` |
1385
+ | `Protocol search returned no search id` | `readProtocolNode` |
1386
+ | `Protocol search found <n> elements where one is required` | `readProtocolNode` |
1387
+ | `Protocol search returned no node id` | `readProtocolNode` |
1388
+ | `Pseudo-element "<pseudo>" must start with "::"` | `readStyle` |
1389
+ | `Pseudo-element "<pseudo>" is not one this engine exposes` | `readStyle` |
1390
+ | `Media emulation was staged with nothing to emulate` | `stageMedia` |
1391
+ | `Media emulation did not reach the tester: <query>` | `stageMedia` |
1392
+ | `Media emulation did not clear from the tester` | `releaseMedia` |
1182
1393
 
1183
1394
  The resolver's absent, gated, ambiguous, and unreachable voices stay unchanged. The DevTools-session
1184
1395
  and media-delivery refusals guard provider failures the managed Chromium suite doesn't induce.
1185
1396
  The suite exercises real delivery rather than substituting a provider that manufactures a failure.
1397
+ `readAccessibilityNode` raises the `readProtocolNode` voices unchanged. Of those, `found <n>
1398
+ elements` is driven, by a detached element. The other two narrow a reply that omits `searchId` or
1399
+ `nodeIds`, which the browser never sends, so they guard a provider that does.
1186
1400
 
1187
1401
  ### Refusals outside the journey layer
1188
1402
 
@@ -1258,7 +1472,7 @@ These hold across `src/core`, `src/browser`, `src/server`, and this guide.
1258
1472
  nor a directory, or when a target resolves outside the root. A missing target surfaces the host's
1259
1473
  own `ENOENT` rather than a message from this package. It skips a symlink met while walking rather
1260
1474
  than following it. A target may be written relative to the root or as an absolute path inside it,
1261
- and one that escapes is refused either way.
1475
+ and one that resolves outside the root is refused either way.
1262
1476
 
1263
1477
  A link in the **middle** of a named target is a separate refusal, and the only one that reaches
1264
1478
  it: the symbolic-link check reads the final segment, which such a link is not. The named target is
@@ -1289,13 +1503,13 @@ These hold across `src/core`, `src/browser`, `src/server`, and this guide.
1289
1503
  case are the same file varies by filesystem, so the suite probes the running host and asserts
1290
1504
  what the probe returned instead of assuming either answer.
1291
1505
 
1292
- 7. **`createScratch` refuses a lexical escape, not a symbolic link.** It allocates with
1506
+ 7. **`createScratch` refuses a path lexically outside its directory, not a symbolic link.** It allocates with
1293
1507
  `mkdtempSync` below `parent`, which creates the directory at mode `0700` on a host that applies
1294
1508
  POSIX permission bits. The suite gates that assertion on `supportsMode`, so the mode is proven
1295
1509
  where the probe answers `true` and the case is skipped where it answers `false`. A Windows host
1296
1510
  answers `false` and reads the same allocation back as `0666`, so the bits describe nothing it
1297
1511
  applies. Every member that takes a target — `write`, `read`, `has`, `names`, `ensure`, `link`,
1298
- and `remove` — throws when that target lexically escapes the allocated directory, and a failed
1512
+ and `remove` — throws when that target lexically resolves outside the allocated directory, and a failed
1299
1513
  seed removes the directory before rethrowing. `remove` adds a refusal the others do not need: a
1300
1514
  target naming the allocation itself, lexically or through an intermediate symbolic link. The
1301
1515
  lexical half compares paths. The physical half reads only the final entry with `lstat` and
@@ -1392,7 +1606,7 @@ These hold across `src/core`, `src/browser`, `src/server`, and this guide.
1392
1606
  lists every member with the reading it breaks. Every member makes a naive read throw or violates
1393
1607
  a naive structural assumption; a total guard survives every member without throwing. Each has a
1394
1608
  direct probe for that failure. The negative control keeps an inert value from entering the corpus
1395
- under a hostile name. Whether it accepts or refuses one is that guard's own contract. Membership
1609
+ under a name suggesting untrusted input. Whether it accepts or refuses one is that guard's own contract. Membership
1396
1610
  may grow in a release, so consumers loop over the whole array, assert their guard's expected
1397
1611
  answer per index, and attribute each failure by that index instead of naming or counting members
1398
1612
  locally.
@@ -1402,10 +1616,11 @@ These hold across `src/core`, `src/browser`, `src/server`, and this guide.
1402
1616
  into a description of the markup. `build` creates a node, `mount` attaches one, `render` does
1403
1617
  both, `clearStorage` takes nothing at all, and `removeDatabase` takes a database name. The
1404
1618
  predicates, the element readers, and the describers do take a node —
1405
- `isRendered`, `isReachable`, `readHit`, `readText`, `readRole`, `readName`, `readStates`,
1619
+ `isRendered`, `isReachable`, `supportsSegmentedFocus`, `readHit`, `readText`, `readRole`, `readName`, `readStates`,
1406
1620
  `readCensus`, `describeTree`, `describeFocus`, `extractOrphans`, `readRows`, `readStyle`,
1407
- `readToken`, `readPixels`, `readContrast`, `readLayers`, `readBackdrop`, and `readRing` — and
1408
- each is a reader of a node the caller already has rather than a verb that acts on a target.
1621
+ `readToken`, `readPixels`, `readContrast`, `readColorContrast`, `readLayers`, `readBackdrop`,
1622
+ and `readRing` — and each is a reader of a node the caller already has rather than a verb that
1623
+ acts on a target.
1409
1624
  `waitForAnimations` takes one as the subject of a wait, and waiting for a browser to stop
1410
1625
  painting it changes nothing about it. `buildContrast`, `buildEscapes`, and `buildCensus` return
1411
1626
  detached nodes for the caller to append and remove, the way `build` does. `captureFrame`
@@ -1504,7 +1719,7 @@ These hold across `src/core`, `src/browser`, `src/server`, and this guide.
1504
1719
  stopped, and `steps` and `output` hand out snapshots. There is no shared instance: a file that
1505
1720
  needs one journal per scenario creates one per scenario.
1506
1721
  18. **The capture layer depends on the Vitest runner's own tester layout, deliberately.** A
1507
- screenshot is taken off the page the runner painted, and `vitest@4.1.11` lays its tester out
1722
+ screenshot is taken off the page the runner painted, and `vitest@4.1.12` lays its tester out
1508
1723
  inside a smaller page and fits it by scaling the pane the tester sits in, so a frame shot through
1509
1724
  that scale is a thumbnail of the surface. `stagePane` therefore reaches into that layout: the
1510
1725
  `iframe[data-vitest]` selector and the `--tester-transform`, `--tester-margin-left`,
@@ -1526,7 +1741,8 @@ These hold across `src/core`, `src/browser`, `src/server`, and this guide.
1526
1741
  the one before it produced so a converging document lands on its fixed point rather than
1527
1742
  creeping toward it. The re-reading is bounded by `CAPTURE_STAGINGS`, and a document still
1528
1743
  growing at that bound is refused rather than photographed at a stale height. An element frame
1529
- keeps the declared pane unless its element is taller, and offsets the calling tester frame only
1744
+ keeps the declared pane unless its element needs more room with its native scroll clearance,
1745
+ and offsets the calling tester frame only
1530
1746
  as far as brings the element inside the runner's window. What
1531
1747
  the capture borrows it gives back: the `releasePane` function returns the tester to the viewport
1532
1748
  it held before the staging, the capture then restores the tester's scroll position, even where
@@ -1553,6 +1769,13 @@ These hold across `src/core`, `src/browser`, `src/server`, and this guide.
1553
1769
  still read `running` would strand the gate on a reading the harness never leaves. The gate stays
1554
1770
  outside this package: the harness carries its own tally, so nothing here reads a harness back,
1555
1771
  and no page is generated or published to host one.
1772
+ 20. **Sheet adoption releases only its own stylesheet.** Repeated release leaves other adoptions
1773
+ intact. [`tests/src/browser/sheets/helpers.test.ts`](../tests/src/browser/sheets/helpers.test.ts)
1774
+ proves independent adoptions and repeated release.
1775
+ 21. **File drags carry trusted `Files` events to rendered targets.** A destination need not be
1776
+ focusable. The file-drag cases in
1777
+ [`tests/src/browser/journeys/helpers.test.ts`](../tests/src/browser/journeys/helpers.test.ts)
1778
+ prove trusted entry, over, and leave events over a status outside the Tab order.
1556
1779
 
1557
1780
  ### Threat model
1558
1781
 
@@ -1566,15 +1789,15 @@ that applies POSIX permission bits, and the suite asserts that mode where `suppo
1566
1789
  answers `false` the mode protects nothing, and the allocation carries only the access its parent
1567
1790
  directory already gives. The mode does not keep out a sibling test worker or the code under test on
1568
1791
  any host, because both run as the same uid, and they are the population that would create a link
1569
- here. Its containment check is lexical: it refuses a relative path that escapes the allocated
1792
+ here. Its containment check is lexical: it refuses a relative path that resolves outside the allocated
1570
1793
  directory, which is the accident that actually happens — a test writing `../foo`. It does not walk
1571
1794
  the path's segments for symbolic links, because per-segment walking is sandbox behavior and this is
1572
1795
  not a sandbox.
1573
1796
 
1574
1797
  So a link inside the allocation was created by the test process or by the code the test drives, and
1575
1798
  handing `scratch.path` to the code under test is the ordinary use of this helper. `link` is this
1576
- package's own entry in that population: it creates a symbolic link on request, it refuses only an
1577
- escaping target, and its source may name anything. The source stays unchecked except on the fallback
1799
+ package's own entry in that population: it creates a symbolic link on request, it refuses only a
1800
+ target outside its directory, and its source may name anything. The source stays unchecked except on the fallback
1578
1801
  path, which stats it to decide whether a junction can point there. [Traversal](#traversal) states
1579
1802
  what each member does with a link it meets, and a contained path reaching outside the allocation
1580
1803
  through one is the result. This helper does not defend against that.
@@ -1618,7 +1841,7 @@ a time in the following table, and some of them ship while others do not.
1618
1841
 
1619
1842
  A member is one implementation group carried by one package, under whatever name that package
1620
1843
  spells it and whether it exports the helper or declares it inside a test file. Repeated calls routed
1621
- through one shared implementation stay one member, and a set of adversarial values fed through one
1844
+ through one shared implementation stay one member, and a set of unexpected values fed through one
1622
1845
  totality loop is one member rather than one per value. Read the first column as the group rather
1623
1846
  than as an export: **nothing in this section is importable**, and the only names you can install are
1624
1847
  in [Surface](#surface).
@@ -1630,12 +1853,12 @@ or when a consumer appears the ruling did not consider.
1630
1853
  | Candidate | Ruling | Why |
1631
1854
  | ------------------------------------------------------------------------------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1632
1855
  | A recorder map over an emitter's events, with its map, event-map, subscriber, and totality types | Ships | It ships as `createRecorders`, with `RecorderMap` and `EventSourceInterface` beside it. Inference is what the earlier refusal turned on, and the shape improves it without settling it: a source parameter typed `EventSourceInterface<TMap>` is an inference site, so a call against one names no type argument, while a concrete class supplies none and the call names both. A keying limit survives that: `TName` derives from the events array's element type, so an array declared with a wider union than its contents keys the map past the events actually listed, and [Bounds a shipped helper carries](#bounds-a-shipped-helper-carries) states what to pass instead. A published signature still cannot import a consumer's event map, so the interface asks for the subscribe half alone and the consumer's own map is what it is instantiated with. |
1633
- | Hostile guard-input sets | Ships | `form`, `table`, and `supervisor` each feed one adversarial set through total readers, and the set is a mechanism rather than a policy: a guard's own contract decides the answers, not the corpus. It ships as `createHostileValues`; its members do not each become a factory, and every member carries a naive-reader negative control. |
1856
+ | Unexpected guard-input sets | Ships | `form`, `table`, and `supervisor` each feed one unexpected-value set through total readers, and the set is a mechanism rather than a policy: a guard's own contract decides the answers, not the corpus. It ships as `createHostileValues`; its members do not each become a factory, and every member carries a naive-reader negative control. |
1634
1857
  | Raw invocation — `invokeRaw` | Ships | It ships as `invokeUnchecked`, with `readProperty` beside it for the read. Native `Reflect.apply` still makes the call; what these add is the boundary — a callability refusal before the call, a target refusal before the read, and one named place where an unchecked runtime result meets the type its caller claims. The claim stays the caller's, and so does the guard that narrows what came back. Without them a consumer that bans `as` cannot drive a foreign object at all. |
1635
1858
  | Condition polling — wall-clock predicate loops | Ships | The wait-family contract states the distinction: the no-polling architecture law governs a product's idle wakeup, and a test instrument waiting on a fact another process produces has no event to park on. It ships as `waitForCondition`. `retryUntil` ships on the same reading, because retrying a real operation is not re-reading a predicate; and where an event does exist, `waitForEvent` is the door. |
1636
1859
  | Deep nesting beyond a guard's cap | Refused | `table` builds a record chain and `supervisor` builds nested arrays. The two nest different containers, so one shared factory needs a selector argument that changes the construction algorithm — a mode switch rather than a mechanism. |
1637
1860
  | Canonical wire fixpoint assertions | Refused | `form` and `table` each serialize, parse untrusted JSON, serialize again, and compare exact bytes. The comparison is an assertion over the consumer's own codecs rather than a reusable mechanism, so the shape stays consumer-local and [Prove a wire fixpoint](#prove-a-wire-fixpoint) publishes the pattern instead. |
1638
- | Numeric corpora, hostile-key tables, and deep-freeze | Refused | A numeric corpus or a hostile-object table is test policy — what a given suite decided to check — rather than a mechanism, and one factory covering the variants would need a mode argument. `createHostileValues` ships because a guard's totality is a property of the guard; these encode a decision about coverage. |
1861
+ | Numeric corpora, unexpected-key tables, and deep-freeze | Refused | A numeric corpus or an unexpected-object table is test policy — what a given suite decided to check — rather than a mechanism, and one factory covering the variants would need a mode argument. `createHostileValues` ships because a guard's totality is a property of the guard; these encode a decision about coverage. |
1639
1862
  | Clearing web storage between tests | Ships | Emptying local and session storage together is one mechanism, and the `afterEach` hook that must run after a failed test too is where every browser suite needs it. It ships as `clearStorage`. |
1640
1863
  | Class-ancestry orphan detection | Ships | A rendered element carrying a child class with no container class above it is a real invariant a stylesheet cannot state, and the check is mechanism when the class names are parameters rather than one framework's. It ships as `extractOrphans(root, child, parent)`. |
1641
1864
  | A DOM element builder | Ships | It ships as `build` for the element and `mount` for the attachment, and `render` widened to take a tag and its class list as well as markup. A class list, a text, and an attribute map are what a fixture actually varies, and expressing that variation through markup means assembling a string. Nothing here assembles a tree one call at a time: a fixture with children is still written as markup. |
@@ -1706,16 +1929,59 @@ the helper rather than to the host, and each names what to reach for instead.
1706
1929
  used; it is covered by review. The measured layout is a single accessible, uniformly scaled tester
1707
1930
  iframe. A covered centre or unsupported geometry can refuse even when the resolver accepts the
1708
1931
  target. The verb inherits the resolver's focus reachability conditions.
1932
+ - **`parkPointer` ends hover and never ends a press.** It moves the pointer to (-1, -1) and waits for
1933
+ a frame, and it sends no button-up, so a press that the `holdAccessible` verb holds stays recorded under
1934
+ `POINTER_HOLD` and its button-up is still owed to `releasePointer`. The control the press was on
1935
+ loses both its `:hover` paint and its `:active` paint when the pointer leaves it. Use it where a case clears hover with no
1936
+ press held, and never as a teardown for a hold.
1709
1937
  - **`releasePointer` parks the pointer outside the page.** It releases at the recorded point first,
1710
- which can produce a click. It then clears hover by moving to (-1, -1) in the runner page's
1711
- coordinates, one pixel above and to the left of that page's viewport. The browser hit-tests
1712
- nothing outside the viewport, so no element takes a `mouseover` event or hover paint from the
1713
- parked pointer until the next pointer verb. This holds even where a staging, scroll, or offset
1714
- lays content over the park point. Register it with `afterEach` before a hover
1938
+ which can produce a click. It then clears hover through `parkPointer`, which moves to (-1, -1) in
1939
+ the runner page's coordinates, one pixel above and to the left of that page's viewport. The
1940
+ browser hit-tests nothing outside the viewport, so no element takes a `mouseover` event or hover
1941
+ paint from the parked pointer until the next pointer verb. This holds even where a staging,
1942
+ scroll, or offset lays content over the park point. Register it with `afterEach` before a hover
1715
1943
  or hold. A second release sends no button-up event. Holds must not overlap. A rejected press
1716
1944
  leaves no marker. A rejected release keeps the marker for a retry and still attempts the park. If
1717
1945
  that park also rejects, the park's error surfaces with the release rejection attached in the
1718
1946
  aggregate's errors; the park rejection is its cause.
1947
+ - **`walkFocus` reports every stop it can read and refuses none of them.** It records the focused
1948
+ element and then presses Tab through `stepFocus`, the one Tab loop that `driveTraversal` shares,
1949
+ and returns what focus reached, so a focusable scroll container that Chromium focuses without a
1950
+ `tabindex` is a stop, and `describeFocus` omits it. Each stop is the innermost element
1951
+ `readActiveElement` reads, so a control inside an open shadow root or a same-origin frame is a stop
1952
+ of its own and its host or frame element is not. A non-HTML control inside one, such as an SVG
1953
+ element, reads as `undefined` rather than as its host, and `readFocus` follows it. A closed shadow root cannot be entered, so its
1954
+ host stands for what it holds, and focus in a frame from another origin throws, because the page
1955
+ cannot name a control there. A hidden or disabled stop is returned like any other. A consumer that
1956
+ refuses one throws from the predicate it passes. The walk ends where focus returns to the body or
1957
+ revisits a stop, except that consecutive landings on the same native segmented input pass through
1958
+ its segments and contribute one stop, up to `FOCUS_SEGMENT_LIMIT` landings including the initial
1959
+ focused element. A longer run or a consecutive repeat on any other element ends the walk.
1960
+ Any different landing starts a separate run. The press cap is `FOCUS_SEGMENT_LIMIT` times the count `countElements` reads, which includes
1961
+ open shadow roots and same-origin frames; exceeding it throws. A control inside a same-origin frame belongs to the
1962
+ frame's window, so it fails this window's `instanceof HTMLElement`. `collectTabStops` is the same
1963
+ walk bounded to a region by `containsNode`, which places a control inside an open shadow root or a
1964
+ same-origin frame in the region that holds its host or frame element. The start element is the
1965
+ walk's origin and never its end, so a start immediately before the region, outside it, reaches the
1966
+ region's first stop with the first press.
1967
+ - **`waitForStability` compares the last `frames + 1` reads.** The first read is immediate and each
1968
+ later read follows a painted frame, with the interval slept between polls, so the reads are not on
1969
+ consecutive frames when the interval is longer than one. A reading that settles on read `n`
1970
+ resolves on read `n + frames`. The default is four frames; the budget and interval are those of
1971
+ `waitForCondition`. A reading that changes on every read exhausts the budget, and the refusal names
1972
+ the description. The budget and the abort signal are checked between reads, so a frame that never
1973
+ paints is not bounded by either. The `releaseMedia` function with nothing staged waits through it
1974
+ for one frame.
1975
+ - **`settleCapture` holds endless animations until its resume runs.** It parks the pointer through
1976
+ `parkPointer`, so a held press stays held and only loses its hover and pressed paint; `pointer: false`
1977
+ leaves the pointer, its hover, and that paint where they are. It waits out every finite animation
1978
+ in the document, root element included, then pauses each endless one at its first frame and
1979
+ resolves to the resume. Call the resume from a `finally` block after the frame is shot. A settle
1980
+ whose wait exhausts throws before it holds anything, so it owes no resume.
1981
+ - **`readShotPixel` reads a shot at the runner's scale.** The position is a fraction of the shot,
1982
+ and a fraction of `1` reads its last row or column. Where the runner scales the tester, the shot's
1983
+ edge pixels can blend with what lies beside the element, so read a pixel inside a painted band rather
1984
+ than on its edge.
1719
1985
  - **`stageMedia` overrides the provider page, and `releaseMedia` restores the first stage's
1720
1986
  readings.** Pin a base with `{ motion: true }` rather than assuming the host prefers motion.
1721
1987
  `motion: false` stages reduced motion; `print: true` stages print, and `print: false` stages
@@ -1724,28 +1990,70 @@ the helper rather than to the host, and each names what to reach for instead.
1724
1990
  other emulated feature the provider configured is cleared. An empty `{}`
1725
1991
  refuses. Each staged query has a 1000 ms read-back budget with a 10 ms poll interval; refusal
1726
1992
  restores the carried pre-call readings before throwing and can take two budgets. A restoration
1727
- failure is attached as the refusal's cause. The read-back exhaustion path can't be driven from
1728
- inert input against a conforming engine; its restoration is covered by review and by the shared
1729
- payload code, not by a case. The first stage records print, reduced motion, dark colour scheme,
1730
- and forced colours on the tester root under `MEDIA_STAGE`, as a bit string in that order (`1` for
1731
- a match, `0` otherwise). Later stages keep that marker. Release restores those readings as
1732
- explicit emulation rather than removing every override, waits per axis for its recorded value, and
1733
- removes the marker. A release whose wait exhausts keeps `MEDIA_STAGE` on the tester root, so the
1734
- next `releaseMedia` retries from the same recorded readings. A release with nothing staged clears
1735
- every override and compares readings taken strictly after the reset until they are stable.
1993
+ failure is attached as the refusal's cause. The pointer refusal proof passes an unsupported runtime
1994
+ pointer value through the JavaScript boundary and checks restoration after read-back exhaustion.
1995
+ The first stage records print, reduced motion, dark colour scheme, and forced colours on the
1996
+ tester root under `MEDIA_STAGE`, as a bit string in that order (`1` for a match, `0` otherwise).
1997
+ The first pointer stage appends a fifth bit for the pre-call coarse-pointer reading; later pointer
1998
+ stages retain it. A stage without a pointer keeps the four-bit marker until a pointer stage occurs.
1999
+ Release restores those readings as explicit emulation rather than removing every override, waits
2000
+ per axis for its recorded value, and removes the marker. A release whose wait exhausts keeps
2001
+ `MEDIA_STAGE` on the tester root, so the next `releaseMedia` retries from the same recorded
2002
+ readings. A release with nothing staged clears media overrides and compares readings taken
2003
+ strictly after the reset until they are stable.
1736
2004
  Stability doesn't prove the engine's own baseline. Each release read-back has a 1000 ms budget and
1737
2005
  waits for a frame per poll, with 10 ms between polls. The budget is checked between polls, so a
1738
2006
  frame that never paints is not bounded by it. Media scopes must not overlap. The browser project
1739
2007
  runs files serially.
2008
+ - **Pointer emulation also changes hover.** `pointer: 'coarse'` enables touch emulation with one
2009
+ touch point and reads `(pointer: coarse)`, `(any-pointer: coarse)`, `(hover: none)`, and
2010
+ `(any-hover: none)`. `pointer: 'fine'` disables touch emulation and reads fine precision with
2011
+ hover support. A pointer stage and its release preserve precision, not an earlier maximum
2012
+ touch-point count. Staging and releasing media without a pointer axis sends no touch-emulation
2013
+ command, including an unstaged release.
2014
+ - **Touch input requires a Chromium protocol session.** `tapTouch` enables touch emulation, waits
2015
+ for the touch layout's box to settle, then sends a single-point start and end. It does not scroll
2016
+ or check focus reachability. A tap that
2017
+ stops a scroller's momentum can produce no click. `releaseTouch` tolerates Chromium's idle
2018
+ cancellation refusal, disables emulation, and parks without sending a mouse release. Parking
2019
+ does not prove that sticky touch hover cleared. A native drag holding the mouse needs its own
2020
+ `mouseReleased` protocol command before parking. Touch cleanup also disables a coarse pointer
2021
+ staged by `stageMedia`, leaving `(pointer: fine)` while `MEDIA_STAGE` stays unchanged. Stage
2022
+ `pointer: 'coarse'` again after cleanup when later steps need it. `releaseMedia` still restores
2023
+ the reading recorded before the first pointer stage. A first pointer stage while tap emulation
2024
+ is live records coarse. In teardown, call `releaseMedia` before `releaseTouch`. The reverse
2025
+ order restores coarse emulation with one touch point after touch cleanup.
2026
+ - **File cancellation must leave the target first.** Chromium raises no `dragleave` for protocol
2027
+ cancellation alone. `sendFileDrag` first moves outside the page. It writes named fixture files
2028
+ under `tmp/drag`, refuses names containing a path, and sends no drop or file-content delivery.
2029
+ - **Layer names follow declaration order at every depth.** `readLayerNames` descends through
2030
+ grouping and style rules, qualifying each statement and block by its enclosing layer. It includes
2031
+ anonymous layers and repeated names, and reads conditions whether or not they match the document.
2032
+ An anonymous nested layer contributes a trailing dot to its enclosing qualified name.
2033
+ An `@import ... layer()` rule contributes nothing, and neither do its imported sheet's rules.
2034
+ A dotted declaration such as `@layer a.b` contributes `a.b` as declared, without an implicit `a`.
2035
+ Distinct anonymous layers produce the same names; the result does not distinguish their identities.
1740
2036
  - **`readStyle` refuses a pseudo argument rather than ignoring it.** The argument must start with
1741
2037
  `::` before the engine's selector-support check runs. A supported pseudo-element with no value for
1742
2038
  the requested property still reads as an empty string. `readPixels` uses the same guard.
1743
2039
  - **`sendProtocol` reaches a Chromium-family provider alone.** It needs the provider's DevTools
1744
- session, passes the named command and parameters through, and discards the response. Browser
1745
- termination cannot promise cleanup. Edge observations belong to a separate run.
2040
+ session, passes the named command and parameters through, and resolves the reply as `unknown`.
2041
+ Narrow the reply with guards before reading a field, and omit the parameters for a command that
2042
+ takes none. Browser termination cannot promise cleanup. Edge observations belong to a separate run.
2043
+ It resolves the reply since 0.0.27, where 0.0.26 resolved nothing. A callback typed to return
2044
+ `void | Promise<void>`, such as a teardown or an `onTestFinished` hook, that returns
2045
+ `sendProtocol(…)` directly no longer type-checks. Write `async () => { await sendProtocol(…) }`.
2046
+ - **`readProtocolPoint` reads the layout at the call, and `readProtocolNode` hands back an id to use at
2047
+ once.** The point is the layout at the call, so a box that moves afterwards moves the point it
2048
+ named. Each frame on the way up adds its border and its padding, because its content starts inside
2049
+ both. An element no document holds is refused, because its window still names the tester frame and
2050
+ the call would otherwise answer that frame's corner. A node id comes from a document the browser is asked for afresh on every call, so use it
2051
+ before the next call. Both leave `readHit` its own question, which is what the element's own
2052
+ document hit-tests at the centre and never leaves that document.
1746
2053
  - **`parseCSSColor` resolves an undeclared token to the inherited color.** A `var()` naming a custom
1747
2054
  property nothing declares is not a parse failure: the cascade accepts it and computes the
1748
- inherited color, so `parseCSSColor('var(--absent)')` hands back channels rather than `undefined`.
2055
+ inherited color, which is the body's, or the `scope` element's with one, so
2056
+ `parseCSSColor('var(--absent)')` hands back channels rather than `undefined`.
1749
2057
  Read `readToken` or `readRootToken` where a missing token is the subject.
1750
2058
  - **`createLoopback` cannot take back an upgraded socket.** A server that claims an upgrade keeps
1751
2059
  that connection, detached from the server itself, so `destroy()`'s `closeAllConnections` never
@@ -1753,7 +2061,7 @@ the helper rather than to the host, and each names what to reach for instead.
1753
2061
  handler took and destroys it before destroying the loopback.
1754
2062
  - **`destroyScratch`'s behavior under a permission hold is unproven where the hold cannot bind.** The
1755
2063
  retry path is proven against a real host refusal, and a host that produces no such refusal cannot
1756
- exercise it: a container running as uid `0` bypasses the access check the mode bits describe, so
2064
+ exercise it: a container running as uid `0` is exempt from the access check the mode bits describe, so
1757
2065
  the suite reads a runtime probe and skips that case rather than asserting either answer. Read
1758
2066
  `supportsMode` for the narrower question of whether the bits are stored at all.
1759
2067
  - **`createRecorders` keys its map from the events array's declared element type.** An array declared
@@ -1769,7 +2077,7 @@ the helper rather than to the host, and each names what to reach for instead.
1769
2077
  the quota, and the grant are.
1770
2078
  - **`readProperty`'s `TypeError` names the target, never the read.** It refuses a target that is
1771
2079
  neither an object nor a function before it reads anything, and a getter that throws on an accepted
1772
- target hands that throw straight to the caller. Wrap the call in `captureError` where a hostile
2080
+ target hands that throw straight to the caller. Wrap the call in `captureError` where an untrusted
1773
2081
  getter is the subject.
1774
2082
  - **`readPixels` reports a measured contribution rather than a parsed length.** A resolved value
1775
2083
  carrying no leading number — `'auto'`, `'none'`, `''` — reads as `0`, because none of them
@@ -2220,9 +2528,9 @@ for (const [index, value] of values.entries()) {
2220
2528
  ```
2221
2529
 
2222
2530
  The corpus is the positive proof input. Keep a negative control for every member too: perform the
2223
- naive read or the naive structural reading that member is meant to break, and prove it answers the
2224
- way the member's own hostility says. Without that control, an inert value can make the totality loop
2225
- look stronger without exercising another hostile boundary.
2531
+ naive read or the naive structural reading that member is meant to break, and prove it fails the
2532
+ way the member's structure dictates. Without that control, an inert value can make the totality loop
2533
+ look stronger without exercising another untrusted-input boundary.
2226
2534
 
2227
2535
  This package's own suite carries one control per member, in corpus order, and each names the reading
2228
2536
  that member breaks:
@@ -2273,7 +2581,7 @@ browser suite runs. It is a native disclosure with two doors: the summary toggle
2273
2581
  button closes it and does nothing when it is already closed. That second door is what gives the
2274
2582
  table a row whose event leaves the state where it found it, which a lone `<details>` cannot have —
2275
2583
  its one event always flips. Every value these fences claim is pinned in
2276
- `tests/src/browser/factories.test.ts`, because the fences drive a browser and the `guides` project
2584
+ `tests/src/browser/journeys/factories.test.ts`, because the fences drive a browser and the `guides` project
2277
2585
  runs with the browser disabled.
2278
2586
 
2279
2587
  The phases are module functions the whole table shares, which is the shape a table of any size
@@ -2547,11 +2855,11 @@ Object.keys(
2547
2855
  // 'src/core/types.ts', 'src/core/validators.ts']
2548
2856
 
2549
2857
  // A directory key takes every key below it.
2550
- Object.keys(readInventory(root, ['src'], { extensions: ['.ts'], exclude: ['src/server'] }))
2551
- // ['src/browser/constants.ts', 'src/browser/factories.ts', 'src/browser/helpers.ts',
2552
- // 'src/browser/index.ts', 'src/browser/types.ts', 'src/core/constants.ts',
2553
- // 'src/core/factories.ts', 'src/core/helpers.ts', 'src/core/index.ts', 'src/core/types.ts',
2554
- // 'src/core/validators.ts']
2858
+ Object.keys(
2859
+ readInventory(root, ['src'], { extensions: ['.ts'], exclude: ['src/server', 'src/browser'] }),
2860
+ )
2861
+ // ['src/core/constants.ts', 'src/core/factories.ts', 'src/core/helpers.ts', 'src/core/index.ts',
2862
+ // 'src/core/types.ts', 'src/core/validators.ts']
2555
2863
 
2556
2864
  // An exclusion also applies to a target you name, so naming one file below an excluded directory
2557
2865
  // does not reinstate it.
@@ -2562,7 +2870,7 @@ readInventory(root, ['src/core/index.ts'], { extensions: ['.ts'], exclude: ['src
2562
2870
  ### Own a temporary directory
2563
2871
 
2564
2872
  This demonstration builds a scratch directory seeded with a file, writes and reads inside it,
2565
- refuses an escaping write, and nests one allocation inside another.
2873
+ refuses a write outside its directory, and nests one allocation inside another.
2566
2874
 
2567
2875
  ```ts
2568
2876
  import { createScratch } from '@orkestrel/test/server'
@@ -2821,9 +3129,9 @@ jar.read('session') // undefined
2821
3129
  jar.header // 'theme=dark'
2822
3130
  ```
2823
3131
 
2824
- ### Refuse an escaping path in your own fixture
3132
+ ### Refuse a path outside the root in your own fixture
2825
3133
 
2826
- `readInventory` and `createScratch` refuse an escape with this predicate. Reach for it when a
3134
+ `readInventory` and `createScratch` refuse a path outside the root with this predicate. Reach for it when a
2827
3135
  fixture of your own resolves a caller-supplied path below a root.
2828
3136
 
2829
3137
  ```ts
@@ -2907,6 +3215,41 @@ await traverseAccessible('Evaluate')
2907
3215
  readPerception('Run') // one visible named region, whitespace collapsed, hidden-but-read text kept
2908
3216
  ```
2909
3217
 
3218
+ #### Tap with one touch
3219
+
3220
+ Resolve the element the fixture supplies, then release touch emulation in cleanup.
3221
+
3222
+ ```ts
3223
+ import { releaseTouch, resolveRendered, tapTouch } from '@orkestrel/test/browser'
3224
+
3225
+ try {
3226
+ await tapTouch(resolveRendered('Tap target'))
3227
+ } finally {
3228
+ await releaseTouch()
3229
+ }
3230
+ ```
3231
+
3232
+ The browser proof records one trusted touch click and no click on the sibling control.
3233
+ Pass a trailing stability-options object when the fixture needs a longer settling budget.
3234
+
3235
+ #### Drag files from outside the page
3236
+
3237
+ A rendered status receives the drag even when it is absent from the Tab order.
3238
+
3239
+ ```ts
3240
+ import { sendFileDrag } from '@orkestrel/test/browser'
3241
+
3242
+ await sendFileDrag('dragEnter', 'status', 'Drop status', ['note.txt'])
3243
+ try {
3244
+ await sendFileDrag('dragOver', 'status', 'Drop status', ['note.txt'])
3245
+ } finally {
3246
+ await sendFileDrag('dragCancel', 'status', 'Drop status', ['note.txt'])
3247
+ }
3248
+ ```
3249
+
3250
+ The browser proof observes trusted entry, over, and leave events carrying the `Files` type.
3251
+ No drop occurs; fixture files remain under `tmp/drag` for the caller to remove.
3252
+
2910
3253
  ### Hold a control and read the pressed paint
2911
3254
 
2912
3255
  Register pointer cleanup before a test can fail. This fixture declares `padding-top: 16px` on its
@@ -2982,6 +3325,21 @@ matchMedia('(prefers-reduced-motion: reduce)').matches === reduced // true
2982
3325
  readPixels(button, 'padding-top') === (reduced ? 2 : 1) // true
2983
3326
  ```
2984
3327
 
3328
+ Pointer precision uses touch emulation. These readings are proved in the browser media cases.
3329
+
3330
+ ```ts
3331
+ import { releaseMedia, stageMedia } from '@orkestrel/test/browser'
3332
+
3333
+ await stageMedia({ pointer: 'coarse' })
3334
+ matchMedia('(pointer: coarse)').matches // true
3335
+ matchMedia('(any-pointer: coarse)').matches // true
3336
+ matchMedia('(hover: none)').matches // true
3337
+ await stageMedia({ pointer: 'fine' })
3338
+ matchMedia('(pointer: fine)').matches // true
3339
+ matchMedia('(hover: hover)').matches // true
3340
+ await releaseMedia()
3341
+ ```
3342
+
2985
3343
  ### Send a key to what holds focus
2986
3344
 
2987
3345
  Bring focus about through a verb, then send the sequence. A key sent while the document body holds
@@ -3452,10 +3810,20 @@ await portfolio.place('start-empty')
3452
3810
  portfolio.place('answer-partial') // rejects: Capture state "answer-partial" is not registered
3453
3811
  ```
3454
3812
 
3813
+ Settle the page before each frame of a capture run. `settleCapture` parks the pointer, waits out
3814
+ every finite animation in the document, and holds each endless one at its first frame, so a hover,
3815
+ a transition caught part way, and a spinner's phase stay out of the picture. It resolves to the
3816
+ resume; call `place` after it, and call the resume from a `finally` block, so the held animations
3817
+ run again whether or not the frame was written. Pass `pointer: false` where the pointer is part of
3818
+ the state, such as a drag in flight. Gate the settle on the flag that `enabled` takes: an ordinary run's
3819
+ `place` shoots nothing, and parking the pointer there changes the journey that run is proving.
3820
+ Where a frame holds a scroll region, record `describeScrollRegions` of what `collectScrollRegions`
3821
+ finds beside it, because a picture does not show how far a pane scrolls.
3822
+
3455
3823
  ### Measure a document's content edge
3456
3824
 
3457
3825
  A capture stages the pane at the height the document needs, and the body's box cannot answer for
3458
- that height once the pane is taller than the document. In the following fence the document holds
3826
+ that height after the pane grows taller than the document. In the following fence the document holds
3459
3827
  1600 rows of fixed content and nothing is bound to the viewport.
3460
3828
 
3461
3829
  ```ts
@@ -3532,7 +3900,7 @@ picking one of them.
3532
3900
  is either slower than it needs to be or shorter than the slowest host, and it reports nothing when
3533
3901
  it fails. Name the fact instead, and let `waitForCondition`, `retryUntil`, or `waitForEvent` decide
3534
3902
  when it holds.
3535
- - **Let `readInventory` refuse.** A symlinked root or an escaping target is an error, not a
3903
+ - **Let `readInventory` refuse.** A symlinked root or a target outside the root is an error, not a
3536
3904
  filtered result, so a misconfigured walk fails loudly instead of returning a short map.
3537
3905
  - **Reach for `parent` only when the allocation must be somewhere named.** The default keeps it out
3538
3906
  of the repository, and a path inside a package tree is walked by every tool that reads that tree.
@@ -3557,7 +3925,11 @@ Each entry names the contracts its file proves. The test names carry the cases.
3557
3925
  carrying both the arrival and the departure that `absent` waits past, an empty expectation and an
3558
3926
  empty departure each refused, a reader throw propagated unchanged, the timeout naming the wait and
3559
3927
  the budget, an abort rejecting with the signal's own reason, and a refused bound raised through the
3560
- family. `decodeJSONLines` takes empty input, a trailing newline, CRLF,
3928
+ family. `collectSelectorClasses` takes a selector list whose functional pseudo-classes nest class
3929
+ names beside an attribute block, quoted strings, and a comment that name none, every escape form
3930
+ decoded with the out-of-range points read as the replacement character, names opening with an
3931
+ underscore, a hyphen, two hyphens, and a non-ASCII letter, the empty selector, and each unreadable
3932
+ text refused by name. `decodeJSONLines` takes empty input, a trailing newline, CRLF,
3561
3933
  line order, primitive lines, and a malformed physical line named with the native `SyntaxError` as
3562
3934
  its cause. `collect` and `collectStream` drain an empty and an ordered source, and the stream's
3563
3935
  reader lock is released afterwards. `roundTripJSON` takes a copy of a flat and a nested
@@ -3587,11 +3959,24 @@ Each entry names the contracts its file proves. The test names carry the cases.
3587
3959
  with every remaining handler still run, both together aggregated in run order, a handler added
3588
3960
  during a run kept for the next call, the count reset before the handlers run, and a `destroy()`
3589
3961
  that is called empty and called twice. `createHostileValues` proves a naive-reader failure for
3590
- every member, frozen and fresh membership, and one total guard's benign and hostile answers with
3962
+ every member, frozen and fresh membership, and one total guard's answers for ordinary and unexpected inputs with
3591
3963
  loop-index attribution.
3592
- - [`tests/src/browser/helpers.test.ts`](../tests/src/browser/helpers.test.ts) — the journey-layer,
3593
- role-map, and announced-half-and-clickable-half contracts across the layer, in real Chromium
3594
- against constructed markup. The resolver takes a bare name, a
3964
+ - [`tests/src/browser/helpers.test.ts`](../tests/src/browser/helpers.test.ts),
3965
+ [`tests/src/browser/journeys/helpers.test.ts`](../tests/src/browser/journeys/helpers.test.ts),
3966
+ [`tests/src/browser/captures/helpers.test.ts`](../tests/src/browser/captures/helpers.test.ts),
3967
+ [`tests/src/browser/colors/helpers.test.ts`](../tests/src/browser/colors/helpers.test.ts), and
3968
+ [`tests/src/browser/sheets/helpers.test.ts`](../tests/src/browser/sheets/helpers.test.ts) — the
3969
+ journey-layer, role-map, and announced-half-and-clickable-half contracts across the layer, in real Chromium
3970
+ against constructed markup. The touch proofs cover a pointer-dependent target beside a sibling,
3971
+ a target moving across painted frames before settling, a supplied stability budget that refuses
3972
+ the tap, and both orders of media and touch teardown. The pointer proof restores the first coarse
3973
+ reading after fine then coarse staging. The file-drag proofs reach a target under a smooth root
3974
+ and refuse a file name without scrolling or writing any file in the batch. The `releaseTouch`
3975
+ rethrow of a non-idle protocol error is covered by review: its cancellation parameters are fixed,
3976
+ and no controllable non-idle refusal was found through the browser provider without replacing
3977
+ its transport or disrupting the tester session. A provider with a recoverable protocol-failure
3978
+ seam would let a proof drive that branch.
3979
+ The resolver takes a bare name, a
3595
3980
  role that disambiguates a tab from its own panel, a name no element carries, a name carried only by
3596
3981
  a role outside `ACCESSIBLE_ROLES`, and the disabled, hidden, and inert matches that are present but
3597
3982
  gated. It takes the glyph cases against a fixture stylesheet that really paints one: an exact name
@@ -3623,7 +4008,17 @@ Each entry names the contracts its file proves. The test names carry the cases.
3623
4008
  reading, and a refused bound. `waitForAnimations` takes a descendant transition awaited to its end,
3624
4009
  the exhaustion naming the subject and the animation still running, an infinite-iteration animation
3625
4010
  excluded while it is still turning, a detached subject refused, and a refused bound; its settled
3626
- reading is compared against the interpolated one taken mid-transition. `readRefusal` takes the
4011
+ reading is compared against the interpolated one taken mid-transition. `readAnimations` takes a
4012
+ running transition and a running infinite animation split apart, a paused animation and one
4013
+ outside the subject left out, and a detached element read as two empty lists. `settleCapture`
4014
+ takes a hovered control's transition waited out and a spinner held at its first frame until the
4015
+ resume sets it running, a held press whose hover and pressed paint `pointer: false` leaves in
4016
+ place beside the default park that takes both away, and an exhausted wait refused with nothing
4017
+ held. `collectScrollRegions` takes a vertical and a horizontal scroller beside a box whose content
4018
+ fits, a clipping box, and a hidden one, each axis alone, and a scrolling root counted itself;
4019
+ `describeScrollRegions` takes a region named by its tag, its id, and its cut accessible name with
4020
+ its offsets after a scroll, the window's position after a scroll, and an empty population.
4021
+ `readRefusal` takes the
3627
4022
  absent, gated, and ambiguous voices beside a target that resolves under a bare name and under a
3628
4023
  role, and — as the control for the rethrow — a fixture element whose own `tabIndex` getter refuses
3629
4024
  with a string, handed straight back. `readCensus` takes a population carrying both undeclared
@@ -3641,17 +4036,44 @@ Each entry names the contracts its file proves. The test names carry the cases.
3641
4036
  Each acting verb takes its happy path and every voice it owns, including both
3642
4037
  region-scoped refusals and both native-disclosure ones; `clickAccessibleWithin` also takes a
3643
4038
  glyph-captioned control inside a region a glyph-carrying heading labels, which is the loose match
3644
- proving unchanged. `traverseAccessible` takes a Tab-reachable
3645
- target; as the cap control, a lone target whose own focus handler blurs it, so focus never lands
4039
+ proving unchanged. `traverseAccessible` takes a Tab-reachable target, a crossing for each type in
4040
+ `SEGMENTED_INPUT_TYPES`, and a page of fractional-second `datetime-local` inputs that traversal
4041
+ and census both cross with spare presses for another landing per input. As the cap control, it
4042
+ takes a lone target whose own focus handler blurs it, so focus never lands
3646
4043
  and the cap fails with an empty trail; and, as the cycle control, the same self-blurring target
3647
4044
  behind a reachable decoy, so the traversal completes one cycle and reports the decoy in its trail.
4045
+ `driveTraversal` takes a non-modal button trap with a resolvable target and refuses after the
4046
+ repeated landing, plus a segmented input trap that refuses within its consecutive landing budget.
3648
4047
  The page readers take their own inputs: `readPerception` takes one named region including its
3649
4048
  visually hidden text, a region a glyph-carrying heading labels through `aria-labelledby`, a painted
3650
4049
  region an `aria-hidden` attribute withholds, and its not-visible and ambiguous refusals, `readPage`
3651
4050
  the whole page as one
3652
4051
  normalized sentence, and
3653
- `readFocus` a focused control's rendered text, a focused element that renders none, and nothing
3654
- holding focus at all; `readValue` takes a rendered value and a control carrying none. The element
4052
+ `readFocus` a focused control's rendered text, a focused element that renders none, an SVG element
4053
+ focused inside an open shadow root that reads nothing rather than its host's text, a host holding
4054
+ focus with nothing inside, and nothing holding focus at all; `readValue` takes a rendered value
4055
+ and a control carrying none. The focus
4056
+ walk takes its inputs across each boundary: `readActiveElement` takes the page's focused control,
4057
+ focus inside an open shadow root and through nested ones, an SVG element focused inside one that
4058
+ answers nothing rather than its host, a host holding focus with nothing inside
4059
+ and one with a closed root, a control inside a same-origin frame typed for the frame's window, a
4060
+ frame holding focus with nothing inside, and a frame from another origin refused;
4061
+ `countElements` takes the shadow and frame content a plain query misses; `containsNode` takes a
4062
+ region's own content, a shadow root's and a frame's, and a detached node left outside;
4063
+ `stepFocus` takes one press per landing, none past the consumer's last read, a limit of zero,
4064
+ a landing on nothing, repeated segment landings for each type in `SEGMENTED_INPUT_TYPES`, and
4065
+ the measured nine landings of a fractional-second `datetime-local` input with a spare press in
4066
+ its budget. `supportsSegmentedFocus` takes the frozen type list, every listed type beside text
4067
+ and number inputs, a button and a non-input carrying `type="date"`, an input from a same-origin
4068
+ frame, and a document with no window. `walkFocus` takes a date input counted once before the
4069
+ following control, a single-button modal trap that returns after one press, and a lone segmented
4070
+ input trap that returns within its consecutive landing budget. `collectTabStops` takes a region
4071
+ holding a date input with every stop listed and a trapped date input returned within that budget.
4072
+ `walkFocus` and `collectTabStops` take the buttons of an open shadow root
4073
+ and of a same-origin frame between two light controls, a start element inside a shadow root, a
4074
+ start element immediately before the region that reaches the region's first stop, and
4075
+ focus in a foreign frame refused; `readProtocolPoint` takes a padded frame beside a bordered one,
4076
+ both hit-tested through the protocol, and a disconnected element refused. The element
3655
4077
  readers follow: `readText` takes an `aria-hidden` glyph dropped with the runs around it collapsed
3656
4078
  and an element with no text at all; `readRole` takes exactly the tags `IMPLICIT_ROLES` carries and
3657
4079
  one it leaves out, a declared role taken over the implicit one, a section made a region only by
@@ -3676,7 +4098,12 @@ Each entry names the contracts its file proves. The test names carry the cases.
3676
4098
  nothing paints and one whose every painted layer is translucent, a stack 64 translucent layers
3677
4099
  deep whose composite has rounded to the canvas's own channels, a detached element whose computed
3678
4100
  foreground does not exist, and the same unpainted and translucent stacks measured against a
3679
- supplied floor instead of refused. The color leaves beneath it take their own inputs: `parseColor`
4101
+ supplied floor instead of refused. `readColorContrast` takes the ceiling, the floor, a clear
4102
+ color, and a half-opaque one over an opaque surface, a keyword and a `var()` reference resolved
4103
+ through the cascade, a translucent and an unpainted stack refused until a floor is named and then
4104
+ composited onto it, an `opacity` reading equal to the declared alpha it multiplies with the
4105
+ refused factors beside it, and an unreadable color and an unreadable painted layer each refused.
4106
+ The color leaves beneath it take their own inputs: `parseColor`
3680
4107
  across the legacy and modern syntaxes, a refused keyword, hex triple, empty value, and unsupported
3681
4108
  color space, and — as the control the literals cannot supply — what this browser actually computes
3682
4109
  for a keyword and for a `color-mix()`. `blendColor`, `measureLuminance`, and `measureContrast`
@@ -3687,7 +4114,8 @@ Each entry names the contracts its file proves. The test names carry the cases.
3687
4114
  composited onto it, and an opaque layer that ends the walk. `readRing` takes a painted outline and
3688
4115
  a painted box-shadow reached through `traverseAccessible`, a control that is not focused, a
3689
4116
  focused control left the browser's own ring, a focus style that only repaints the control's fill,
3690
- and a `worn` element whose reading separates from the control's own. `measureContent` takes a
4117
+ a `worn` element whose reading separates from the control's own, and an unpainted focused control
4118
+ over an unreadable surface that is refused. `measureContent` takes a
3691
4119
  1600-row document read under an 844 pane and under a 2356 one, against the scroll height that
3692
4120
  agrees with it under the shorter pane and stretches to the pane under the taller, and a document
3693
4121
  whose last element carries a bottom margin under a body carrying bottom padding, where a reading
@@ -3717,7 +4145,10 @@ Each entry names the contracts its file proves. The test names carry the cases.
3717
4145
  height on the panel's own color with the scroll handed back; an element below both the pane and
3718
4146
  the window, whole on the document's floor with the scroll handed back; a `50vh` element and a
3719
4147
  `30vh` element below the fold, at the declared pane rather than a grown one; an element taller
3720
- than the pane, in a pane of its own height; an element that outgrows every pane, refused with the
4148
+ than the pane, in a pane of its own height; tall and near-viewport elements below authored native
4149
+ clearance, with decoded heading pixels distinguishing them from an undeclared sticky obstruction;
4150
+ a fixed element beyond a padded viewport that keeps its declared viewport height;
4151
+ an element that outgrows every pane, refused with the
3721
4152
  pane and the scroll handed back; a second tester frame the offset leaves in place; and a frame
3722
4153
  carrying no `style` attribute, handed back without one. After the `releasePointer` function, an
3723
4154
  element frame takes no `mouseover` event for an element inside the window, which also takes no `scroll` event;
@@ -3736,8 +4167,31 @@ Each entry names the contracts its file proves. The test names carry the cases.
3736
4167
  frame's size and floor, read a second way through the cascade's own answer for the same canvas, a
3737
4168
  bottom row split between two colors reported as no floor at all, a path holding no file,
3738
4169
  a file holding no image and refused without a size, and a PNG header over no image data, refused
3739
- with the size the header declares. The `readCascade` function takes class tokens collected from
3740
- plain and grouped rules and only real ones;
4170
+ with the size the header declares. The `paintImage` function takes a PNG painted at its natural
4171
+ size and bytes that are no image, whose decode failure it hands on unchanged. The `readShotPixel`
4172
+ function takes each half of a split shot, frozen, and both corners clamped onto the shot, and
4173
+ refuses a fraction below `0`, above `1`, infinite, or `NaN` on either axis. The `readClippedBlock` function takes a frame
4174
+ clipping a taller child, a paint containment and a clip margin, elements wholly under a frame's
4175
+ edge, an unclipped control, a block in an open shadow tree under a clipping wrapper around its
4176
+ host, a slotted child under a clipping wrapper around its slot, a disconnected element refused,
4177
+ and the same reading on a page scrolled past its top, which a reading
4178
+ that skipped the coordinate conversion fails. The `collectClippingAncestors` function takes the
4179
+ same climb through a host and through a slot, nearest first, and a disconnected element refused.
4180
+ The `measureContent` function takes a slotted child ended at the shadow wrapper that clips around
4181
+ its slot. The `waitForStability` function takes reads spaced by the interval past the second.
4182
+ The `parseCSSColor` function takes an expression resolved on the scope it is handed, a registered
4183
+ token that does not inherit, a keyword, a hex triple, and `currentColor` at an `input` and an SVG
4184
+ element, a running color transition read at its sampled midpoint and left running, a scroller
4185
+ with a running height transition keeping its height and scroll position, a scope that records no
4186
+ mutation during the read, a transition on every property that starts none, a color scheme
4187
+ answered to a system color and to `light-dark()`, a token named only in a fallback carried, a
4188
+ disconnected scope answered with nothing, and an element that is no HTML, SVG, or MathML element
4189
+ resolved like any other. The `readColorContrast`
4190
+ function takes a token on an ancestor the body never declares, `currentColor`, and a token the
4191
+ element declares itself, each read at the element, a keyword, a hex triple, and a token at an
4192
+ `input`, a registered token that does not inherit, and a disconnected element refused. The `readCascade` function takes class tokens collected from
4193
+ plain and grouped rules and only real ones, and an escaped class decoded beside an attribute
4194
+ value whose class-like text defines nothing;
3741
4195
  `readRows` takes a row joined from its own text nodes rather than from run-together content, and
3742
4196
  an empty list; `extractOrphans` takes a child class rendered outside its container with a nested
3743
4197
  one left alone, nothing reported when every child sits inside one, and, as the control, an element
@@ -3752,11 +4206,14 @@ Each entry names the contracts its file proves. The test names carry the cases.
3752
4206
  element carrying an inline attribute reported once, a whitespace-only attribute read past, a
3753
4207
  fragment root, and — as the control — markup whose classes and `data-*` attributes leave it with
3754
4208
  nothing to report.
3755
- - [`tests/src/browser/factories.test.ts`](../tests/src/browser/factories.test.ts) — the journal
4209
+ - [`tests/src/browser/journeys/factories.test.ts`](../tests/src/browser/journeys/factories.test.ts) and
4210
+ [`tests/src/browser/captures/factories.test.ts`](../tests/src/browser/captures/factories.test.ts) — the journal
3756
4211
  contract, and the portfolio's refusals, its disabled gate, and its writes. Creation refuses an
3757
4212
  unregistered variant name; the registry expands across every variant whether or not the run
3758
4213
  writes; a run
3759
- that is not enabled applies nothing, writes nothing, and records nothing; an enabled run applies
4214
+ that is not enabled applies nothing, writes nothing, and records nothing, yet refuses an
4215
+ unregistered state and a second placement of one state, and accepts two distinct registered states
4216
+ with its lists still empty; an enabled run applies
3760
4217
  the variant, resizes the viewport, writes a real file through the provider, records it, and hands
3761
4218
  out snapshots rather than its own lists; it refuses an unregistered state and a second placement of
3762
4219
  one state; and a placement handed an element writes a frame that is not the whole page's and leaves
@@ -3769,11 +4226,15 @@ Each entry names the contracts its file proves. The test names carry the cases.
3769
4226
  operation, each withheld operation refused in its own voice, both permissions granted at once with
3770
4227
  the store answering from what it kept, a quota spent on accepted writes alone with `removeItem`
3771
4228
  consuming none of it, a granted permission replenishing no room, every refused quota value, and —
3772
- as the control for the inertness claim — a write that leaves `localStorage` exactly as it was.
4229
+ as the control for the inertness claim — a write that leaves `localStorage` exactly as it was. The
4230
+ event factories take the real event type, the bubbling, cancelable, and composed defaults and an
4231
+ override of each, and an event dispatched from a button inside an open shadow root that a
4232
+ `document` listener hears, beside a bare event of the same type that it does not, and a pointer
4233
+ or drag event whose `{ composed: false }` override keeps it inside the root.
3773
4234
  - [`tests/src/server/helpers.test.ts`](../tests/src/server/helpers.test.ts) — the `readInventory` and
3774
4235
  wait-family contracts, and each pure leaf against its own inputs. `resolveContained` takes
3775
- contained relative and absolute targets and both spellings of an escape, and `requireContained`
3776
- takes the same contained pair and each escape refused with the message naming the target it was
4236
+ contained relative and absolute targets and both spellings of a path outside the root, and `requireContained`
4237
+ takes the same contained pair and each path outside the root refused with the message naming the target it was
3777
4238
  given. `readIdentity` takes a real
3778
4239
  allocation's three fields, that allocation matching itself across a `stat` and an `lstat`, and a
3779
4240
  second allocation reading as a different identity. `readErrorCode` takes a real `ENOENT` off the
@@ -3814,14 +4275,14 @@ Each entry names the contracts its file proves. The test names carry the cases.
3814
4275
  leaves a moved allocation alone. Then one group per subject.
3815
4276
  `destruction` takes `write`, `read`, `has`, `names`, `ensure`, `link`, and `remove` after
3816
4277
  `destroy()`, with `write`, `ensure`, and `link` also proven not to rebuild the allocation root, and
3817
- `remove` proving its root and escape refusals answer before the destroyed-allocation one.
4278
+ `remove` proving its root and path-containment refusals answer before the destroyed-allocation one.
3818
4279
  `names` takes its sorted output, including the population that discriminates a dropped `.sort()`.
3819
4280
  `ensure` takes an empty directory, every missing parent, and a repeated call. `link` takes
3820
4281
  traversal through a planted link, the final segment `has` reports rather than follows, and the
3821
4282
  `EEXIST` an occupied final segment throws. `remove` takes a file beside a kept
3822
4283
  sibling, an empty directory, a populated subtree, a missing target, an ancestor link back to the
3823
4284
  allocation with every seeded file read back afterwards, a final link whose destination is read back
3824
- afterwards, a sibling directory reached through that same ancestor link, an escaping target with
4285
+ afterwards, a sibling directory reached through that same ancestor link, a target outside the root with
3825
4286
  the file outside left intact, the root refused as `''`, as `'.'`, and as its absolute path, a
3826
4287
  foreign directory swapped onto the allocated path that `remove('')` refuses, and that same swap
3827
4288
  under `destroy()`, which removes nothing either. `parent` and `prefix` take their own refusals.