@orkestrel/scaffold 0.0.81 → 0.0.82
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/bin/main.js +167 -13
- package/dist/bin/main.js.map +1 -1
- package/dist/host/AGENTS.md +5 -3
- package/dist/host/agents/skills/orkestrel-harden/references/centralization.md +2 -2
- package/dist/host/agents/skills/orkestrel-journey/SKILL.md +41 -38
- package/dist/host/agents/skills/orkestrel-journey/references/captures.md +3 -3
- package/dist/host/agents/skills/orkestrel-journey/references/decide.md +3 -3
- package/dist/host/agents/skills/orkestrel-journey/references/layer.md +5 -5
- package/dist/host/agents/skills/orkestrel-journey/references/statechart.md +3 -3
- package/dist/host/agents/skills/orkestrel-journey/references/styles.md +9 -9
- package/dist/host/claude/rules/application.md +20 -6
- package/dist/host/claude/rules/architecture.md +2 -2
- package/dist/host/claude/rules/browser.md +9 -0
- package/dist/host/claude/rules/documentation.md +2 -1
- package/dist/host/claude/rules/styles.md +35 -12
- package/dist/host/claude/rules/tests.md +15 -10
- package/dist/host/claude/rules/workspace.md +128 -98
- package/dist/host/claude/skills/orkestrel-journey/SKILL.md +1 -1
- package/dist/host/configs/helpers.ts +157 -9
- package/dist/host/configs/policy.ts +64 -61
- package/dist/host/dotfiles/oxlintrc.json +132 -16
- package/dist/host/dotfiles/prettierignore +1 -1
- package/dist/host/guides/README.md +9 -5
- package/dist/host/guides/scaffold.md +475 -115
- package/dist/host/manifest.json +26 -26
- package/dist/host/tests/config.test.ts +996 -39
- package/dist/host/tests/policy.test.ts +11 -0
- package/dist/host/tests/setupPolicy.ts +396 -22
- package/dist/src/core/index.cjs +1261 -159
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +297 -35
- package/dist/src/core/index.d.ts +297 -35
- package/dist/src/core/index.js +1241 -160
- package/dist/src/core/index.js.map +1 -1
- package/package.json +1 -1
package/dist/host/AGENTS.md
CHANGED
|
@@ -16,14 +16,16 @@
|
|
|
16
16
|
## Project model
|
|
17
17
|
|
|
18
18
|
```text
|
|
19
|
-
src/ published library: core, browser, server, optional styles
|
|
20
|
-
app/ application: core, browser, server
|
|
19
|
+
src/ published library: core, browser, server, optional styles, extension faces
|
|
20
|
+
app/ application: core, browser, server, extension faces
|
|
21
21
|
tests/ mirrors source; setup*.ts owns shared test infrastructure
|
|
22
22
|
configs/ thin target wrappers around root Vite/TypeScript configuration
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
- `core` is host-independent. Browser and server may import core; core imports neither; browser and server never import each other.
|
|
26
26
|
- `app/core` is host-independent. `app/server` may import `app/core`, `src/core`, and `src/server`, never browser code. `app/browser` may import `app/core`, `src/core`, and `src/browser`, and reaches server behavior through shared contracts and transports only.
|
|
27
|
+
- The browser surface is `src/browser`, `app/browser`, the journey, and the showcase; the styles surface is `src/styles` and its themes. An extension adds a face to a surface: the `vue` browser extension adds `src/vue` and `app/vue`, and a named styles extension adds `src/<name>`.
|
|
28
|
+
- `src/vue` and each `src/<name>` may import `src/core` and `src/browser`. `app/vue` may import what `app/browser` may import, plus `app/browser` and `src/vue`. No extension face imports server code or another extension's face.
|
|
27
29
|
- Published source never imports private app code.
|
|
28
30
|
- Enforce boundaries with the toolchain (Oxlint import restrictions, scoped TypeScript projects, Vite graphs). Add no second parser for TypeScript, Oxlint, Vue, HTML, CSS, or Vite.
|
|
29
31
|
- `tsconfig.json`, `vite.config.ts`, and each `*/types.ts` are their sources of truth. Keep structural files even when empty.
|
|
@@ -57,7 +59,7 @@ configs/ thin target wrappers around root Vite/TypeScript configuration
|
|
|
57
59
|
- **Named discriminants.** Name the axis (`relationship`, `command`, `category`), never `kind` or `type`.
|
|
58
60
|
- **Centralize by kind.** Types, constants, helpers, validators, parsers, factories, and errors live in their kind file. An implementation file holds one class plus imports.
|
|
59
61
|
- **Export and test reusable logic.** Fold a trivial one-use helper into its caller or export it from its kind file and test it.
|
|
60
|
-
- **No nested functions**, except
|
|
62
|
+
- **No nested functions**, except a callback passed as an argument or returned as the result; `.claude/rules/architecture.md` § Functions and orchestration bounds the literal positions it climbs.
|
|
61
63
|
- **Functional core, imperative shell.** Pure exported leaves; stateful orchestration as class methods. A method never forwards 1:1 to a helper.
|
|
62
64
|
- **No superfluous wrappers.** A wrapper adds a boundary, invariant, composition, translation, lifecycle, or materially narrower contract, or it goes.
|
|
63
65
|
- **Minimal public API.** Create or substantively expand a capability with its first real consumer; this gate applies at creation, never later. Expose an existing reusable capability through its environment barrel regardless of consumer count. Remove a symbol only when the capability itself must not exist. Prefer one minimal interface and one shared engine, with a native backend override only for a faster path.
|
|
@@ -28,8 +28,8 @@ execution and must explain why sibling imports cannot work.
|
|
|
28
28
|
| Defining recursive or compositional spine | Class method, after extracting its pure leaves |
|
|
29
29
|
| Trivial and genuinely one-use | Inline it into the caller |
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
Apply `.claude/rules/architecture.md` § Functions and orchestration to nested functions and
|
|
32
|
+
callback positions.
|
|
33
33
|
|
|
34
34
|
## Hunt the wrapper
|
|
35
35
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: orkestrel-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 — 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
|
|
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 screen 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
|
# Prove an application through human journeys
|
|
@@ -23,25 +23,27 @@ each ruling was taken at.
|
|
|
23
23
|
|
|
24
24
|
## Declare the families
|
|
25
25
|
|
|
26
|
-
Declare in the browser environment's `integration.test.ts` which families that
|
|
26
|
+
Declare in the browser environment's `integration.test.ts` which families that screen carries, and
|
|
27
27
|
assert in the always-on proofs that every declared family is present. A declaration names which
|
|
28
|
-
families a
|
|
28
|
+
families a screen owes. It never switches what a declared family proves.
|
|
29
29
|
|
|
30
30
|
| Family | Declared | Proves |
|
|
31
31
|
| ---------- | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
|
|
32
32
|
| Journey | Always | Each user intent reaches its outcome through the interface |
|
|
33
|
-
| Refusal | Always | Each control the
|
|
34
|
-
| Matrix | Where the
|
|
33
|
+
| Refusal | Always | Each control the screen withholds, through one exact failure voice |
|
|
34
|
+
| Matrix | Where the journey wrapper declares more than one variant | The values the browser resolved under each declared variant |
|
|
35
35
|
| Statechart | Where a journey drives a transition of an entity carrying its own state and event vocabulary | Each declared transition, driven through the interface where it can be |
|
|
36
|
-
| Transport | Where the
|
|
36
|
+
| Transport | Where the screen persists or restarts | Persistence, restart, and storage failure through real implementations |
|
|
37
37
|
| Capture | Under the capture flag | The registry times the variants, each registered file written to disk |
|
|
38
38
|
|
|
39
|
-
-
|
|
39
|
+
- Prove Matrix in the arrival journey `scaffold new` seeds while its wrapper declares more than one
|
|
40
|
+
variant: read one resolved value under each declared variant.
|
|
41
|
+
- Refuse a declaration that omits a family whose trigger the screen meets. Report the omission as a
|
|
40
42
|
scope finding and stop; never prove the remaining families around it.
|
|
41
43
|
- Assert the declaration itself: a family listed with no proof, and a proof belonging to no listed
|
|
42
44
|
family, each fail the run.
|
|
43
45
|
- Bind the journey laws to every declared family, not to the journey family alone. A matrix reading
|
|
44
|
-
and a transport assertion reach their
|
|
46
|
+
and a transport assertion reach their screen through the same verbs a journey reaches it through.
|
|
45
47
|
- Change route and theme through the interface in the matrix family and the transport family. A
|
|
46
48
|
family that navigates by calling the application's own router proves the router, and says nothing
|
|
47
49
|
about the screen it reads afterwards.
|
|
@@ -92,9 +94,10 @@ start, and let it choose the capture destination, the matrix row, and the statec
|
|
|
92
94
|
- Run the axis with `npm run test:journey`, which runs that wrapper and joins the `test` chain. Set
|
|
93
95
|
`CAPTURE` to `1` in your own shell and run `npm run test:journey` again to write the frames; the
|
|
94
96
|
root configuration reads that variable and provides it as `capture`.
|
|
95
|
-
- Keep the journeys in `tests/app/browser/integration.test.ts
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
- Keep the journeys in `tests/app/browser/integration.test.ts`, and the journeys of the `vue`
|
|
98
|
+
extension's application in `tests/app/vue/integration.test.ts`, which `npm run test:journey:vue`
|
|
99
|
+
runs. Each variant project of a mode collects that file alone, and the application's ordinary
|
|
100
|
+
project excludes it while the axis is on, so a journey written anywhere else runs in no variant.
|
|
98
101
|
- Loop every declared variant inside one run for the matrix family
|
|
99
102
|
([styles.md](references/styles.md) → Run per variant).
|
|
100
103
|
- Render exactly one variant per run for the capture family
|
|
@@ -120,7 +123,7 @@ whose text names `vitest`, so a script naming another runner's configuration rai
|
|
|
120
123
|
to `createPortfolio` when capture needs no additional synchronous document change. Never attach
|
|
121
124
|
an asynchronous action to `CaptureVariant.apply` or re-resolve a covered theme control at capture
|
|
122
125
|
time.
|
|
123
|
-
- Where the
|
|
126
|
+
- Where the screen has no theme control, prepare the theme through the attribute the screen
|
|
124
127
|
reads. Use the optional `CaptureVariant.apply` hook only for a synchronous document change,
|
|
125
128
|
such as setting that attribute. Treat its `() => void` contract as synchronous: `createPortfolio`
|
|
126
129
|
invokes the hook without awaiting a returned promise.
|
|
@@ -194,15 +197,15 @@ await PORTFOLIO.place('home')
|
|
|
194
197
|
5. **Generate the portfolio from the acceptance journeys.** Place each registered state inside the
|
|
195
198
|
journey that reaches it, and never register a state no journey reaches.
|
|
196
199
|
6. **Commit a value through an act a person performs:** `pressKeys('{Enter}')` on the focused
|
|
197
|
-
control, a Tab away, or a named button. Report a
|
|
198
|
-
unpredictable event, or only after work the person cannot observe as a
|
|
200
|
+
control, a Tab away, or a named button. Report a screen that commits on a timer, on an
|
|
201
|
+
unpredictable event, or only after work the person cannot observe as a screen finding, and never
|
|
199
202
|
work around it in the layer.
|
|
200
203
|
7. **Type only what a person would.** Journeys carry trusted input; adversarial payloads belong to
|
|
201
204
|
the transport family and the parser suites.
|
|
202
205
|
8. **Perform every interaction step unconditionally.** Never gate a step on whether the control it
|
|
203
206
|
is about to drive exists or is reachable, and never branch a journey on `readRefusal`. Let the
|
|
204
207
|
resolver's failure voice name what the interface withheld. A guarded step passes whether or not
|
|
205
|
-
the control was there, so the run goes green on a
|
|
208
|
+
the control was there, so the run goes green on a screen that removed the control.
|
|
206
209
|
|
|
207
210
|
## Import the journey layer
|
|
208
211
|
|
|
@@ -231,7 +234,7 @@ placement and scope `.claude/rules/tests.md` fixes.
|
|
|
231
234
|
|
|
232
235
|
- Enter through the real entry: mount the shipped root component with a real store and the route a
|
|
233
236
|
person lands on, and let the application load itself.
|
|
234
|
-
- Reach each
|
|
237
|
+
- Reach each screen's own controls through forward Tab traversal in at least one journey.
|
|
235
238
|
- Type keystroke by keystroke where the keystrokes are the subject; fill in one operation where the
|
|
236
239
|
text is only a payload the person pastes.
|
|
237
240
|
- Poll every fact the application produces asynchronously until it converges. Never assert one from
|
|
@@ -243,38 +246,38 @@ placement and scope `.claude/rules/tests.md` fixes.
|
|
|
243
246
|
sentence and carry the replaced one in `absent`, so the reading that resolves carries one and not
|
|
244
247
|
the other ([layer.md](references/layer.md) → The waits).
|
|
245
248
|
- Assert the state a control announces beside every drive that sets it, and on an unselected
|
|
246
|
-
sibling. A control announcing state owes this assertion whether or not the
|
|
249
|
+
sibling. A control announcing state owes this assertion whether or not the screen carries the
|
|
247
250
|
statechart family.
|
|
248
251
|
- After a confirmed destructive action, assert through trusted input that focus landed on a visible,
|
|
249
252
|
announced location.
|
|
250
253
|
- Assert the whole page's perception never matches the vocabulary the product does not speak —
|
|
251
254
|
engine, schema, and implementation words the interface is supposed to translate.
|
|
252
|
-
- Report a bare accessible name that answers for more than one reachable element on one screen as a
|
|
253
|
-
finding, and target through role or region until the
|
|
255
|
+
- Report a bare accessible name that answers for more than one reachable element on one screen as a screen
|
|
256
|
+
finding, and target through role or region until the screen is fixed.
|
|
254
257
|
|
|
255
|
-
### The intents every
|
|
258
|
+
### The intents every screen owes
|
|
256
259
|
|
|
257
|
-
Write a journey for each of the following wherever the
|
|
260
|
+
Write a journey for each of the following wherever the screen has that state. Take the expected
|
|
258
261
|
outcome from the product guide; this skill supplies the mechanism and invents no copy, no redirect,
|
|
259
262
|
and no title scheme.
|
|
260
263
|
|
|
261
|
-
| Intent | The journey proves
|
|
262
|
-
| ----------------- |
|
|
263
|
-
| Arrival | The entry route renders its own screen, read through a named region
|
|
264
|
-
| An unknown route | What the application does with a route it does not carry, and what it says
|
|
265
|
-
| An empty result | What a query matching nothing renders, in the product's own words
|
|
266
|
-
| The document name | The title each screen publishes, asserted per screen
|
|
267
|
-
| A render failure | What a person reads when a component throws, rather than an unexplained
|
|
264
|
+
| Intent | The journey proves |
|
|
265
|
+
| ----------------- | ------------------------------------------------------------------------------ |
|
|
266
|
+
| Arrival | The entry route renders its own screen, read through a named region |
|
|
267
|
+
| An unknown route | What the application does with a route it does not carry, and what it says |
|
|
268
|
+
| An empty result | What a query matching nothing renders, in the product's own words |
|
|
269
|
+
| The document name | The title each screen publishes, asserted per screen |
|
|
270
|
+
| A render failure | What a person reads when a component throws, rather than an unexplained screen |
|
|
268
271
|
|
|
269
272
|
- Report a missing outcome as a product finding, with its evidence site, rather than inventing the
|
|
270
|
-
copy the
|
|
273
|
+
copy the screen owes.
|
|
271
274
|
- Assert the title from `document.title` per screen, against the title the product guide names for
|
|
272
275
|
that screen. Report a screen the guide gives no title as a product finding.
|
|
273
276
|
|
|
274
277
|
## Prove the refusals
|
|
275
278
|
|
|
276
|
-
- Give every
|
|
277
|
-
journey has put the
|
|
279
|
+
- Give every screen a refusal family: the controls a person must not reach in the state the
|
|
280
|
+
journey has put the screen in.
|
|
278
281
|
- Assert the exact failure voice the case means. Never write an assertion that accepts more than
|
|
279
282
|
one voice ([layer.md](references/layer.md) → The failure voices).
|
|
280
283
|
- Cover the restrictions the interface imposes on itself: a collapsed panel's field, a verb
|
|
@@ -296,11 +299,11 @@ and no title scheme.
|
|
|
296
299
|
`QuotaExceededError`.
|
|
297
300
|
- Prove the visible half in a journey: the failure sentence a person reads, and the retry control
|
|
298
301
|
that clears it. A storage failure whose visible half is a control that silently does nothing is a
|
|
299
|
-
|
|
302
|
+
screen finding.
|
|
300
303
|
- Assert restart by starting a second session over the same store and polling the restored value.
|
|
301
304
|
- Take a stalled read to the application's own asynchronous store contract, never to `Storage`.
|
|
302
305
|
`Storage` is synchronous, so a hanging read is not expressible against it and a fixture that fakes
|
|
303
|
-
one is proving a
|
|
306
|
+
one is proving a store contract the application does not have.
|
|
304
307
|
|
|
305
308
|
## Prove the styles
|
|
306
309
|
|
|
@@ -350,16 +353,16 @@ Completion requires all of:
|
|
|
350
353
|
|
|
351
354
|
- every in-scope user intent reaching its outcome through the interface, with no step that reaches
|
|
352
355
|
past it;
|
|
353
|
-
- the intents every
|
|
356
|
+
- the intents every screen owes present wherever the screen has the state, each outcome taken from
|
|
354
357
|
the product guide and every missing one reported as a product finding;
|
|
355
|
-
- keyboard-only reachability proven on every
|
|
356
|
-
- a refusal family per
|
|
358
|
+
- keyboard-only reachability proven on every screen the journeys cover;
|
|
359
|
+
- a refusal family per screen, each asserting one exact failure voice;
|
|
357
360
|
- the transport family declared separately, driven through real implementations, and convergent;
|
|
358
361
|
- the declared families each proven, and the declaration itself asserted;
|
|
359
362
|
- the matrix family read once per declared variant, each style reading carrying its published
|
|
360
363
|
control from [styles.md](references/styles.md) → The published controls in the same run, and the
|
|
361
364
|
contrast reading's control straddling its declared bar;
|
|
362
|
-
- the authored-class census and the `extractStyles` reading taken on the mounted
|
|
365
|
+
- the authored-class census and the `extractStyles` reading taken on the mounted screen, each
|
|
363
366
|
reporting the population it walked;
|
|
364
367
|
- the statechart table driven to a terminal status with no failed row, and the harness tally read
|
|
365
368
|
from the object and from its markup;
|
|
@@ -378,5 +381,5 @@ block is the home of that limit and of the condition that reopens it. Cite that
|
|
|
378
381
|
neither into a verdict.
|
|
379
382
|
|
|
380
383
|
Report each journey by the intent it proves, the refusals it establishes, the states it placed, the
|
|
381
|
-
variants it read, the statechart outcome it reached, and every
|
|
384
|
+
variants it read, the statechart outcome it reached, and every screen finding the layer's refusals
|
|
382
385
|
exposed.
|
|
@@ -51,12 +51,12 @@ The package already refuses these, so assert none of them again:
|
|
|
51
51
|
|
|
52
52
|
Declare the state names once in the journey file, and build the portfolio from that declaration.
|
|
53
53
|
|
|
54
|
-
- Name a state for its
|
|
54
|
+
- Name a state for its screen and its condition — `answer-partial`, `start-storage-failure`,
|
|
55
55
|
`case-delete-confirmation`.
|
|
56
56
|
- Register the states the design work actually needs, and place every registered one. Never leave a
|
|
57
57
|
registered state unplaced.
|
|
58
58
|
- Place a capture state from inside the journey that reaches it, immediately after the assertion
|
|
59
|
-
that proves the
|
|
59
|
+
that proves the screen is in the condition that state names.
|
|
60
60
|
- Record each placed name in the suite's own set in the same step that calls `place`. That set, not
|
|
61
61
|
`placements`, is what the always-on placement proof reads.
|
|
62
62
|
|
|
@@ -117,7 +117,7 @@ after the click returns.
|
|
|
117
117
|
- Take each frame after the paint settles. Reach for `waitForAnimations` on the element whose
|
|
118
118
|
transition was running ([layer.md](layer.md) → The waits); a frame shot mid-transition pictures a
|
|
119
119
|
state the interface never rests in.
|
|
120
|
-
- Regenerate the whole matrix from the journeys after any
|
|
120
|
+
- Regenerate the whole matrix from the journeys after any screen change. Never judge a round
|
|
121
121
|
against a portfolio that is part old and part new.
|
|
122
122
|
- Route review of the portfolio to the `orkestrel-polish` campaign, which owns preflight,
|
|
123
123
|
verdicts, and reconciliation. This reference owns only how the journeys generate it.
|
|
@@ -53,8 +53,8 @@ so a decision cites the exact file and reads it in one call.
|
|
|
53
53
|
|
|
54
54
|
Compose each file from what the run already holds:
|
|
55
55
|
|
|
56
|
-
- `describeTree` of the mounted
|
|
57
|
-
- `describeFocus` of the mounted
|
|
56
|
+
- `describeTree` of the mounted screen, for the roles, names, and states a person meets.
|
|
57
|
+
- `describeFocus` of the mounted screen, for the focus order the keyboard walk took.
|
|
58
58
|
- The resolved-style rows the matrix family read for that variant — the property, the element, and
|
|
59
59
|
the value the browser returned.
|
|
60
60
|
- The journal's `steps` and `output`, for what the run did and what the page said while it did it.
|
|
@@ -66,7 +66,7 @@ Rules the artifact obeys:
|
|
|
66
66
|
combination nobody can reproduce.
|
|
67
67
|
- Write one file per variant, never one file per journey. A decision is taken per variant, and a
|
|
68
68
|
reader opening one file per journey pays a round trip per journey.
|
|
69
|
-
- Regenerate the whole set after any
|
|
69
|
+
- Regenerate the whole set after any screen change. Never judge a round against a set that is part
|
|
70
70
|
old and part new.
|
|
71
71
|
- Keep it out of version control.
|
|
72
72
|
|
|
@@ -185,13 +185,13 @@ the capture voices in [captures.md](captures.md).
|
|
|
185
185
|
|
|
186
186
|
- Report an absent control and a present-but-unreachable one as different findings: absence names
|
|
187
187
|
a missing control, and unreachability names the interface gating one that exists.
|
|
188
|
-
- Report ambiguity as a finding about the
|
|
188
|
+
- Report ambiguity as a finding about the screen. Quote the match count from the message, and
|
|
189
189
|
re-target the journey by role or region.
|
|
190
190
|
- Assert a `DOMException` on its `name` as well as its message. A withheld storage operation raises
|
|
191
191
|
`SecurityError` and a write past the quota raises `QuotaExceededError`, which is the pair a denied
|
|
192
192
|
or full origin raises.
|
|
193
193
|
- Never assert a `could not be resolved` sentence. Each one is narrowing the resolver needs under
|
|
194
|
-
`noUncheckedIndexedAccess`, and no
|
|
194
|
+
`noUncheckedIndexedAccess`, and no screen reaches it.
|
|
195
195
|
- Read a refused bound as a test defect rather than a finding. The wait family validates its budget
|
|
196
196
|
and its interval and names the argument the caller passed, under the subject `Wait` or
|
|
197
197
|
`Animation`.
|
|
@@ -211,7 +211,7 @@ the capture voices in [captures.md](captures.md).
|
|
|
211
211
|
`fillAccessible` where the text is only a payload the person pastes.
|
|
212
212
|
- Bring focus about first — through `traverseAccessible`, `clickAccessible`, or `typeAccessible` —
|
|
213
213
|
and then press. The refusal is what `pressKeys` adds over the provider's own keyboard call: a key
|
|
214
|
-
sent to the body reaches no control, and every assertion after it reads a
|
|
214
|
+
sent to the body reaches no control, and every assertion after it reads a screen the key never
|
|
215
215
|
touched.
|
|
216
216
|
- Escape the sequence yourself where the sequence is the subject. Reach for `typeAccessible` where
|
|
217
217
|
the text is the subject and the key syntax is in the way.
|
|
@@ -245,7 +245,7 @@ the capture voices in [captures.md](captures.md).
|
|
|
245
245
|
- Pass `absent: true` to `waitForState` where the claim is that the state went away, and read the
|
|
246
246
|
returned states rather than taking a second reading afterwards.
|
|
247
247
|
- Take `waitForState` as the published replacement for a settle keyed to a framework's own class
|
|
248
|
-
names. Where the control announces nothing, the finding is the
|
|
248
|
+
names. Where the control announces nothing, the finding is the screen's: give it `aria-expanded`,
|
|
249
249
|
`aria-pressed`, or `aria-busy` rather than reading the classes a stylesheet happens to use.
|
|
250
250
|
- Take every style, contrast, and capture reading after `waitForAnimations` on the element whose
|
|
251
251
|
paint was moving. A reading taken mid-transition reports an interpolated frame no state of the
|
|
@@ -274,7 +274,7 @@ the capture voices in [captures.md](captures.md).
|
|
|
274
274
|
locators do not, because the platform exposes a native disclosure rather than a role.
|
|
275
275
|
- Drive an ARIA disclosure as what it is: a button. Click it with `clickAccessible`, then settle it
|
|
276
276
|
with `waitForState` on the state it announces.
|
|
277
|
-
- Report a control that opens a panel and announces no state as a
|
|
277
|
+
- Report a control that opens a panel and announces no state as a screen finding. Author
|
|
278
278
|
`aria-expanded` on it rather than settling on the classes its framework toggles.
|
|
279
279
|
- Read a native summary's expansion with `readStates`, which reads the parent `details` element's
|
|
280
280
|
own `open` where the summary declares no `aria-expanded`.
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
`assert`. Each phase receives the context and the part of the transition it owns.
|
|
37
37
|
- Put the scenarios and the table in the workspace's browser test setup module, and import them from
|
|
38
38
|
there.
|
|
39
|
-
- Declare a transition for every event the
|
|
39
|
+
- Declare a transition for every event the screen accepts in every state it accepts it, including
|
|
40
40
|
the event that leaves the state unchanged. A table that lists only the moves the happy path takes
|
|
41
41
|
proves the happy path.
|
|
42
42
|
- Write the phases as module functions the whole table shares. Each phase reads its subject from its
|
|
@@ -184,7 +184,7 @@ it('walks the disclosure statechart', async () => {
|
|
|
184
184
|
- Drive `act` through the journey verbs — `clickAccessible`, `clickDisclosure`, `typeAccessible`,
|
|
185
185
|
`traverseAccessible`, `pressKeys` — for every transition a person can cause.
|
|
186
186
|
- Drive `act` through the entity's own API only where the transition is the entity's rather than the
|
|
187
|
-
person's: a lifecycle event, a transport reply, a timer the
|
|
187
|
+
person's: a lifecycle event, a transport reply, a timer the screen owns.
|
|
188
188
|
- Say which door each row used, in the row's `name`. A table that mixes the doors silently reads as
|
|
189
189
|
a set of user transitions and proves something else.
|
|
190
190
|
|
|
@@ -286,7 +286,7 @@ import it.
|
|
|
286
286
|
|
|
287
287
|
- Name only the attribute contract such a page must honour: the names in `STATECHART_ATTRIBUTES`,
|
|
288
288
|
the readings in `STATECHART_STATUSES`, and the tally on one root node.
|
|
289
|
-
- Report the page itself as the repository owner's decision — which transitions a
|
|
289
|
+
- Report the page itself as the repository owner's decision — which transitions a screen owes,
|
|
290
290
|
where the page is linked, and whether it ships at all.
|
|
291
291
|
- Route a person who must watch the widget move to the harness run's own frames and its written
|
|
292
292
|
artifact ([decide.md](decide.md) → The harness run), and name a deep link only where the workspace
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Proving what the browser resolved
|
|
2
2
|
|
|
3
|
-
Prove a style from what the browser resolved on the mounted
|
|
3
|
+
Prove a style from what the browser resolved on the mounted screen. The `enterprise-bootstrap`
|
|
4
4
|
skill's [instruments reference](../../enterprise-bootstrap/references/inspection.md) names each
|
|
5
5
|
instrument's property, its population, and its coverage. Take those from there, the reading from
|
|
6
6
|
here, and the control from the builder this layer publishes for it.
|
|
@@ -75,12 +75,12 @@ readings follow.
|
|
|
75
75
|
reads the whole matrix, where the capture family renders one variant per run.
|
|
76
76
|
- Compose each variant's `apply` in the test, and apply it with the variant's `width` and `height`
|
|
77
77
|
before the readings. Take every reading for that variant before moving to the next.
|
|
78
|
-
- Name the attribute the
|
|
78
|
+
- Name the attribute the screen actually reads in `apply`; a Bootstrap screen switches on
|
|
79
79
|
`data-bs-theme`. An `apply` that sets another attribute leaves the run in the default theme, where
|
|
80
80
|
every reading passes.
|
|
81
|
-
- Reach for the application's own theme control where the
|
|
81
|
+
- Reach for the application's own theme control where the screen ships one, and assert the state it
|
|
82
82
|
announces. Setting the attribute directly proves the stylesheet; driving the control proves the
|
|
83
|
-
|
|
83
|
+
screen.
|
|
84
84
|
- Assert that the run read every declared variant. A matrix that silently walked one variant reports
|
|
85
85
|
a pass for the theme nobody exercised.
|
|
86
86
|
- Report which variants a result covers beside it. A pairing that appears only in a state the run
|
|
@@ -102,7 +102,7 @@ provided.
|
|
|
102
102
|
`pressKeys`, or a real click. Pass `worn` where the chrome is painted onto a second element such
|
|
103
103
|
as a label. It reports `undefined` for a control not matching `:focus-visible`, for the browser's
|
|
104
104
|
own automatic ring, and for a focus style that only repaints the fill — treat each as a finding
|
|
105
|
-
about the
|
|
105
|
+
about the screen rather than as a pass.
|
|
106
106
|
- Reach for `measureContrast`, `measureLuminance`, `blendColor`, `readLayers`, and `readBackdrop`
|
|
107
107
|
only where the composite itself is the subject. Never re-derive `readContrast` from them.
|
|
108
108
|
|
|
@@ -117,7 +117,7 @@ Take each reading's control from the builder this layer publishes for it.
|
|
|
117
117
|
| `extractStyles` | `buildEscapes(permitted)` | An inline declaration, an embedded `<style>` element, and the sheet the id exempts |
|
|
118
118
|
| `readCensus` | `buildCensus()` | An HTML token and an SVG token, neither declared by any loaded stylesheet |
|
|
119
119
|
|
|
120
|
-
- Append each control's `root` to the same
|
|
120
|
+
- Append each control's `root` to the same screen root the reading walks, take the reading, and
|
|
121
121
|
remove it afterwards. Every builder returns detached nodes and mounts nothing, so where the
|
|
122
122
|
control is read is the caller's decision.
|
|
123
123
|
- Assert on the fields the builder returns rather than on a token or a selector written down in the
|
|
@@ -134,12 +134,12 @@ Take each reading's control from the builder this layer publishes for it.
|
|
|
134
134
|
Take the property, the population, and the coverage from the instruments reference → Authored class
|
|
135
135
|
in the shipped cascade. This is the reading.
|
|
136
136
|
|
|
137
|
-
- Read the census with `readCensus(root)`, which walks the mounted
|
|
137
|
+
- Read the census with `readCensus(root)`, which walks the mounted screen, reports `elements` as
|
|
138
138
|
the population it read, lists every `tokens` value the markup carries, and lists as `undeclared`
|
|
139
139
|
the tokens no loaded stylesheet declares. It refuses a walk that read no element.
|
|
140
140
|
- Assert on `elements` as well as on `undeclared`. An empty walk reports no undeclared token, and so
|
|
141
141
|
does markup whose every class the cascade declares.
|
|
142
|
-
- Take `root` from the mounted
|
|
142
|
+
- Take `root` from the mounted screen, so the census covers what rendered rather than what a
|
|
143
143
|
template file spells.
|
|
144
144
|
- Read `readClasses` and `readCascade` directly only where one side of the difference is the
|
|
145
145
|
subject. `readCensus` is the reading, and re-deriving it drops the population it reports.
|
|
@@ -152,7 +152,7 @@ Take the property, the population, the named exemptions, and the coverage from t
|
|
|
152
152
|
reference → Style escapes. This is the reading.
|
|
153
153
|
|
|
154
154
|
- Read escapes with `extractStyles(root)`, which returns the markup of every hit it found.
|
|
155
|
-
- Take the reading before any journey drives the
|
|
155
|
+
- Take the reading before any journey drives the screen, because the population is the undriven
|
|
156
156
|
tree.
|
|
157
157
|
- Append `buildEscapes(permitted).root` to that same `root`, so the control reaches the reading
|
|
158
158
|
through `extractStyles`.
|
|
@@ -15,13 +15,25 @@ paths:
|
|
|
15
15
|
are first-class.
|
|
16
16
|
- The CLI names the independent selections `--src` and `--app`.
|
|
17
17
|
`--surfaces` is not an alias and must fail as an unknown option.
|
|
18
|
+
- `new` selects the styles surface with `--styles`, its themes target with `--themes` (requires
|
|
19
|
+
`--styles`), the showcase with `--showcase` (requires `--app browser`), and extensions with
|
|
20
|
+
`--extend <surface:name,…>`. Each `--extend` entry is `browser:vue`, applied to every selected
|
|
21
|
+
browser axis, or `styles:<name>`, which requires `--styles`. `--app browser` implies the journey.
|
|
18
22
|
- Core-only, browser-only, and server-only applications are valid. A combined
|
|
19
23
|
browser+server application includes app/core for shared contracts.
|
|
20
24
|
- Every selected environment has an `index.ts` barrel. `main.ts` is an executable
|
|
21
25
|
entry and never owns reusable declarations.
|
|
22
26
|
- app/core is host-independent and check/test-only.
|
|
23
|
-
- app/browser uses app/core contracts,
|
|
24
|
-
entry, `
|
|
27
|
+
- app/browser is framework-independent: it uses app/core contracts, an
|
|
28
|
+
`index.html` entry, a `main.ts` that renders through the DOM, `check:app:browser`
|
|
29
|
+
through `tsc`, and real Chromium tests.
|
|
30
|
+
- The Vue application lives in app/vue: `main.ts`, `index.html`, `App.vue`,
|
|
31
|
+
`check:app:vue` through `vue-tsc`, `dev:vue`, and real Chromium tests.
|
|
32
|
+
- When a target's app/browser holds a `.vue` file, move it to app/vue. The
|
|
33
|
+
generator's `repair` raises a blocking question naming that move until it lands.
|
|
34
|
+
- The showcase builds one page per application, app/browser and each app-side
|
|
35
|
+
browser extension, into root `showcase/` with the final-page stamp;
|
|
36
|
+
`.claude/rules/workspace.md` § Build outputs owns its build.
|
|
25
37
|
- app/server uses app/core contracts, parses environment values before binding,
|
|
26
38
|
defaults to loopback, emits `dist/app/server/main.cjs`, and keeps only
|
|
27
39
|
`node:*` external.
|
|
@@ -36,8 +48,9 @@ paths:
|
|
|
36
48
|
generated-consumer tests exercise those real configurations.
|
|
37
49
|
- Generated consumers must pass lint, scoped typechecking, production builds, and
|
|
38
50
|
real integration tests.
|
|
39
|
-
-
|
|
40
|
-
|
|
51
|
+
- Include `.ts`, `.tsx`, `.mts`, and `.cts` in scoped checks. Publish only TypeScript from
|
|
52
|
+
`src/vue`, and put Vue SFCs in `app/vue`. Put CSS in browser code; compile SCSS through the
|
|
53
|
+
`sass` dependency the styles surface declares.
|
|
41
54
|
- Published `src` environments never import private `app` modules. Src core is
|
|
42
55
|
host-independent; src browser/server may import src core but never one
|
|
43
56
|
another's implementation. Apply the same environment law to
|
|
@@ -45,8 +58,9 @@ paths:
|
|
|
45
58
|
is its core API.
|
|
46
59
|
- App-only manifests are unscoped and `private: true`, with no `main`,
|
|
47
60
|
`module`, `types`, export map, or publish configuration. Mixed manifests
|
|
48
|
-
publish only `dist/src
|
|
49
|
-
|
|
61
|
+
publish only `dist/src` and the SCSS sources a stylesheet export names, and
|
|
62
|
+
never app output; `vue` stays a development dependency apart from the optional
|
|
63
|
+
peer `.claude/rules/browser.md` admits.
|
|
50
64
|
- Give app/server process signals to a tested, explicitly stoppable,
|
|
51
65
|
generation-safe runner whose stale failures cannot release a newer run.
|
|
52
66
|
- Return the runner from convenience startup, so normal cleanup cannot be hidden.
|
|
@@ -49,7 +49,7 @@ Use only the centralized files an environment needs.
|
|
|
49
49
|
- Extract local declarations by kind. “Only used here” and “not exported” are not exemptions.
|
|
50
50
|
- Every declaration in a centralized file is exported. Fold away a trivial single-use declaration or export/test it; never leave it hidden.
|
|
51
51
|
- The only permitted non-exported module-scope declarations are in a runtime entrypoint that must be self-contained and cannot import siblings, such as raw source loaded in a worker. Explain that necessity in a comment.
|
|
52
|
-
-
|
|
52
|
+
- Treat `src/bin/main.ts`, `app/browser/main.ts`, `app/vue/main.ts`, and `app/server/main.ts` as fixed runtime entries. Declare no module-scope constant or function in them; import what they need and run. Apply the preceding self-contained exception only to an entrypoint that cannot import siblings.
|
|
53
53
|
- Perform a cleanup pass after implementation: no stray implementation-file declarations, non-exported/wrong-kind centralized declarations, prohibited nested declarations, duplicate implementations, compatibility aliases, superfluous wrappers, stale imports/barrel rows, or untested extracted functions.
|
|
54
54
|
|
|
55
55
|
## Kind purity
|
|
@@ -166,7 +166,7 @@ A wrapper survives only when it adds a real boundary, invariant, composition, tr
|
|
|
166
166
|
|
|
167
167
|
- Never declare or assign a function inside another function or method.
|
|
168
168
|
- This bans local `function`, `function*`, and `const fn = () => ...`, regardless of caller count.
|
|
169
|
-
-
|
|
169
|
+
- Admit a named or anonymous function expression passed as a call or constructor argument, returned as a result, or used as an arrow body, through parentheses, non-computed object-literal property values, and array elements. Admit methods, getters, and setters of such an object literal. Refuse a climb out of a method or accessor body, or through a spread, computed key, class field, assignment, or local binding.
|
|
170
170
|
- Instance-bound work that reaches state or sibling methods is a method, not a free function.
|
|
171
171
|
|
|
172
172
|
Separate these roles:
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
---
|
|
2
2
|
paths:
|
|
3
3
|
- 'src/browser/**/*.ts'
|
|
4
|
+
- 'src/vue/**/*.ts'
|
|
4
5
|
- 'app/browser/**/*.{ts,vue}'
|
|
6
|
+
- 'app/vue/**/*.{ts,vue}'
|
|
5
7
|
- 'tests/{src,app}/browser/**/*'
|
|
8
|
+
- 'tests/{src,app}/vue/**/*'
|
|
6
9
|
- 'tests/setupBrowser.ts'
|
|
7
10
|
---
|
|
8
11
|
|
|
9
12
|
# Vue and browser rules
|
|
10
13
|
|
|
14
|
+
- Publish only TypeScript composables and contracts from `src/vue`; put Vue single-file components
|
|
15
|
+
in `app/vue`. Keep `.vue` files and `vue` imports out of `src/browser` and `app/browser`.
|
|
16
|
+
Apply `AGENTS.md` § Project model to imports from each Vue face.
|
|
17
|
+
- Declare `vue` in the manifest as an optional peer only for the `./vue` export. Until that
|
|
18
|
+
declaration exists, the `./vue` build refuses `vue`, `vue/*`, and `@vue/*`; after it, the build
|
|
19
|
+
externalizes `vue` and its subpaths and still refuses `@vue/*`.
|
|
11
20
|
- Never use Vue `$emit`.
|
|
12
21
|
- Coordinate reactivity through props, controllers, stores, services, and composables.
|
|
13
22
|
- A composable exposes readonly refs plus methods; consumers never mutate returned refs directly:
|
|
@@ -23,7 +23,8 @@ Documentation is an enforced contract, not explanatory decoration. The Writing r
|
|
|
23
23
|
- `AGENTS.md` and its linked rules are the sole convention source. Do not create competing instruction copies in guides.
|
|
24
24
|
- `guides/README.md` is the map: maintain both a concept index and a directory index. The concept index runs `spec ↔ source ↔ tests ↔ showcase` minus every column whose subject this workspace lacks, so an app-only workspace that publishes no library and builds no showcase still owes a full index over the columns it has.
|
|
25
25
|
- Where the repository keeps one, `ROADMAP.md` is the sequenced plan of record. Each chunk reaches green before the next.
|
|
26
|
-
-
|
|
26
|
+
- Demonstrate public API in application source, prove its journeys there, and rebuild the selected showcase pages for publication.
|
|
27
|
+
- Name each `showcase/<application>.html` page that demonstrates the row in the concept index's showcase column; name `browser.html` for the base modes.
|
|
27
28
|
- An integration surface's guide documents the validated hookup for each supported client: the exact commands run, the authentication and approval model that client needs, and the honest limit wherever a client cannot reach part of the surface.
|
|
28
29
|
|
|
29
30
|
## Parity
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
paths:
|
|
3
3
|
- '**/*.{scss,css}'
|
|
4
4
|
- 'src/styles/**/*'
|
|
5
|
+
- 'src/*/sheet.ts'
|
|
5
6
|
- 'tests/setupStyles.ts'
|
|
7
|
+
- 'tests/setupStyles.test.ts'
|
|
6
8
|
- 'tests/setupBrowser.ts'
|
|
7
9
|
---
|
|
8
10
|
|
|
@@ -12,19 +14,24 @@ SCSS mirrors TypeScript centralization. Concrete token prefixes are project-spec
|
|
|
12
14
|
|
|
13
15
|
## Centralized files
|
|
14
16
|
|
|
15
|
-
| File
|
|
16
|
-
|
|
|
17
|
-
| `_mixins.scss`
|
|
18
|
-
| `_tokens.scss`
|
|
19
|
-
| `_theme.scss`
|
|
20
|
-
| `
|
|
17
|
+
| File | Sole responsibility |
|
|
18
|
+
| ------------------- | ------------------------------------------------------------- |
|
|
19
|
+
| `_mixins.scss` | `@function` values and `@mixin` declaration emitters |
|
|
20
|
+
| `_tokens.scss` | `:root` public custom-property tokens and cascade-layer order |
|
|
21
|
+
| `_theme.scss` | Token overrides under theme selectors |
|
|
22
|
+
| `_reset.scss` | The face's reset declarations, when that face owns a reset |
|
|
23
|
+
| `themes/index.scss` | Barrel of named theme packs, compiled into its own sheet |
|
|
24
|
+
| `index.scss` | Sole compilation barrel |
|
|
21
25
|
|
|
26
|
+
- Apply this table and the folder barrels in § Folders to every sheet face: `src/styles` and each
|
|
27
|
+
`src/<name>` styles extension. `.claude/rules/workspace.md` § Environments fixes the `sheet.ts`
|
|
28
|
+
entry.
|
|
22
29
|
- `_mixins.scss` emits no top-level CSS.
|
|
23
30
|
- Consumers load it with `@use '../mixins' as *`.
|
|
24
31
|
- Never load `mixins` from `index.scss`.
|
|
25
|
-
- `index.scss` is the sole compilation barrel; it loads `tokens`, `theme
|
|
32
|
+
- `index.scss` is the sole compilation barrel of its sheet; it loads `tokens`, `theme` where `_theme.scss` exists, and the output partials with `@use`. Never load `themes/` from `index.scss`.
|
|
26
33
|
- `_tokens.scss` is the token source of truth. Adding a token is allowed; rename/removal is breaking.
|
|
27
|
-
- `_theme.scss` only
|
|
34
|
+
- `_theme.scss` and each `themes/` pack only retune tokens under theme selectors such as `[data-theme='…']`.
|
|
28
35
|
- Component partials never override global tokens.
|
|
29
36
|
|
|
30
37
|
## Sass mechanisms
|
|
@@ -39,8 +46,11 @@ SCSS mirrors TypeScript centralization. Concrete token prefixes are project-spec
|
|
|
39
46
|
- Check `_tokens.scss` before inventing a token.
|
|
40
47
|
- Put global tokens in `_tokens.scss`; put truly component-scoped custom properties on the component selector.
|
|
41
48
|
- Never bury tokens in unrelated partials.
|
|
42
|
-
- Never use literal colors. Use `var(--token)` or `color-mix()` over
|
|
43
|
-
literal color may appear in is `_tokens.scss`, where the token itself is
|
|
49
|
+
- Never use literal colors outside a pinned recreation. Use `var(--token)` or `color-mix()` over
|
|
50
|
+
tokens. The one file a literal color may appear in is `_tokens.scss`, where the token itself is
|
|
51
|
+
declared. A face whose contract is the exact recreation of a pinned external artifact keeps
|
|
52
|
+
the literals, declarations, and order the pin declares, and records tokenization and
|
|
53
|
+
accessibility additions in its separate authored face.
|
|
44
54
|
- Never repeat per-color/per-variant blocks; drive shared structure with one `@each` over a shared list.
|
|
45
55
|
- If a pattern appears in at least two partials, move it to `_mixins.scss`.
|
|
46
56
|
- Treat a declaration block two partials share because each records an external value as a
|
|
@@ -50,8 +60,21 @@ SCSS mirrors TypeScript centralization. Concrete token prefixes are project-spec
|
|
|
50
60
|
- Never `@extend` across partials; share through tokens/mixins.
|
|
51
61
|
- Never declare a `transition:` without `prefers-reduced-motion: reduce`. Use the project transition mixin, which emits both.
|
|
52
62
|
- Animations include `@include reduced-motion { animation: none }`.
|
|
53
|
-
- Never wrap rules in a foreign cascade layer. Each partial uses its folder's own layer.
|
|
54
|
-
|
|
63
|
+
- Never wrap rules in a foreign cascade layer. Each partial uses its folder's own layer. A sheet
|
|
64
|
+
that recreates an external framework instead writes every normal declaration into one layer named
|
|
65
|
+
for that framework and every `!important` declaration outside every layer.
|
|
66
|
+
- Declare cascade-layer order once in the consumer entry before `@import 'tailwindcss'`, so utilities win predictably. When a package publishes several sheets, open every published sheet with the same full order statement, so the order holds whichever sheet loads first.
|
|
67
|
+
- Open `themes/index.scss` with `@use '../tokens'`, whose first emitted rule is the order statement, then `@use 'default'`; Sass refuses a `@use` after another rule, so never write the statement there literally.
|
|
68
|
+
|
|
69
|
+
## Folders
|
|
70
|
+
|
|
71
|
+
- Give each folder an `_index.scss` barrel that loads its partials with `@use`; keep the barrel when the folder is empty.
|
|
72
|
+
- Put a rule that styles one element in `elements/`, a class skin that applies with no script running in `components/`, and a class that sets one property in `utilities/`.
|
|
73
|
+
- Add another folder only for a job `elements/`, `components/`, and `utilities/` do not hold, and give it its own barrel and its own layer.
|
|
74
|
+
|
|
75
|
+
## Proofs
|
|
76
|
+
|
|
77
|
+
- Declare every CSSOM instrument a sheet proof reads in `tests/setupStyles.ts`, never in a test file. `tests/setupStyles.test.ts` proves each instrument under the root setup mirror in `.claude/rules/tests.md`.
|
|
55
78
|
|
|
56
79
|
## Naming
|
|
57
80
|
|