@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.
- package/dist/host/agents/skills/enterprise-bootstrap/SKILL.md +91 -82
- package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +5 -5
- package/dist/host/agents/skills/enterprise-bootstrap/references/components.md +10 -10
- package/dist/host/agents/skills/enterprise-bootstrap/references/inputs.md +501 -0
- package/dist/host/agents/skills/enterprise-bootstrap/references/inspection.md +167 -0
- package/dist/host/agents/skills/enterprise-bootstrap/references/utilities.md +2 -2
- package/dist/host/agents/skills/orkestrel-polish-surface/SKILL.md +4 -1
- package/dist/host/agents/skills/orkestrel-polish-surface/references/capture-harness.md +71 -50
- package/dist/host/agents/skills/orkestrel-prove-journey/SKILL.md +93 -29
- package/dist/host/agents/skills/orkestrel-prove-journey/agents/openai.yaml +1 -1
- package/dist/host/agents/skills/orkestrel-prove-journey/references/captures.md +62 -38
- package/dist/host/agents/skills/orkestrel-prove-journey/references/decide.md +68 -0
- package/dist/host/agents/skills/orkestrel-prove-journey/references/layer.md +107 -79
- package/dist/host/agents/skills/orkestrel-prove-journey/references/statechart.md +84 -0
- package/dist/host/agents/skills/orkestrel-prove-journey/references/styles.md +87 -0
- package/dist/host/claude/agents/orkestrel.md +11 -10
- package/dist/host/claude/skills/orkestrel-prove-journey/SKILL.md +1 -1
- package/dist/host/manifest.json +43 -13
- package/dist/src/core/index.cjs +5 -5
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.js +5 -5
- package/dist/src/core/index.js.map +1 -1
- package/package.json +5 -5
|
@@ -1,85 +1,112 @@
|
|
|
1
1
|
# The journey layer
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
7
|
-
|
|
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.
|
|
12
|
-
and `userEvent` from `vitest/browser`; the `@vitest/browser/context` specifier is
|
|
13
|
-
is not the import a
|
|
14
|
-
-
|
|
15
|
-
`
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
26
|
-
resolveAccessible(name
|
|
27
|
-
|
|
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
|
-
-
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
has no `[inert]`
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
| Condition
|
|
48
|
-
|
|
|
49
|
-
| No element carries the name
|
|
50
|
-
| Every match fails a reachability condition
|
|
51
|
-
| Several matches are reachable
|
|
52
|
-
| Still off-viewport after being scrolled to
|
|
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.
|
|
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
|
|
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>`
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
76
|
-
|
|
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
|
|
118
|
+
| `traverseAccessible(name)` | Move focus by forward Tab from wherever focus is, and return the target after focus lands on it. |
|
|
92
119
|
|
|
93
|
-
-
|
|
94
|
-
|
|
95
|
-
-
|
|
96
|
-
|
|
97
|
-
above the cycle so a page with no tab order fails instead of hanging.
|
|
98
|
-
-
|
|
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
|
-
|
|
105
|
-
|
|
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.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
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
|
-
|
|
131
|
-
variant matrix, and the proofs that read what it
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
72
|
-
| `@orkestrel/process` | `0.0.
|
|
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.
|
|
81
|
-
| `@orkestrel/sea` | `0.0.
|
|
82
|
-
| `@orkestrel/server` | `0.0.
|
|
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.
|
|
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.
|
|
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
|