@orkestrel/scaffold 0.0.107 → 0.0.109

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 (164) hide show
  1. package/dist/agents/skills/orkestrel-dispatch/scripts/bench.js +326 -91
  2. package/dist/agents/skills/orkestrel-dispatch/scripts/brief.js +110 -14
  3. package/dist/agents/skills/orkestrel-dispatch/scripts/chain.js +280 -0
  4. package/dist/agents/skills/orkestrel-dispatch/scripts/cite.js +188 -26
  5. package/dist/agents/skills/orkestrel-dispatch/scripts/launch.js +142 -43
  6. package/dist/agents/skills/orkestrel-dispatch/scripts/login.js +240 -62
  7. package/dist/agents/skills/orkestrel-dispatch/scripts/patch.js +323 -0
  8. package/dist/agents/skills/orkestrel-dispatch/scripts/result.js +169 -32
  9. package/dist/agents/skills/orkestrel-dispatch/scripts/sweep.js +368 -27
  10. package/dist/agents/skills/orkestrel-falsify/scripts/mutate.js +560 -0
  11. package/dist/agents/skills/orkestrel-harden/scripts/discovery.js +1052 -205
  12. package/dist/agents/skills/orkestrel-harden/scripts/measure.js +361 -0
  13. package/dist/agents/skills/orkestrel-harden/scripts/titles.js +455 -80
  14. package/dist/agents/skills/orkestrel-parallel/scripts/cases.js +327 -0
  15. package/dist/agents/skills/orkestrel-publish/scripts/compare.js +298 -51
  16. package/dist/agents/skills/orkestrel-publish/scripts/pins.js +170 -13
  17. package/dist/agents/skills/orkestrel-publish/scripts/swap.js +956 -191
  18. package/dist/agents/skills/orkestrel-publish/scripts/wave.js +707 -112
  19. package/dist/agents/skills/orkestrel-publish/scripts/window.js +235 -41
  20. package/dist/agents/skills/orkestrel-scout/scripts/map.js +341 -54
  21. package/dist/agents/templates/brief.md +1 -1
  22. package/dist/bin/main.js +51 -46
  23. package/dist/bin/main.js.map +1 -1
  24. package/dist/host/AGENTS.md +26 -20
  25. package/dist/host/agents/orchestration.md +98 -58
  26. package/dist/host/agents/platforms/posix.md +15 -0
  27. package/dist/host/agents/platforms/windows.md +37 -0
  28. package/dist/host/agents/skills/orkestrel-align/SKILL.md +10 -13
  29. package/dist/host/agents/skills/orkestrel-align/references/fleet.md +4 -10
  30. package/dist/host/agents/skills/orkestrel-align/references/integration.md +1 -1
  31. package/dist/host/agents/skills/orkestrel-build/SKILL.md +16 -48
  32. package/dist/host/agents/skills/orkestrel-build/agents/openai.yaml +1 -1
  33. package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +19 -16
  34. package/dist/host/agents/skills/orkestrel-debrief/agents/openai.yaml +1 -1
  35. package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +15 -28
  36. package/dist/host/agents/skills/orkestrel-debrief/references/instruction-audit.md +5 -6
  37. package/dist/host/agents/skills/orkestrel-debrief/references/retention.md +12 -14
  38. package/dist/host/agents/skills/orkestrel-dispatch/SKILL.md +36 -24
  39. package/dist/host/agents/skills/orkestrel-dispatch/references/launch.md +14 -11
  40. package/dist/host/agents/skills/orkestrel-dispatch/scripts/bench.ts +366 -75
  41. package/dist/host/agents/skills/orkestrel-dispatch/scripts/brief.ts +126 -16
  42. package/dist/host/agents/skills/orkestrel-dispatch/scripts/chain.ts +323 -0
  43. package/dist/host/agents/skills/orkestrel-dispatch/scripts/cite.ts +220 -31
  44. package/dist/host/agents/skills/orkestrel-dispatch/scripts/launch.ts +176 -33
  45. package/dist/host/agents/skills/orkestrel-dispatch/scripts/login.ts +294 -59
  46. package/dist/host/agents/skills/orkestrel-dispatch/scripts/patch.ts +401 -0
  47. package/dist/host/agents/skills/orkestrel-dispatch/scripts/result.ts +199 -34
  48. package/dist/host/agents/skills/orkestrel-dispatch/scripts/sweep.ts +428 -27
  49. package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +10 -8
  50. package/dist/host/agents/skills/orkestrel-falsify/references/brief.md +1 -5
  51. package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +1 -1
  52. package/dist/host/agents/skills/orkestrel-falsify/scripts/mutate.ts +701 -0
  53. package/dist/host/agents/skills/orkestrel-harden/SKILL.md +16 -12
  54. package/dist/host/agents/skills/orkestrel-harden/references/bindings.md +23 -81
  55. package/dist/host/agents/skills/orkestrel-harden/references/centralization.md +4 -5
  56. package/dist/host/agents/skills/orkestrel-harden/references/contract.md +2 -0
  57. package/dist/host/agents/skills/orkestrel-harden/references/document.md +33 -0
  58. package/dist/host/agents/skills/orkestrel-harden/references/equivalence.md +6 -3
  59. package/dist/host/agents/skills/orkestrel-harden/references/hardening.md +8 -4
  60. package/dist/host/agents/skills/orkestrel-harden/references/research.md +2 -0
  61. package/dist/host/agents/skills/orkestrel-harden/scripts/discovery.ts +1272 -279
  62. package/dist/host/agents/skills/orkestrel-harden/scripts/measure.ts +425 -0
  63. package/dist/host/agents/skills/orkestrel-harden/scripts/titles.ts +603 -98
  64. package/dist/host/agents/skills/orkestrel-journey/SKILL.md +75 -98
  65. package/dist/host/agents/skills/orkestrel-journey/references/captures.md +54 -17
  66. package/dist/host/agents/skills/orkestrel-journey/references/decide.md +1 -20
  67. package/dist/host/agents/skills/orkestrel-journey/references/layer.md +21 -31
  68. package/dist/host/agents/skills/orkestrel-journey/references/styles.md +1 -1
  69. package/dist/host/agents/skills/orkestrel-parallel/SKILL.md +91 -30
  70. package/dist/host/agents/skills/orkestrel-parallel/agents/openai.yaml +2 -2
  71. package/dist/host/agents/skills/orkestrel-parallel/references/claude.md +34 -0
  72. package/dist/host/agents/skills/orkestrel-parallel/references/codex.md +29 -0
  73. package/dist/host/agents/skills/orkestrel-parallel/references/cursor.md +29 -0
  74. package/dist/host/agents/skills/orkestrel-parallel/references/t3.md +31 -0
  75. package/dist/host/agents/skills/orkestrel-parallel/scripts/cases.ts +389 -0
  76. package/dist/host/agents/skills/orkestrel-polish/SKILL.md +16 -20
  77. package/dist/host/agents/skills/orkestrel-polish/references/capture-harness.md +16 -26
  78. package/dist/host/agents/skills/orkestrel-publish/SKILL.md +12 -26
  79. package/dist/host/agents/skills/orkestrel-publish/agents/openai.yaml +2 -2
  80. package/dist/host/agents/skills/orkestrel-publish/references/adopt.md +23 -0
  81. package/dist/host/agents/skills/orkestrel-publish/references/release.md +4 -4
  82. package/dist/host/agents/skills/orkestrel-publish/references/wave.md +12 -10
  83. package/dist/host/agents/skills/orkestrel-publish/references/window.md +20 -25
  84. package/dist/host/agents/skills/orkestrel-publish/scripts/compare.ts +348 -59
  85. package/dist/host/agents/skills/orkestrel-publish/scripts/pins.ts +202 -22
  86. package/dist/host/agents/skills/orkestrel-publish/scripts/swap.ts +1061 -191
  87. package/dist/host/agents/skills/orkestrel-publish/scripts/wave.ts +812 -140
  88. package/dist/host/agents/skills/orkestrel-publish/scripts/window.ts +280 -38
  89. package/dist/host/agents/skills/orkestrel-scout/SKILL.md +3 -3
  90. package/dist/host/agents/skills/orkestrel-scout/scripts/map.ts +428 -70
  91. package/dist/host/agents/templates/brief.md +1 -1
  92. package/dist/host/agents/transports/claude-cli.md +3 -3
  93. package/dist/host/agents/transports/codex.md +3 -8
  94. package/dist/host/agents/transports/cursor.md +2 -3
  95. package/dist/host/claude/AGENTS.md +7 -10
  96. package/dist/host/claude/agents/analyst.md +3 -3
  97. package/dist/host/claude/agents/astra.md +3 -3
  98. package/dist/host/claude/agents/builder.md +1 -1
  99. package/dist/host/claude/agents/checker.md +1 -1
  100. package/dist/host/claude/agents/distiller.md +1 -1
  101. package/dist/host/claude/agents/grok.md +1 -1
  102. package/dist/host/claude/agents/opus.md +2 -2
  103. package/dist/host/claude/agents/orkestrel.md +57 -90
  104. package/dist/host/claude/agents/researcher.md +1 -1
  105. package/dist/host/claude/agents/reviewer.md +4 -0
  106. package/dist/host/claude/agents/scout.md +1 -1
  107. package/dist/host/claude/agents/verifier.md +1 -1
  108. package/dist/host/claude/rules/application.md +14 -21
  109. package/dist/host/claude/rules/architecture.md +34 -44
  110. package/dist/host/claude/rules/browser.md +3 -4
  111. package/dist/host/claude/rules/campaign.md +55 -0
  112. package/dist/host/claude/rules/documentation.md +26 -11
  113. package/dist/host/claude/rules/names.md +9 -4
  114. package/dist/host/claude/rules/patterns.md +6 -7
  115. package/dist/host/claude/rules/portability.md +2 -3
  116. package/dist/host/claude/rules/quality.md +4 -5
  117. package/dist/host/claude/rules/styles.md +2 -2
  118. package/dist/host/claude/rules/tests.md +57 -114
  119. package/dist/host/claude/rules/typescript.md +8 -10
  120. package/dist/host/claude/rules/workspace.md +21 -19
  121. package/dist/host/claude/rules/writing.md +3 -3
  122. package/dist/host/claude/skills/orkestrel-dispatch/SKILL.md +1 -1
  123. package/dist/host/claude/skills/orkestrel-harden/SKILL.md +1 -1
  124. package/dist/host/claude/skills/orkestrel-parallel/SKILL.md +1 -1
  125. package/dist/host/claude/skills/orkestrel-publish/SKILL.md +1 -1
  126. package/dist/host/claude/skills/orkestrel-scout/SKILL.md +1 -1
  127. package/dist/host/codex/agents/analyst.toml +9 -3
  128. package/dist/host/codex/agents/astra.toml +3 -4
  129. package/dist/host/codex/agents/builder.toml +1 -1
  130. package/dist/host/codex/agents/opus.toml +3 -4
  131. package/dist/host/codex/agents/orkestrel.toml +1 -1
  132. package/dist/host/codex/agents/researcher.toml +2 -2
  133. package/dist/host/codex/agents/verifier.toml +1 -1
  134. package/dist/host/codex/config.toml +3 -3
  135. package/dist/host/configs/helpers.ts +21 -1
  136. package/dist/host/configs/policy.ts +405 -16
  137. package/dist/host/cursor/rules/orchestration.mdc +1 -0
  138. package/dist/host/dotfiles/oxlintrc.json +141 -5
  139. package/dist/host/guides/agent.md +111 -134
  140. package/dist/host/guides/capture.md +801 -0
  141. package/dist/host/guides/probe.md +27 -8
  142. package/dist/host/guides/scaffold.md +152 -19
  143. package/dist/host/guides/study.md +534 -440
  144. package/dist/host/guides/test.md +111 -44
  145. package/dist/host/manifest.json +199 -242
  146. package/dist/host/tests/config.test.ts +1558 -219
  147. package/dist/host/tests/policy.test.ts +127 -1
  148. package/dist/host/tests/setupPolicy.ts +1389 -39
  149. package/dist/src/core/index.cjs +239 -87
  150. package/dist/src/core/index.cjs.map +1 -1
  151. package/dist/src/core/index.d.cts +123 -39
  152. package/dist/src/core/index.d.ts +123 -39
  153. package/dist/src/core/index.js +234 -86
  154. package/dist/src/core/index.js.map +1 -1
  155. package/dist/src/server/index.cjs +20 -27
  156. package/dist/src/server/index.cjs.map +1 -1
  157. package/dist/src/server/index.js +21 -28
  158. package/dist/src/server/index.js.map +1 -1
  159. package/package.json +11 -11
  160. package/dist/agents/skills/orkestrel-dispatch/scripts/helpers.js +0 -283
  161. package/dist/host/agents/skills/orkestrel-dispatch/references/bench.md +0 -25
  162. package/dist/host/agents/skills/orkestrel-dispatch/scripts/helpers.ts +0 -333
  163. package/dist/host/agents/skills/orkestrel-parallel/references/harnesses.md +0 -19
  164. package/dist/host/guides/supervisor.md +0 -4890
@@ -303,12 +303,14 @@ a union's arms escaped as `\|`.
303
303
 
304
304
  | Type | Kind | Shape | Summary |
305
305
  | --------------------- | --------- | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
306
+ | `CaptureReader` | type | `(path: string) => Promise<string>` | Reads the lowercase hexadecimal SHA-256 digest of a captured file through a browser command. |
307
+ | `FrameShot` | interface | `{ path, base64 }` | Represents the path and base64 bytes returned by Vitest's screenshot operation. |
306
308
  | `Color` | type | `readonly [red, green, blue, alpha]` | Represents one rendered color as straight sRGB channels and its alpha. |
307
309
  | `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. |
308
310
  | `ElementOptions` | interface | `{ classes?, text?, attributes? }` | Configures one built element: its class list, its text, and its attributes. |
309
311
  | `FrameOptions` | interface | `{ path, width, height, element? }` | Configures one captured frame: where it is written, the viewport it is shot at, and what it shoots. |
310
312
  | `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. |
311
- | `FrameReading` | interface | `{ width, height, floor }` | Represents one written frame read back from the file a capture produced: its size in device pixels, and the single color its bottom row paints. |
313
+ | `FrameResult` | interface | `{ width, height, floor }` | Represents one written frame read back from the file a capture produced: its size in device pixels, and the single color its bottom row paints. |
312
314
  | `CaptureVariant` | interface | `JourneyVariant` plus `{ apply? }` | Adds to a journey variant the document change a capture run applies before resizing. |
313
315
  | `PortfolioOptions` | interface | `{ states, variants, variant, directory, enabled? }` | Configures a capture portfolio: the state registry, the variant matrix, this run's variant, where it writes, and whether it writes at all. |
314
316
  | `PortfolioInterface` | interface | `{ variant, placements, paths, files }` plus `place` | Holds the registry of capture states one run places, and the files it wrote placing them. |
@@ -316,7 +318,7 @@ a union's arms escaped as `\|`.
316
318
  | `JournalInterface` | interface | `{ steps, output }` plus `start` / `stop` / `record` | Records one scenario: every step it took and everything the page said while it ran. |
317
319
  | `StateOptions` | interface | `WaitOptions` plus `{ absent? }` | Configures a bounded wait over the states a control announces. |
318
320
  | `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. |
321
+ | `AnimationResult` | interface | `{ finite, endless }` | Represents the animations running on one element and its subtree, split by whether each one ends. |
320
322
  | `MediaOptions` | interface | `{ print?, motion?, forced?, pointer? }` | Configures the tester's print medium, motion preference, forced colours, and pointer precision. |
321
323
  | `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
324
  | `AccessibilityNode` | interface | `{ role, name, properties }` | Represents the accessibility node the browser computed for one element. |
@@ -324,7 +326,7 @@ a union's arms escaped as `\|`.
324
326
  | `ProtocolPoint` | interface | `{ x, y }` | Represents one point in the coordinates of the browser's top-level viewport, the space a DevTools input command addresses. |
325
327
  | `StorageOptions` | interface | `{ values?, reads?, writes?, quota? }` | Configures an inert `Storage`: its seed, which operations the host permits, and its quota. |
326
328
  | `WebStorageInterface` | interface | `Storage` plus `{}` plus `permit` | Holds a store the host can withhold and later grant. |
327
- | `CensusReading` | interface | `{ elements, tokens, undeclared }` | Reports an authored-class census: the population walked, the tokens found, and the undeclared. |
329
+ | `CensusResult` | interface | `{ elements, tokens, undeclared }` | Reports an authored-class census: the population walked, the tokens found, and the undeclared. |
328
330
  | `ContrastFixture` | interface | `{ root, refused, accepted }` | Holds a detached translucent stack whose flat and composited readings disagree across one bar. |
329
331
  | `EscapeFixture` | interface | `{ root, inline, embedded, permitted }` | Holds detached markup a style-escape reading must find, and the one it must leave alone. |
330
332
  | `CensusFixture` | interface | `{ root, token, mark }` | Holds detached markup an authored-class census must report as undeclared. |
@@ -340,7 +342,7 @@ A `Shape` cell holds the constant's declared type.
340
342
  | `ACCESSIBLE_ROLES` | const | `readonly string[]` | Names the interactive ARIA roles a bare accessible name is searched across. |
341
343
  | `CANVAS_COLOR` | const | `Color` | Names the color a browser paints an unstyled document with: opaque white. |
342
344
  | `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. |
345
+ | `CAPTURE_STAGE_LIMIT` | const | `number` | Bounds the restagings one capture takes before it refuses a document whose height never settles. |
344
346
  | `CONTENT_ROLES` | const | `readonly string[]` | Names the roles whose accessible name is the text a reader can see inside them. |
345
347
  | `FIELD_ROLES` | const | `Readonly<Record<string, string>>` | Names the role each `input` type carries. |
346
348
  | `FOCUSABLE_SELECTOR` | const | `string` | Names what sequential keyboard navigation can reach, before disabled and unrendered elements go. |
@@ -385,7 +387,8 @@ A `Shape` cell holds the constant's declared type.
385
387
  | `countElements` | function | `(root?: Document \| ShadowRoot) => number` | Counts the elements a tree holds, descending into open shadow roots and same-origin frames. |
386
388
  | `containsNode` | function | `(root: Node, node: Node) => boolean` | Determines whether a node sits inside a root, through open shadow roots and frames. |
387
389
  | `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. |
390
+ | `matchesTraversalName` | function | `(element: HTMLElement, name: string, exact?: boolean) => boolean` | Checks whether a landing can carry a requested accessible name without resolving the document. |
391
+ | `driveTraversal` | function | `(resolve: () => HTMLElement, name: string, candidate?: (element: HTMLElement) => boolean) => Promise<HTMLElement>` | Reaches the control a resolver returns, only through natural forward Tab traversal from the current focus. |
389
392
  | `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
393
  | `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
394
  | `pressKeys` | function | `(keys: string) => Promise<void>` | Sends a key sequence to whatever holds focus, and refuses to send it to nothing. |
@@ -403,7 +406,7 @@ A `Shape` cell holds the constant's declared type.
403
406
  | `waitForFrame` | function | `() => Promise<void>` | Waits for one animation frame to settle pending browser paint work. |
404
407
  | `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
408
  | `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. |
409
+ | `readAnimations` | function | `(element: Element) => AnimationResult` | Reads the animations running on one element and its subtree, split by whether each one ends. |
407
410
  | `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
411
  | `collectScrollRegions` | function | `(root: Element, axis?: 'inline' \| 'block') => readonly HTMLElement[]` | Collects the rendered boxes a person can scroll. |
409
412
  | `describeScrollRegions` | function | `(regions: readonly HTMLElement[]) => string` | Describes the window's scroll position and each scroll region's offsets and extents. |
@@ -448,12 +451,14 @@ A `Shape` cell holds the constant's declared type.
448
451
  | `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
452
  | `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
453
  | `captureFrame` | function | `(options: FrameOptions) => Promise<string>` | Shoots one frame at one viewport size and proves the file on disk holds this run's bytes. |
454
+ | `readCaptureReader` | function | `() => CaptureReader \| undefined` | Reads the optional disk-digest command the tester page registered. |
455
+ | `verifyFrame` | function | `(shot: FrameShot, path: string, reader?: CaptureReader) => Promise<void>` | Verifies a screenshot's destination and compares its returned bytes with the written file. |
451
456
  | `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. |
457
+ | `readFrame` | function | `(path: string) => Promise<FrameResult>` | Reads one written frame back and reports its size and the color its bottom row paints. |
453
458
  | `readShotPixel` | function | `(element: Element, x: number, y: number) => Promise<Color>` | Reads one painted pixel of an element's screenshot. |
454
459
  | `readCascade` | function | `() => ReadonlySet<string>` | Collects every class token the stylesheets loaded into this document actually define. |
455
460
  | `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. |
461
+ | `readCensus` | function | `(root: ParentNode) => CensusResult` | Takes the authored-class census of one subtree against the cascade this document loaded. |
457
462
  | `sendTouch` | function | `(type: 'touchStart' \| 'touchMove' \| 'touchEnd' \| 'touchCancel', element?: Element, point?: ProtocolPoint) => Promise<void>` | Sends one emulated touch event through the browser protocol. |
458
463
  | `tapTouch` | function | `(element: Element, options?: StabilityOptions) => Promise<void>` | Taps an element's centre with emulated touch after its box settles. |
459
464
  | `releaseTouch` | function | `() => Promise<void>` | Cancels emulated touch input, disables touch emulation, and parks the pointer. |
@@ -540,9 +545,26 @@ resolution on its own, and `holdAccessibleWithin` and `traverseAccessibleWithin`
540
545
  resolves the way `holdAccessible` and `traverseAccessible` drive a document-wide one, so a twin of
541
546
  the same name in another region is left alone.
542
547
 
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
548
+ `traverseAccessible` resolves before pressing Tab to preserve its refusal voices, then checks each
549
+ landing with `matchesTraversalName` before resolving again. A candidate is accepted only when the
550
+ fresh resolution returns that very element. No resolved target is retained between presses, so a
551
+ framework can replace it before focus arrives. The candidate check compares names using the role
552
+ query's whitespace normalization, including removal of zero-width spaces and soft hyphens.
553
+ It rejects a mismatch only for a nonempty `aria-label` without `aria-labelledby`, or plain native
554
+ button and link text without associated labels, descendants, explicit roles, owned or shadow content,
555
+ or generated content. Other naming cases conservatively reach the full resolver; `readName` is not
556
+ an equivalent implementation of the provider's accessible-name algorithm. A walk over unrelated
557
+ plain controls therefore avoids the repeated document-wide role scans, while complex naming can
558
+ still require a resolution at each landing.
559
+
560
+ `traverseAccessibleWithin` uses the same candidate check with the region resolver's case-insensitive
561
+ substring semantics, then checks membership in a freshly resolved named region. The full resolver
562
+ still decides role, reachability, ambiguity, and identity at arrival. The keyboard path of
563
+ `chooseAccessible` admits native file inputs as candidates and leaves their label matching to
564
+ `resolveFileInput`, whose label-query semantics differ from the role query.
565
+
566
+ A traversal charges a step only when focus actually lands on an element.
567
+ Consecutive landings on the same native `date`, `time`, `datetime-local`, `month`, or
546
568
  `week` input pass through its editable segments and count that control once, up to
547
569
  `FOCUS_SEGMENT_LIMIT` landings. A longer run or any other revisit ends the traversal, including a
548
570
  consecutive repeat on a button in a focus trap. Any different landing starts a separate run.
@@ -554,7 +576,10 @@ The limit budgets ten presses per candidate. Chromium measured nine landings on
554
576
  input with `step="0.001"`: year, month, day, hour, minute, second, millisecond, and meridiem segments,
555
577
  plus the picker indicator. The budget leaves one spare press per input, so a page of segmented
556
578
  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
579
+ `driveTraversal` implements this forward Tab traversal for any resolver. Its optional candidate
580
+ predicate must admit every landing the supplied resolver could return; without it, the conservative
581
+ exact-name check applies. A resolver with different naming semantics must supply its own candidate
582
+ predicate. `driveHold` is the one pointer drive every hold
558
583
  verb shares, releasing the pointer before refusing when the frame wait or the pressed-state read
559
584
  fails, as well as when the press misses; each verb supplies its resolver and the name its refusals
560
585
  voice.
@@ -735,12 +760,16 @@ through the runner's fitting scale is a thumbnail of the surface. That couples i
735
760
  tester layout, which is contract rather than accident: `vitest@4.1.12` is the version behind the
736
761
  `iframe[data-vitest]` selector and the `--tester-transform`, `--tester-margin-left`,
737
762
  `--viewport-width`, and `--viewport-height` custom properties it writes. A release that renames any
738
- of them reddens the size check rather than writing a wrong frame. Always hand the pane back with
763
+ of them reddens the size check rather than writing a wrong frame. The custom-property overrides
764
+ serve Vitest 4; the pane and iframe also carry `transform: none !important` for Vitest 5's direct
765
+ transform. An already matching viewport skips the resize command while retaining the staging
766
+ rule, paint waits, and rendered-size check. Always hand the pane back with
739
767
  `releasePane`: a tester left pinned at a viewport taller than the window puts its lower half beyond
740
768
  what a pointer can reach, so the next ordinary press fails as a control that is covered. The release
741
769
  also resizes the tester to the viewport it had before the staging, which it reads off the rule
742
770
  element it removes, so the capture's variant size belongs to the frame rather than to every test
743
- that runs after it.
771
+ that runs after it. When the viewport already matches that saved size, the release removes the
772
+ staging rule without sending a resize command.
744
773
 
745
774
  That hand-back is why the pair is a capture's staging and not a resize. Call `page.viewport` from
746
775
  `vitest/browser` where a journey needs its own size — a breakpoint to drive, a variant to act at —
@@ -748,18 +777,34 @@ and leave the tester there. A suite that stages and releases instead resizes the
748
777
  undoes the resize, so its next step runs at the size the file started at. This package publishes no
749
778
  verb for that, because `page.viewport` already is one.
750
779
 
751
- `captureFrame` stages, shoots, and proves the file. The path a screenshot call returns is the path
752
- it meant to write, so `captureFrame` reads that file back through the runner's built-in `readFile`
753
- command and compares it with the shot itself, which is what separates this run's frame from one an
754
- earlier run left behind. It releases the pane in a `finally`, so a refusal at any stage hands the
755
- tester back before it propagates.
780
+ `captureFrame` stages, shoots, and proves the file through `verifyFrame`. The path a screenshot
781
+ returns is the path it meant to write, so verification compares the file's bytes with the returned
782
+ shot as well as checking the requested destination. `readCaptureReader` reads the registered
783
+ command for each frame and reads undefined outside a Vitest browser page, so importing the module
784
+ there never throws. If the consumer registered `readCapture`, Node hashes the file with
785
+ SHA-256 and returns only its lowercase hexadecimal digest; the browser hashes the decoded shot
786
+ with Web Crypto and compares the digests. Without that registration, verification keeps the
787
+ built-in `readFile(path, 'base64')` comparison. A digest command failure propagates; it never
788
+ selects the fallback. Both paths retain the destination and disk-mismatch refusals. The capture
789
+ releases the pane in a `finally`, so a refusal hands the tester back before it propagates.
790
+
791
+ To opt in, add a `readCapture` browser command to the consumer's `configs/commands.ts` and keep
792
+ that command in the browser project's command map. Resolve the `@orkestrel/test/server` export
793
+ through `context.project.vitest.import`, refuse a missing `readCapture` function, and pass it the
794
+ absolute screenshot path. Before reading, apply Vite's `isFileLoadingAllowed` to the normalized
795
+ path against both `context.project.vite.config` and `context.project.vitest.vite.config`; refuse
796
+ the read when neither permits it. This package's `configs/commands.ts` carries that registration.
797
+ Consumers that register no command need no configuration change.
756
798
 
757
799
  A page frame covers the whole document at the width it was given, whatever height it was given. The
758
800
  provider shoots the tester's body in the top-level page's own coordinates, so a document taller than
759
801
  the pane paints for the pane's height and the rows under it are the runner's page: the frame reads
760
802
  as the surface down to the fold and as bare canvas after it. `captureFrame` therefore lays the
761
- document out at the declared viewport, measures the height the shot needs, and stages the pane again
762
- at that height for the shot alone.
803
+ document out at the declared viewport, measures the height the shot needs, and stages the pane at
804
+ that height for the shot alone. When the tester already has the declared width and height, the
805
+ measurement precedes the initial staging, so fixed content needs only its final staging. A capture
806
+ starting at a different viewport first establishes the declared layout. Both paths remeasure after
807
+ every staging and retain the bounded growth correction.
763
808
 
764
809
  The measurement is `measureContent`, floored at the declared height because the declared viewport is
765
810
  the smallest frame a variant asks for. Read the content's edge rather than the body's box: the box
@@ -798,7 +843,7 @@ stretched.
798
843
 
799
844
  The re-reading stops when the pane and the edge agree, which is the pane the shot is taken at. A
800
845
  rule that adds height with every pane never reaches that point, so the re-reading is bounded by
801
- `CAPTURE_STAGINGS` and the shot is refused with
846
+ `CAPTURE_STAGE_LIMIT` and the shot is refused with
802
847
  `Capture frame at <path> never settled after <n> restagings: <h> over a <h> pane` rather than
803
848
  written at a height that is already wrong. That bound is 4: a document holding half the pane plus a
804
849
  fixed block settles in two restagings, one whose growth is capped part way settles in three, and
@@ -919,6 +964,7 @@ A `Shape` cell holds the constant's declared type.
919
964
 
920
965
  | API | Kind | Signature | Summary |
921
966
  | ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
967
+ | `readCapture` | function | `(path: string) => Promise<string>` | Reads the lowercase hexadecimal SHA-256 digest of a captured file's bytes. |
922
968
  | `readInventory` | function | `(root: URL \| string, targets: readonly string[], options?: InventoryOptions) => Readonly<Record<string, string>>` | Reads files from selected targets below a root directory. |
923
969
  | `resolveContained` | function | `(root: string, target: string) => string \| undefined` | Resolves a target that stays below a root directory. |
924
970
  | `requireContained` | function | `(root: string, target: string) => string` | Resolves a target that stays below a root directory, refusing a path outside it. |
@@ -1299,8 +1345,9 @@ absent, present-but-gated, and ambiguous are different findings about an interfa
1299
1345
  | `Tester pane is unavailable for a capture` | `stagePane` |
1300
1346
  | `Tester pane rendered <w>x<h> for a <w>x<h> viewport` | `stagePane` |
1301
1347
  | `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` |
1348
+ | `Capture frame was written to <path> where <path> was asked for` | `captureFrame`, `verifyFrame` |
1349
+ | `Capture frame at <path> is not the one this run shot` | `captureFrame`, `verifyFrame` |
1350
+ | `Capture path must be absolute: <path>` | `readCapture` |
1304
1351
  | `Capture frame at <path> could not be read` | `readFrame` |
1305
1352
  | `Capture frame at <path> is not an image this browser decodes` | `readFrame` |
1306
1353
  | `Capture frame at <path> is not an image this browser decodes: <w>x<h> device pixels` | `readFrame` |
@@ -1355,14 +1402,14 @@ status` is another: the harness writes that attribute at construction and nothin
1355
1402
  writes it, so the reading is a member of `STATECHART_STATUSES` unless a caller took the attribute
1356
1403
  off the root it was handed.
1357
1404
 
1358
- The capture guards are the other population no test drives, because each answers for a runner or a
1359
- provider this package does not control. `Tester pane is unavailable for a capture` fires where
1360
- Vitest stops laying its tester out inside a pane. `Capture frame was written to <path> where <path>
1361
- was asked for` fires where the provider resolves a screenshot path against a base other than the
1362
- calling test file. `Capture frame at <path> is not the one this run shot` fires where the file on
1363
- disk disagrees with the bytes the provider handed back — which a provider that overwrites its target
1364
- never produces, so the suite proves that comparison discriminates with a planted file rather than by
1365
- reaching the refusal. `Capture frame at <path> cannot be measured without a 2D canvas` is narrowing
1405
+ Some capture guards answer for a runner or provider this package does not control. `Tester pane is
1406
+ unavailable for a capture` fires where Vitest stops laying its tester out inside a pane.
1407
+ `Capture frame was written to <path> where <path> was asked for` fires where the provider resolves
1408
+ a screenshot path against a different base. The `verifyFrame` proof drives that refusal by pairing
1409
+ a real screenshot with a different requested destination. `Capture frame at <path> is not the one
1410
+ this run shot` fires where the file disagrees with the returned bytes; the proof overwrites the
1411
+ actual file and reaches this refusal through both digest verification and base64 read-back.
1412
+ `Capture frame at <path> cannot be measured without a 2D canvas` is narrowing
1366
1413
  of the same kind: a canvas allocated for this reading and asked for no other context type hands one
1367
1414
  back. `readShotPixel` carries the same pair for a shot the provider hands back rather than a file:
1368
1415
  `Shot cannot be measured without a 2D canvas` is that narrowing, and `Shot is not an image this
@@ -1724,14 +1771,16 @@ These hold across `src/core`, `src/browser`, `src/server`, and this guide.
1724
1771
  that scale is a thumbnail of the surface. `stagePane` therefore reaches into that layout: the
1725
1772
  `iframe[data-vitest]` selector and the `--tester-transform`, `--tester-margin-left`,
1726
1773
  `--viewport-width`, and `--viewport-height` custom properties are the runner's, not this
1727
- package's, and `captureFrame` reads its written file back through the runner's built-in
1728
- `readFile` command. That is contract rather than accident. A Vitest release that renames any of
1774
+ package's. Direct `transform: none !important` rules also suppress Vitest 5's pane and iframe
1775
+ scaling. The disk check compares Node and browser SHA-256 digests where the consumer registers
1776
+ `readCapture`, and keeps the runner's built-in base64 read-back otherwise. That is contract
1777
+ rather than accident. A Vitest release that renames any of
1729
1778
  them reddens `stagePane`'s size check, which throws
1730
1779
  `Tester pane rendered <w>x<h> for a <w>x<h> viewport` rather than writing a wrong frame. The
1731
1780
  coupling therefore fails loudly, and the version this rule names moves with the fix instead of a
1732
1781
  suite shipping thumbnails nobody inspects. The same layout decides what a frame covers: the
1733
1782
  provider shoots the tester's body in the top-level page's coordinates, so the `captureFrame`
1734
- function stages the pane again at the document's own height wherever the document outruns the
1783
+ function stages the pane at the document's own height wherever the document outruns the
1735
1784
  declared one, and a page frame is neither shorter nor taller than the document it photographs.
1736
1785
  That height is
1737
1786
  `measureContent`, the content's own edge rounded up, rather than the body's box: the box is the
@@ -1739,7 +1788,7 @@ These hold across `src/core`, `src/browser`, `src/server`, and this guide.
1739
1788
  way back down. The edge is read again after every staging, because a rule bound to the viewport
1740
1789
  height lays the document out taller against the taller pane, and each staging carries the growth
1741
1790
  the one before it produced so a converging document lands on its fixed point rather than
1742
- creeping toward it. The re-reading is bounded by `CAPTURE_STAGINGS`, and a document still
1791
+ creeping toward it. The re-reading is bounded by `CAPTURE_STAGE_LIMIT`, and a document still
1743
1792
  growing at that bound is refused rather than photographed at a stale height. An element frame
1744
1793
  keeps the declared pane unless its element needs more room with its native scroll clearance,
1745
1794
  and offsets the calling tester frame only
@@ -3868,10 +3917,10 @@ const written = await captureFrame({
3868
3917
  height: 844,
3869
3918
  })
3870
3919
 
3871
- const reading = await readFrame(written)
3872
- reading.width // 390 — the pane's width, in device pixels
3873
- reading.height // 844 — the pane's height, because this document is shorter than the pane
3874
- reading.floor // 'rgb(0, 128, 0)' — the document's own background, all the way down
3920
+ const result = await readFrame(written)
3921
+ result.width // 390 — the pane's width, in device pixels
3922
+ result.height // 844 — the pane's height, because this document is shorter than the pane
3923
+ result.floor // 'rgb(0, 128, 0)' — the document's own background, all the way down
3875
3924
  ```
3876
3925
 
3877
3926
  The floor is what separates a covered frame from a clipped one. The rows a capture cannot paint are
@@ -4036,8 +4085,15 @@ Each entry names the contracts its file proves. The test names carry the cases.
4036
4085
  Each acting verb takes its happy path and every voice it owns, including both
4037
4086
  region-scoped refusals and both native-disclosure ones; `clickAccessibleWithin` also takes a
4038
4087
  glyph-captioned control inside a region a glyph-carrying heading labels, which is the loose match
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
4088
+ proving unchanged. `traverseAccessible` takes a target after many unrelated Tab stops, a same-name
4089
+ landing outside the accepted roles, a target replaced between landings, and names from referenced
4090
+ labels, associated labels, normalized ARIA labels, transformed text, and generated content.
4091
+ `traverseAccessibleWithin` passes the exact bare twin to reach a loose match inside its region,
4092
+ preserves the bare resolver's ambiguity refusal for exact twins, and reaches a replaced region.
4093
+ `matchesTraversalName` refuses certain mismatches and admits names requiring the full resolver.
4094
+ `driveTraversal` records resolution calls through its existing resolver callback and proves that
4095
+ unrelated plain stops require no full resolution. `traverseAccessible` also takes a crossing for
4096
+ each type in `SEGMENTED_INPUT_TYPES`, and a page of fractional-second `datetime-local` inputs that traversal
4041
4097
  and census both cross with spare presses for another landing per input. As the cap control, it
4042
4098
  takes a lone target whose own focus handler blurs it, so focus never lands
4043
4099
  and the cap fails with an empty trail; and, as the cycle control, the same self-blurring target
@@ -4121,8 +4177,19 @@ Each entry names the contracts its file proves. The test names carry the cases.
4121
4177
  whose last element carries a bottom margin under a body carrying bottom padding, where a reading
4122
4178
  taken from rectangles alone stops 100 rows short. `stagePane` takes the marked pane, the tester
4123
4179
  rendered at the viewport it was given, a release that runs twice without complaining, and a
4124
- release after two stagings that hands back the viewport the tester held before the first;
4125
- `captureFrame` takes a real file written, read back, and matched, with a planted file as the
4180
+ release after two stagings that hands back the viewport the tester held before the first. A
4181
+ runner-channel observer proves that staging an unchanged viewport sends no viewport request,
4182
+ with a real resize as its control; a release always sends one, because the runner refits its
4183
+ frame scale only on that request. A scaled pane and iframe prove the direct transform override.
4184
+ A marker observer distinguishes a tall element's single staging at an already prepared viewport
4185
+ from the declared-layout staging needed when the viewport differs, beside a decoded-size proof.
4186
+ `verifyFrame` takes a real screenshot through both a registered digest reader and an absent
4187
+ reader, and refuses overwritten bytes and the wrong destination through both; missing files
4188
+ propagate their read error. A digest command reading a removed file rejects even while the shot's
4189
+ own file still passes base64 verification, proving that command failures never fall back.
4190
+ The server's `readCapture` proofs pin SHA-256 for known bytes and an
4191
+ empty file, replace a file with different bytes of the same length, and refuse relative and
4192
+ missing paths. `captureFrame` takes a real file written, read back, and matched, with a planted file as the
4126
4193
  comparison's negative control, one element shot rather than the page, and a pane pinned to the
4127
4194
  wrong size by a rule of higher specificity, which is the refusal that also proves the release runs
4128
4195
  on the failing path. It also takes a document taller than the pane, whose frame equals the