@orkestrel/scaffold 0.0.59 → 0.0.60

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 (23) hide show
  1. package/dist/host/agents/skills/enterprise-bootstrap/SKILL.md +91 -82
  2. package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +5 -5
  3. package/dist/host/agents/skills/enterprise-bootstrap/references/components.md +10 -10
  4. package/dist/host/agents/skills/enterprise-bootstrap/references/inputs.md +501 -0
  5. package/dist/host/agents/skills/enterprise-bootstrap/references/inspection.md +167 -0
  6. package/dist/host/agents/skills/enterprise-bootstrap/references/utilities.md +2 -2
  7. package/dist/host/agents/skills/orkestrel-polish-surface/SKILL.md +4 -1
  8. package/dist/host/agents/skills/orkestrel-polish-surface/references/capture-harness.md +71 -50
  9. package/dist/host/agents/skills/orkestrel-prove-journey/SKILL.md +93 -29
  10. package/dist/host/agents/skills/orkestrel-prove-journey/agents/openai.yaml +1 -1
  11. package/dist/host/agents/skills/orkestrel-prove-journey/references/captures.md +62 -38
  12. package/dist/host/agents/skills/orkestrel-prove-journey/references/decide.md +68 -0
  13. package/dist/host/agents/skills/orkestrel-prove-journey/references/layer.md +107 -79
  14. package/dist/host/agents/skills/orkestrel-prove-journey/references/statechart.md +84 -0
  15. package/dist/host/agents/skills/orkestrel-prove-journey/references/styles.md +87 -0
  16. package/dist/host/claude/agents/orkestrel.md +11 -10
  17. package/dist/host/claude/skills/orkestrel-prove-journey/SKILL.md +1 -1
  18. package/dist/host/manifest.json +43 -13
  19. package/dist/src/core/index.cjs +5 -5
  20. package/dist/src/core/index.cjs.map +1 -1
  21. package/dist/src/core/index.js +5 -5
  22. package/dist/src/core/index.js.map +1 -1
  23. package/package.json +5 -5
@@ -1,85 +1,112 @@
1
1
  # The journey layer
2
2
 
3
- Build every capability here before writing the first journey, and route every journey step through
4
- it. Treat a journey that works around a missing capability as a layer defect.
3
+ Route every journey step through the published layer. Treat a journey that works around a missing
4
+ capability by reaching for a selector as a layer defect.
5
5
 
6
- Implement the signatures below as a contract in the workspace's browser test setup module; never
7
- copy them as source. Name each helper for the human act it performs.
6
+ ## Import, never implement
7
+
8
+ Import every journey helper from `@orkestrel/test/browser`. Write one of your own only where that
9
+ package publishes none for the act.
10
+
11
+ - Place a helper you write in the workspace's browser test setup module, export it from there, and
12
+ name it for the human act it performs.
13
+ - Read the package's own exports before writing anything. A helper that renames a published one is a
14
+ defect under `AGENTS.md`, and a second implementation of one drifts from the first.
15
+ - Code every journey against the vocabulary in this file, which is the published one. Diagnose a
16
+ target that stops resolving here, and fix it in the application.
8
17
 
9
18
  ## What it drives
10
19
 
11
- - Drive the real browser through the installed Vitest browser provider. Import its `page` locators
12
- and `userEvent` from `vitest/browser`; the `@vitest/browser/context` specifier is deprecated and
13
- is not the import a new layer uses.
14
- - Use the provider verbs for input: `click`, `keyboard`, `tab`, `type`, `clear`, `fill`, and
15
- `selectOptions`. Use `page.viewport` and `page.screenshot` for captures, and the runner's file
16
- command to read a written capture back.
17
- - Never dispatch a constructed event from the layer or from a journey. The centralized event
18
- factories `.claude/rules/tests.md` prescribes serve unit tests whose subject is the handler; a
19
- journey drives input through the provider verbs only.
20
- - Never let a helper take an element, a component instance, or a selector from the caller. Every
21
- helper resolves its own target from role and accessible name.
20
+ - Drive the real browser through the installed Vitest browser provider. The published verbs import
21
+ `page` and `userEvent` from `vitest/browser`; the `@vitest/browser/context` specifier is
22
+ deprecated and is not the import a workspace helper uses.
23
+ - Never dispatch a constructed event from a journey. The published `createPointerEvent`,
24
+ `createDragEvent`, `typeInput`, and `commitInput` serve a unit test whose subject is the handler; a
25
+ journey drives input through `clickAccessible`, `clickAccessibleWithin`, `clickDisclosure`,
26
+ `typeAccessible`, `fillAccessible`, `pressKeys`, and `traverseAccessible` only.
27
+ - Yield with `waitForFrame` where a step needs the browser to paint before the next reading. Never
28
+ guard a fact with a fixed delay.
29
+
30
+ ## Which helpers take an element
31
+
32
+ A journey verb resolves its own target from role and accessible name, and refuses an element, a
33
+ component instance, or a selector from the caller. A reader, a fixture builder, and a capture each
34
+ take one, because their subject is a node the caller already holds.
35
+
36
+ | Population | Takes an element |
37
+ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------- |
38
+ | `resolveAccessible`, `resolveRendered`, `clickAccessible`, `clickAccessibleWithin`, `clickDisclosure`, `typeAccessible`, `fillAccessible`, `pressKeys`, `traverseAccessible`, `readPerception`, `readPage`, `readFocus`, `readValue` | No |
39
+ | `readText`, `readRole`, `readName`, `readStates`, `describeTree`, `describeFocus`, `isReachable`, `isRendered` | Yes |
40
+ | `readContrast`, `readRing`, `readLayers`, `readBackdrop`, `readStyle`, `readToken`, `readPixels`, `readClasses`, `extractStyles`, `extractOrphans`, `readRows` | Yes |
41
+ | `mount`, `typeInput`, `commitInput`, `captureFrame`, and a portfolio's `place` | Yes |
42
+
43
+ Never pass an element to a verb from a journey step. Read a step that would pass one as a missing
44
+ verb, and add the verb instead.
22
45
 
23
46
  ## The resolver
24
47
 
25
- ```ts
26
- resolveAccessible(name: string): HTMLElement
27
- resolveAccessible(role: string, name: string): HTMLElement
28
- ```
48
+ `resolveAccessible(name)` resolves across the published interactive roles, and
49
+ `resolveAccessible(role, name)` resolves within exactly the named role. `resolveRendered` sits
50
+ beneath it and applies the same match without the scroll, so an act does not fail on a target the
51
+ act itself scrolls into view.
29
52
 
30
53
  - Match the accessible name exactly, never a substring.
31
- - Search a fixed set of interactive roles for the bare-name form, and exactly the named role for
32
- the two-argument form.
33
- - Count a match as reachable only when every condition holds: it is connected; it passes a
34
- visibility check that honours opacity and CSS; its box has non-zero width and height; its
35
- `tabIndex` is at least zero; it matches neither `:disabled` nor `[aria-disabled="true"]`; and it
36
- has no `[inert]` ancestor.
37
- - Scroll a wholly off-viewport target into view once, then measure reachability again. Count a
38
- control a person can scroll to as reachable, and one that stays outside the viewport as
39
- unreachable.
40
- - Give the layer a rendered-only resolver beneath the public one, and use it from the acting verbs
41
- so a click does not fail on a target the act itself scrolls into view.
54
+ - Read `ACCESSIBLE_ROLES` for the bare-name search set and `FOCUSABLE_SELECTOR` for what the layer
55
+ counts as focusable, rather than restating either list in a workspace.
56
+ - Read reachability with `isReachable` and rendering with `isRendered` where a test needs the
57
+ condition rather than the throw. A reachable match is connected, passes a visibility check that
58
+ honours opacity and CSS, has a box with non-zero width and height, carries a `tabIndex` of at
59
+ least zero, matches neither `:disabled` nor `[aria-disabled="true"]`, and has no `[inert]`
60
+ ancestor.
61
+ - Count a control a person can scroll to as reachable, and one that stays outside the viewport after
62
+ the scroll as unreachable.
42
63
 
43
64
  ### The failure voices
44
65
 
45
- Keep these distinct, and never merge any of them into one message.
46
-
47
- | Condition | The voice it must throw |
48
- | ------------------------------------------ | ---------------------------------------------------------------- |
49
- | No element carries the name | `No interactive element has the accessible name "<name>"` |
50
- | Every match fails a reachability condition | `Interactive target "<name>" is not visible and focus-reachable` |
51
- | Several matches are reachable | `Interactive target "<name>" is ambiguous across <n> elements` |
52
- | Still off-viewport after being scrolled to | `Interactive target "<name>" is unreachable after scrolling` |
66
+ Assert the one voice the case means. Never write an assertion that accepts more than one.
67
+
68
+ | Condition | The voice thrown | Thrown by |
69
+ | ------------------------------------------------ | ------------------------------------------------------------------------------------- | ----------------------- |
70
+ | No element carries the name | `No interactive element has the accessible name "<name>"` | `resolveRendered` |
71
+ | Every match fails a reachability condition | `Interactive target "<name>" is not visible and focus-reachable` | `resolveRendered` |
72
+ | Several matches are reachable | `Interactive target "<name>" is ambiguous across <n> elements` | `resolveRendered` |
73
+ | Still off-viewport after being scrolled to | `Interactive target "<name>" is unreachable after scrolling` | `resolveAccessible` |
74
+ | The region holds no reachable match | `Interactive target "<name>" is not reachable inside "<region>"` | `clickAccessibleWithin` |
75
+ | The region holds several reachable matches | `Interactive target "<name>" is ambiguous across <n> elements inside "<region>"` | `clickAccessibleWithin` |
76
+ | No native disclosure is reachable under the name | `Native disclosure "<name>" is not visible and focus-reachable` | `clickDisclosure` |
77
+ | Several native disclosures carry the name | `Native disclosure "<name>" is ambiguous across <n> elements` | `clickDisclosure` |
78
+ | Forward Tab never lands on the target | `Interactive target "<name>" is not reachable through forward Tab traversal: <trail>` | `traverseAccessible` |
79
+ | The named region is hidden | `Named region "<name>" is not visible` | `readPerception` |
80
+ | Several named regions carry the name | `Named region "<name>" is ambiguous across <n> elements` | `readPerception` |
81
+ | The resolved control renders no value | `Interactive target "<name>" does not carry a value` | `readValue` |
53
82
 
54
83
  - Report an absent control and a present-but-unreachable one as different findings: absence names
55
84
  a missing control, and unreachability names the interface gating one that exists.
56
- - Report ambiguity as a finding about the surface. Name the match count in the message, and
85
+ - Report ambiguity as a finding about the surface. Quote the match count from the message, and
57
86
  re-target the journey by role or region.
87
+ - Read the package's own voice table before asserting a message this file does not list. A workspace
88
+ that transcribes a voice by memory asserts a sentence the package does not throw.
58
89
 
59
90
  ## Role vocabulary
60
91
 
61
- Never infer a role from markup. Confirm the computed role in the browser whenever a target stops
62
- resolving.
92
+ Never infer a role from markup. Confirm the computed role in the browser with `readRole` whenever a
93
+ target stops resolving, and read the exposed name with `readName` and the exposed state with
94
+ `readStates` beside it.
63
95
 
64
96
  - A `list`-bearing input computes `combobox`, not `textbox`. Attaching native suggestion machinery
65
97
  to a field is a role change: re-target every journey that names that field, and read a resolver
66
98
  miss immediately after such a change as this before treating the element as missing.
67
99
  - Always target a tab by its role. A tab and its panel collide on a bare name by construction,
68
100
  because the panel is labelled by its tab.
69
- - `<summary>` is exposed as a native disclosure rather than through a role the provider's role
70
- locators accept. Give the layer a separate disclosure verb keyed to the summary's rendered text,
71
- applying the same reachability conditions and its own failure voices.
101
+ - Drive a `<summary>` with `clickDisclosure`, which applies the same reachability conditions and
102
+ throws its own voices. The provider's role locators do not resolve it, because it is exposed as a
103
+ native disclosure rather than through a role.
72
104
 
73
105
  ## Region-scoped resolution
74
106
 
75
- ```ts
76
- clickAccessibleWithin(region: string, role: string, name: string): Promise<void>
77
- ```
78
-
79
- - Provide this form for repeated short verbs such as `Add`, and for a control whose accessible name
80
- is completed by a status the row renders.
81
- - Apply the same reachability conditions inside the region, and throw voices that name the region
82
- as well as the target.
107
+ Reach for `clickAccessibleWithin(region, role, name)` where a short verb such as `Add` repeats
108
+ across a page, and where a rendered status completes a control's accessible name. It applies the
109
+ same reachability conditions inside the region and names the region in every voice it throws.
83
110
 
84
111
  ## Input and traversal
85
112
 
@@ -88,44 +115,45 @@ clickAccessibleWithin(region: string, role: string, name: string): Promise<void>
88
115
  | `typeAccessible(name, text)` | Focus the field, select all, delete, then send real keystrokes. Escape the provider's key syntax in the text. |
89
116
  | `fillAccessible(name, text)` | Replace the value in one operation for text too long to type. The real element still publishes real input. |
90
117
  | `pressKeys(keys)` | Send a provider keyboard sequence for Enter, arrows, modifiers, and combinations. |
91
- | `traverseAccessible(name)` | Move focus by forward Tab from wherever focus is, and return the target once focus lands on it. |
118
+ | `traverseAccessible(name)` | Move focus by forward Tab from wherever focus is, and return the target after focus lands on it. |
92
119
 
93
- - Count a traversal step only when focus actually lands on an element, and never charge the step
94
- bound for a Tab that moved nothing.
95
- - End the traversal when focus revisits an element, which is one complete cycle of the tab order,
96
- and throw the traversal's own voice, carrying the trail of what focus did reach. Keep a hard cap
97
- above the cycle so a page with no tab order fails instead of hanging.
98
- - Re-resolve the target by role and name on every step, and never hold a node reference across
99
- steps. A framework may replace the node between resolution and focus arrival.
100
- - Never call the browser's focus method to place focus.
120
+ - Reach for `typeAccessible` where the keystrokes are part of what the journey claims, and
121
+ `fillAccessible` where the text is only a payload the person pastes.
122
+ - Let `traverseAccessible` end the walk. It counts a step only where focus lands, stops at one
123
+ complete cycle of the tab order, carries the trail of what focus reached in its voice, and holds a
124
+ cap above the cycle so a page with no tab order fails instead of hanging.
125
+ - Never call the browser's focus method to place focus, and never hold a node reference across
126
+ traversal steps. A framework may replace the node between resolution and focus arrival.
101
127
 
102
128
  ## Perception
103
129
 
104
- ```ts
105
- readPerception(name: string): string
106
- ```
130
+ `readPerception(name)` returns the normalized `innerText` of exactly one visible named region,
131
+ dialog, table, tab panel, alert, or status. Quote that text in assertions.
107
132
 
108
- - Return the normalized `innerText` of exactly one visible named region, dialog, table, tab panel,
109
- alert, or status. Collapse whitespace runs to single spaces and trim.
110
133
  - Read `innerText`, never `textContent`: `innerText` applies CSS transforms and leaves out content
111
- the layout hides. Quote that text in assertions.
112
- - Include descendant visually-hidden text, which a screen reader perceives and which a clip-based
113
- hiding technique leaves in `innerText`.
114
- - Throw when the named region is absent, hidden, or ambiguous.
115
- - Give the layer a whole-page perception reader for cross-region sentences and the vocabulary
116
- sweep, a focus reader that returns the active element's rendered text, and a value reader that
117
- returns a resolved control's value. A perception assertion may quote that value: it is a rendered
118
- fact rather than internal state.
134
+ the layout hides. Descendant visually-hidden text stays in, which is what a screen reader
135
+ perceives.
136
+ - Reach for `readPage` for a cross-region sentence and the vocabulary sweep, `readFocus` for the
137
+ active element's rendered text, and `readValue` for a resolved control's rendered value. A
138
+ perception assertion may quote that value: it is a rendered fact rather than internal state.
139
+ - Reach for `describeTree` and `describeFocus` where the subject is the accessible tree itself —
140
+ roles, names, states, and focus order. They are the accessibility snapshot a written artifact
141
+ composes ([decide.md](decide.md) → The rendered artifact).
119
142
 
120
143
  ## Mounting and cleanup
121
144
 
122
145
  - Mount the shipped root component with its real provisions and return an idempotent cleanup that
123
- unmounts the app and removes its container.
146
+ unmounts the app and removes its container. Reach for the published `mount`, `render`, and `build`
147
+ where a fixture needs a node rather than the application.
124
148
  - Undo everything a journey changed after each test: unmount, destroy the session, reset the theme,
125
- clear the keys the application persisted, and return the route to its entry. Never let a journey
126
- inherit the previous journey's state.
149
+ return the route to its entry, and clear what the application persisted with `clearStorage` and
150
+ `removeDatabase`. Never let a journey inherit the previous journey's state.
151
+ - Record what a journey did with `createJournal`, started inside the journey and stopped in a
152
+ `finally`. Its `steps` and `output` are the evidence a failing journey hands back, and the input
153
+ the run's written artifact composes.
127
154
 
128
155
  ## The capture hook
129
156
 
130
- Give the layer exactly one capture helper. [captures.md](captures.md) owns the registry, the
131
- variant matrix, and the proofs that read what it wrote.
157
+ `createPortfolio` is the capture door and `place(state, element?)` is the hook.
158
+ [captures.md](captures.md) owns the registry, the variant matrix, and the proofs that read what it
159
+ wrote.
@@ -0,0 +1,84 @@
1
+ # The statechart family
2
+
3
+ Declare one transition table, and give it to the automated run that asserts it and to the
4
+ harness a person watches. Never write a second table for the harness.
5
+
6
+ ## Declare the table
7
+
8
+ - Declare each transition as a `StateTransition` carrying its `name`, its `from` state, the `event`,
9
+ and its `to` state. Type it on the entity's own state and event unions, so a row naming a state the
10
+ entity does not have fails to typecheck.
11
+ - Write one `StateScenario` per transition, carrying that `transition` plus `arrange`, `act`, and
12
+ `assert`. Each phase receives the context and the part of the transition it owns.
13
+ - Put the scenarios in the workspace's browser test setup module. Put the table beside them until a
14
+ harness page ships; from then on declare it in the application's own constants module, typed on the
15
+ entity's unions, and import it from the page and from the setup alike, because a page cannot
16
+ import from `tests/` and a second table is what this reference forbids.
17
+ - Declare a transition for every event the surface accepts in every state it accepts it, including
18
+ the event that leaves the state unchanged. A table that lists only the moves the happy path takes
19
+ proves the happy path.
20
+
21
+ ## Run the table
22
+
23
+ - Run the table with `executeScenarios(scenarios, build)` from `@orkestrel/test`. It drives the rows
24
+ in the order they are written, builds a context per row, and stops at the first row that throws.
25
+ - Run one row with `executeScenario(scenario, context)` where a single transition is the subject.
26
+ - Build the context in `build`, which receives the row it is building for. That is what lets one
27
+ table mix fixtures.
28
+ - Let the runner name the failure. It prepends the transition's `name` to whatever the row threw and
29
+ carries the original as the `cause`, so a bare assertion message still says which row failed.
30
+ - Never assert the entity's internal state in `assert` where the transition is one a person drives.
31
+ Assert what the interface renders, through `readPerception`, `readValue`, or `readStates`.
32
+
33
+ ## Drive the act the way the transition happens
34
+
35
+ - Drive `act` through the journey verbs — `clickAccessible`, `clickDisclosure`, `typeAccessible`,
36
+ `pressKeys`, `traverseAccessible` — for every transition a person can cause.
37
+ - Drive `act` through the entity's own API only where the transition is the entity's rather than the
38
+ person's: a lifecycle event, a transport reply, a timer the surface owns.
39
+ - Say which door each row used, in the row's `name`. A table that mixes the doors silently reads as
40
+ a set of user transitions and proves something else.
41
+
42
+ ## Build the harness a person watches
43
+
44
+ Mount the harness on the same table. It stays in the repository that owns the surface: only
45
+ `StateTransition`, `StateScenario`, `executeScenario`, `executeScenarios`, `STATECHART_ATTRIBUTES`,
46
+ and `STATECHART_STATUSES` are published, and a workspace that spells a `data-statechart-*` string of
47
+ its own has left the contract.
48
+
49
+ - Render one play control per transition and one play-all control, each disabled while a run is in
50
+ flight.
51
+ - Render a state badge carrying the entity's current state, an event log of what the entity emitted,
52
+ and a `role="status"` announcer that narrates each step in a sentence, so a screen reader and a
53
+ vision model both read the run without visual chrome.
54
+ - Publish `STATECHART_ATTRIBUTES.status` on the harness root, and cycle its value through
55
+ `STATECHART_STATUSES`: `pending` before a run has a result for every row, `idle` standing ready,
56
+ `running` in flight, and `passed` or `failed` as the terminal reading. Publish
57
+ `STATECHART_ATTRIBUTES.passed`, `.failed`, and `.total` on the same element, so a gate finds the
58
+ harness and reads the tally from one node.
59
+ - Publish `STATECHART_ATTRIBUTES.scenario` and `STATECHART_ATTRIBUTES.result` on each row, and
60
+ `STATECHART_ATTRIBUTES.state` on the element rendering the entity's current state.
61
+ - Write every attribute from the map rather than from a literal. A harness that sets an attribute
62
+ the gate does not read fails silently as a run that never completes.
63
+ - Deep-link one transition and the play-all run from the route, so a decision round names the exact
64
+ link it wants looked at ([decide.md](decide.md) → The rendered artifact).
65
+ - Give the harness a demo step that leaves the widget in its most legible state after the run, for a
66
+ person or a vision model deciding on a look.
67
+ - Pace the harness for a person to watch. The gate inherits that wall time, so budget the gate from
68
+ the row count and the pause rather than from a fixed timeout.
69
+
70
+ ## Gate the harness
71
+
72
+ Prove the harness from the browser project, through the interface:
73
+
74
+ - Mount the harness page and clear the route's query first, so a leftover deep link cannot start the
75
+ walk before the gate does.
76
+ - Press the play-all control through `clickAccessible`, never through a constructed event and never
77
+ by setting the deep link.
78
+ - Poll `STATECHART_ATTRIBUTES.status` until it reads `passed` or `failed`. Never assert it from one
79
+ read after the click.
80
+ - Assert the status reads `passed`, the failed tally reads zero, and the passed tally equals the
81
+ total. Name the failing rows from `STATECHART_ATTRIBUTES.scenario` in the failure message, so a red
82
+ gate says which transition broke.
83
+ - Assert the harness inventory before the rows: a page that mounted no transition passes every tally
84
+ assertion.
@@ -0,0 +1,87 @@
1
+ # Proving what the browser resolved
2
+
3
+ Prove a style from what the browser resolved on the mounted surface. The `enterprise-bootstrap`
4
+ skill's [instruments reference](../../enterprise-bootstrap/references/inspection.md) names each
5
+ instrument's property, its population, and the negative controls that must fail. Take those from
6
+ there and the reading from here.
7
+
8
+ ## Assert the resolved value
9
+
10
+ - Read one property with `readStyle(element, property)` and a length with `readPixels(element, property)`. A
11
+ class present in the markup and absent from the cascade resolves to nothing, and an assertion on
12
+ the class list passes on it.
13
+ - Never substitute `findRule` for a resolved read. It proves a declaration exists, and another rule
14
+ may still win; reach for it where the stylesheet itself is the subject.
15
+ - Compare a color through `matchesColor` or `parseCSSColor` rather than by string. A browser normalizes a color
16
+ expression, so a literal comparison fails on a value that resolved correctly.
17
+
18
+ ## Run per variant
19
+
20
+ The run axis is fixed in [SKILL.md](../SKILL.md) → Read the variant once. The matrix family's own
21
+ readings follow.
22
+
23
+ - Apply each variant's `apply` and its `width` and `height` before the readings, and take every
24
+ reading for that variant before moving to the next.
25
+ - Name the attribute the surface actually reads in `apply`; a Bootstrap surface switches on
26
+ `data-bs-theme`. An `apply` that sets another attribute leaves the run in the default theme, where
27
+ every reading passes.
28
+ - Assert that the run read every declared variant. A matrix that silently walked one variant reports
29
+ a pass for the theme nobody exercised.
30
+ - Report which variants a result covers beside it. A pairing that appears only in a state the run
31
+ never entered is unmeasured.
32
+
33
+ ## Contrast and focus chrome
34
+
35
+ - Read a text pairing with `readContrast(element)`, which composites the painted ancestors to the first
36
+ opaque layer, so a translucent tint reads as a tint over what shows through it.
37
+ - Pass `floor` only where the surface the stack really sits on is known, and pass `CANVAS_COLOR`
38
+ where the browser paints onto its own canvas. Omitting `floor` refuses a stack whose painted layers
39
+ are all translucent; take that refusal as the reading, because an assumed white canvas turns "this
40
+ surface declares no background" into a number that reads like a measurement.
41
+ - Read focus chrome with `readRing(control)`, after focus arrived through `traverseAccessible`,
42
+ `pressKeys`, or a real click. Pass `worn` where the chrome is painted onto a second element such
43
+ as a label. It reports `undefined` for a control not matching `:focus-visible`, for the browser's
44
+ own automatic ring, and for a focus style that only repaints the fill — treat each as a finding
45
+ about the surface rather than as a pass.
46
+ - Carry the negative controls the composited-contrast instrument names, in the same run and composed
47
+ in the harness rather than taken from the surface. An instrument whose negative control passes is
48
+ broken, and its readings are not evidence.
49
+ - Reach for `measureContrast`, `measureLuminance`, `blendColor`, `readLayers`, and `readBackdrop`
50
+ only where the composite itself is the subject. Never re-derive `readContrast` from them.
51
+
52
+ ## The authored-class census
53
+
54
+ Take the property, the population, and the negative controls from the instruments reference →
55
+ Authored class in the shipped cascade. This is the reading.
56
+
57
+ - Read the census as the difference between `readClasses(root)` of the mounted surface and
58
+ `readCascade()`. A token in the difference is a class the markup uses and no loaded stylesheet
59
+ declares.
60
+ - Take `root` from the mounted surface, so the census covers what rendered rather than what a
61
+ template file spells.
62
+ - Report what `readClasses` walked as the population, and fail a run that walked none.
63
+ - Append the extraction-door negative control to that same `root`, so it reaches the difference
64
+ through `readClasses` rather than beside it.
65
+
66
+ ## Style escapes
67
+
68
+ Take the property, the population, the named exemptions, and the negative control from the
69
+ instruments reference → Style escapes. This is the reading.
70
+
71
+ - Read escapes with `extractStyles(root)`, which returns the markup of every hit it found.
72
+ - Take the reading before any journey drives the surface, because the population is the undriven
73
+ tree.
74
+ - Append the harness-built negative control element to that same `root`, so it reaches the reading
75
+ through `extractStyles`.
76
+ - Reach for `extractOrphans` where the finding is a child element rendered outside its required
77
+ parent, and `readRows` where the subject is a repeated row's rendered text.
78
+
79
+ ## Tokens
80
+
81
+ - Read a token with `readToken(element, name)` where inheritance matters and `readRootToken(name)` where the
82
+ document declares it. The leading dashes are optional in each.
83
+ - Compare values, never presence. An absent token and one declared empty both read as `''`, and a
84
+ `var()` naming an undeclared property resolves to the inherited color rather than refusing, so an
85
+ assertion on presence passes on a token nobody declared.
86
+ - Read each token once per variant and assert the values differ where the design says the variants
87
+ differ. A pair of variants that resolves a token identically is a theme that did not switch.
@@ -50,8 +50,9 @@ so network-controlled descriptions never enter agent instruction context.
50
50
  | `@orkestrel/brief` | `0.0.6` | L4 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/interpret` `^0.0.11`, `@orkestrel/reason` `^0.0.8` |
51
51
  | `@orkestrel/browser` | `0.0.14` | L3 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/html` `^0.0.7`, `@orkestrel/websocket` `^0.0.10` |
52
52
  | `@orkestrel/budget` | `0.0.8` | L1 | `@orkestrel/contract` `^0.0.13` |
53
+ | `@orkestrel/codec` | `0.0.1` | L0 | |
53
54
  | `@orkestrel/console` | `0.0.11` | L2 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8` |
54
- | `@orkestrel/contract` | `0.0.13` | L0 | |
55
+ | `@orkestrel/contract` | `0.0.15` | L0 | |
55
56
  | `@orkestrel/csv` | `0.0.5` | L1 | `@orkestrel/contract` `^0.0.13` |
56
57
  | `@orkestrel/database` | `0.0.12` | L2 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/indexeddb` `^0.0.9`, `@orkestrel/sqlite` `^0.0.9` |
57
58
  | `@orkestrel/emitter` | `0.0.8` | L1 | `@orkestrel/contract` `^0.0.13` |
@@ -60,16 +61,16 @@ so network-controlled descriptions never enter agent instruction context.
60
61
  | `@orkestrel/html` | `0.0.7` | L1 | `@orkestrel/contract` `^0.0.13` |
61
62
  | `@orkestrel/indexeddb` | `0.0.9` | L1 | `@orkestrel/contract` `^0.0.13` |
62
63
  | `@orkestrel/interpret` | `0.0.11` | L3 | `@orkestrel/reason` `^0.0.8`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/template` `^0.0.5` |
63
- | `@orkestrel/lsp` | `0.0.4` | L3 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/process` `^0.0.7` |
64
+ | `@orkestrel/lsp` | `0.0.5` | L3 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/process` `^0.0.8` |
64
65
  | `@orkestrel/markdown` | `0.0.12` | L2 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/html` `^0.0.7` |
65
- | `@orkestrel/mcp` | `0.0.26` | L3 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/process` `^0.0.7`, `@orkestrel/sse` `^0.0.5`, `@orkestrel/tool` `^0.0.12`, `@orkestrel/websocket` `^0.0.10` |
66
+ | `@orkestrel/mcp` | `0.0.27` | L3 | `@orkestrel/codec` `^0.0.1`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/process` `^0.0.8`, `@orkestrel/sse` `^0.0.5`, `@orkestrel/tool` `^0.0.12`, `@orkestrel/websocket` `^0.0.10` |
66
67
  | `@orkestrel/middleware` | `0.0.18` | L2 | `@orkestrel/abort` `^0.0.8`, `@orkestrel/budget` `^0.0.8`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/timeout` `^0.0.8` |
67
68
  | `@orkestrel/msg` | `0.0.8` | L0 | |
68
69
  | `@orkestrel/ndjson` | `0.0.8` | L1 | `@orkestrel/contract` `^0.0.13` |
69
70
  | `@orkestrel/ollama` | `0.0.13` | L6 | `@orkestrel/agent` `^0.0.19`, `@orkestrel/budget` `^0.0.8`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/ndjson` `^0.0.8`, `@orkestrel/timeout` `^0.0.8`, `@orkestrel/tool` `^0.0.12` |
70
71
  | `@orkestrel/pool` | `0.0.9` | L2 | `@orkestrel/emitter` `^0.0.8` |
71
- | `@orkestrel/probe` | `0.0.10` | L4 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/lsp` `^0.0.4`, `@orkestrel/mcp` `^0.0.26`, `@orkestrel/queue` `^0.0.11`, `@orkestrel/timeout` `^0.0.8`, `@orkestrel/tool` `^0.0.12` |
72
- | `@orkestrel/process` | `0.0.7` | L2 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8` |
72
+ | `@orkestrel/probe` | `0.0.11` | L4 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/lsp` `^0.0.5`, `@orkestrel/mcp` `^0.0.27`, `@orkestrel/queue` `^0.0.11`, `@orkestrel/timeout` `^0.0.8`, `@orkestrel/tool` `^0.0.12` |
73
+ | `@orkestrel/process` | `0.0.9` | L2 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8` |
73
74
  | `@orkestrel/program` | `0.0.11` | L4 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/qualifier` `^0.0.12`, `@orkestrel/rater` `^0.0.12`, `@orkestrel/reason` `^0.0.8` |
74
75
  | `@orkestrel/qualifier` | `0.0.12` | L3 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/reason` `^0.0.8` |
75
76
  | `@orkestrel/queue` | `0.0.11` | L3 | `@orkestrel/abort` `^0.0.8`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/database` `^0.0.12`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/timeout` `^0.0.8` |
@@ -77,19 +78,19 @@ so network-controlled descriptions never enter agent instruction context.
77
78
  | `@orkestrel/reason` | `0.0.8` | L2 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8` |
78
79
  | `@orkestrel/relation` | `0.0.10` | L3 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/database` `^0.0.12`, `@orkestrel/emitter` `^0.0.8` |
79
80
  | `@orkestrel/router` | `0.0.12` | L2 | `@orkestrel/abort` `^0.0.8`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8` |
80
- | `@orkestrel/scaffold` | `0.0.58` | L3 | `@orkestrel/console` `^0.0.11`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/markdown` `^0.0.12`, `@orkestrel/process` `^0.0.7`, `@orkestrel/template` `^0.0.5` |
81
- | `@orkestrel/sea` | `0.0.12` | L3 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/process` `^0.0.7` |
82
- | `@orkestrel/server` | `0.0.16` | L3 | `@orkestrel/abort` `^0.0.8`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/router` `^0.0.12`, `@orkestrel/timeout` `^0.0.8` |
81
+ | `@orkestrel/scaffold` | `0.0.59` | L3 | `@orkestrel/console` `^0.0.11`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/markdown` `^0.0.12`, `@orkestrel/process` `^0.0.8`, `@orkestrel/template` `^0.0.5` |
82
+ | `@orkestrel/sea` | `0.0.13` | L3 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/process` `^0.0.8` |
83
+ | `@orkestrel/server` | `0.0.17` | L3 | `@orkestrel/abort` `^0.0.8`, `@orkestrel/codec` `^0.0.1`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/router` `^0.0.12`, `@orkestrel/timeout` `^0.0.8` |
83
84
  | `@orkestrel/sqlite` | `0.0.9` | L1 | `@orkestrel/contract` `^0.0.13` |
84
85
  | `@orkestrel/sse` | `0.0.5` | L0 | |
85
86
  | `@orkestrel/supervisor` | `0.0.1` | L5 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.9`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/workflow` `^0.0.12` |
86
87
  | `@orkestrel/table` | `0.0.3` | L2 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8` |
87
88
  | `@orkestrel/template` | `0.0.5` | L2 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8` |
88
89
  | `@orkestrel/terminal` | `0.0.13` | L3 | `@orkestrel/console` `^0.0.11`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/database` `^0.0.12`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/form` `^0.0.3`, `@orkestrel/sse` `^0.0.5` |
89
- | `@orkestrel/test` | `0.0.11` | L0 | |
90
+ | `@orkestrel/test` | `0.0.12` | L0 | |
90
91
  | `@orkestrel/timeout` | `0.0.8` | L1 | `@orkestrel/contract` `^0.0.13` |
91
92
  | `@orkestrel/tool` | `0.0.12` | L1 | `@orkestrel/contract` `^0.0.13` |
92
- | `@orkestrel/toolbox` | `0.0.10` | L6 | `@orkestrel/agent` `^0.0.19`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/database` `^0.0.12`, `@orkestrel/form` `^0.0.3`, `@orkestrel/relation` `^0.0.10`, `@orkestrel/server` `^0.0.16`, `@orkestrel/terminal` `^0.0.13`, `@orkestrel/tool` `^0.0.12`, `@orkestrel/workflow` `^0.0.16`, `@orkestrel/workspace` `^0.0.6` |
93
+ | `@orkestrel/toolbox` | `0.0.11` | L6 | `@orkestrel/agent` `^0.0.19`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/database` `^0.0.12`, `@orkestrel/form` `^0.0.3`, `@orkestrel/relation` `^0.0.10`, `@orkestrel/server` `^0.0.17`, `@orkestrel/terminal` `^0.0.13`, `@orkestrel/tool` `^0.0.12`, `@orkestrel/workflow` `^0.0.16`, `@orkestrel/workspace` `^0.0.6` |
93
94
  | `@orkestrel/websocket` | `0.0.10` | L2 | `@orkestrel/emitter` `^0.0.8` |
94
95
  | `@orkestrel/worker` | `0.0.10` | L4 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/database` `^0.0.12`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/pool` `^0.0.9`, `@orkestrel/queue` `^0.0.11` |
95
96
  | `@orkestrel/workflow` | `0.0.16` | L4 | `@orkestrel/abort` `^0.0.8`, `@orkestrel/budget` `^0.0.8`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/database` `^0.0.12`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/queue` `^0.0.11`, `@orkestrel/timeout` `^0.0.8` |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: orkestrel-prove-journey
3
- description: Prove a browser application the way a person uses it — real keystrokes, clicks, and Tab/Enter against only what is visible and reachable — and generate the capture portfolio from those same journeys. Use when accepting a UI build, proving an application end to end, deciding whether a surface is reachable by keyboard alone, proving what a screen refuses as well as what it does, auditing whether the interface speaks the user's vocabulary rather than the engine's, producing the screenshots a design review judges, or whenever the only evidence a screen works is a test that drove it through JavaScript instead of through the interface.
3
+ description: Prove a browser application the way a person uses it — real keystrokes, clicks, and Tab/Enter against only what is visible and reachable — through the journey layer @orkestrel/test/browser publishes, and generate the capture portfolio, the resolved-style matrix, and the statechart outcome from those same journeys. Use when accepting a UI build, proving an application end to end, deciding whether a surface is reachable by keyboard alone, proving what a screen refuses as well as what it does, proving the styles a browser actually resolved under each theme and viewport, driving a transition table through the interface and watching it run, auditing whether the interface speaks the user's vocabulary rather than the engine's, producing the screenshots a design review judges, routing a rendered question to an artifact a model can read, or whenever the only evidence a screen works is a test that drove it through JavaScript instead of through the interface.
4
4
  ---
5
5
 
6
6
  # Load the canonical workflow