@reqvire-org/ads 0.3.1 → 0.3.3

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 (35) hide show
  1. package/dist/bootstrap-ds/ADS.md +29 -0
  2. package/dist/bootstrap-ds/design-system/tokens/base.css +4 -1
  3. package/dist/bootstrap-ds/design-system/tokens/colors.css +2 -2
  4. package/dist/boundaries.mjs +21 -23
  5. package/dist/check.mjs +45 -15
  6. package/dist/cli.mjs +1 -0
  7. package/dist/contracts/foundation.json +211 -0
  8. package/dist/foundation.mjs +114 -0
  9. package/dist/mcp/build-status.mjs +22 -9
  10. package/dist/mcp/mutations.mjs +1 -1
  11. package/dist/mcp/preview-watch.mjs +35 -12
  12. package/dist/mcp/prompts.mjs +1 -1
  13. package/dist/mcp/server.mjs +6 -6
  14. package/dist/oxlint.mjs +1 -1
  15. package/dist/scenarios-compiler.mjs +86 -43
  16. package/dist/skills/ads-assimilate/SKILL.md +43 -2
  17. package/dist/skills/ads-assimilate/references/cli.md +3 -2
  18. package/dist/skills/ads-assimilate/references/native-implementation.md +16 -0
  19. package/dist/skills/ads-break-ui/SKILL.md +43 -2
  20. package/dist/skills/ads-designer/SKILL.md +43 -2
  21. package/dist/skills/ads-designer/assets/bootstrap-ds/ADS.md +29 -0
  22. package/dist/skills/ads-designer/assets/bootstrap-ds/design-system/tokens/base.css +4 -1
  23. package/dist/skills/ads-designer/assets/bootstrap-ds/design-system/tokens/colors.css +2 -2
  24. package/dist/skills/ads-designer/references/cli.md +3 -2
  25. package/dist/skills/ads-import/SKILL.md +43 -2
  26. package/dist/skills/ads-import/references/cli.md +3 -2
  27. package/dist/skills/ads-motion/SKILL.md +43 -2
  28. package/dist/style-boundaries.mjs +59 -0
  29. package/dist/units.mjs +37 -1
  30. package/docs/ADS.md +34 -1
  31. package/docs/ARCHITECTURE.md +38 -6
  32. package/docs/CHANGELOG.md +28 -0
  33. package/docs/GUIDE.md +18 -1
  34. package/docs/MCP.md +48 -7
  35. package/package.json +2 -1
@@ -55,7 +55,7 @@ If no published distribution exists, build and pack the tooling checkout with
55
55
  From inside an existing test project:
56
56
 
57
57
  ```sh
58
- ads_tool_package="/absolute/path/to/reqvire-org-ads-0.3.1.tgz"
58
+ ads_tool_package="/absolute/path/to/reqvire-org-ads-0.3.2.tgz"
59
59
  npx --yes --package "$ads_tool_package" ads --help
60
60
  npx --yes --package "$ads_tool_package" ads init --framework react --tool-package "$ads_tool_package"
61
61
  npm install
@@ -93,7 +93,8 @@ component rules through the project's plugin adapter and `ds.config.json`, then
93
93
  run its actual build. Use `lint-config` directly for custom pipeline integration
94
94
  or debugging, not as a separate routine setup step.
95
95
 
96
- Use ADS 0.3.1 or later for the shared-resource preview compiler. `mcp` starts stdio
96
+ Use ADS 0.3.2 or later for the closed foundation contract and recoverable
97
+ shared-resource preview compiler. `mcp` starts stdio
97
98
  plus its local website; `showcase` starts the browser-only MCP runtime. Open the existing runtime URL from `ads_status` when
98
99
  connected; otherwise use `npm run dev:showcase` or the installed `ads showcase`.
99
100
  The tool client discovers entries and renders MCP Apps. Init adds the launch
@@ -55,6 +55,22 @@ target framework. With the default root ADS layout:
55
55
  dimensions, positions, or transforms. Document and constrain the exception in
56
56
  the shared architecture guard; never broaden it to arbitrary visual styles.
57
57
 
58
+ Preserve the reference's required capabilities while assigning each concern to
59
+ its owner. Demo utilities for container size, border or framing become a public
60
+ review/layout pattern around the real component. Add component props for a
61
+ deliberate runtime API requirement; missing demo layout alone is not justification.
62
+ For imperative geometry, register exact `styleWrites` properties. After migrating
63
+ from JSX inline geometry, change source and its own config companion together:
64
+ remove the obsolete `inlineStyleFiles` exception and register only properties the
65
+ implementation actually writes. Verify the final component with that boundary.
66
+
67
+ Keep presentational building blocks independent of an unnecessary form controller
68
+ or other state engine. Scenario adapters may own demo state and validation. When
69
+ a reusable controller is part of the intended product API, define and test its
70
+ actual contract; a similar method shape alone does not establish compatibility
71
+ with the reference library. Keep state updaters pure and verify submit, focus,
72
+ validation, reset and asynchronous behavior that the API promises.
73
+
58
74
  Use current repository conventions in another target rather than assuming the
59
75
  same framework, directory structure, or lint tooling.
60
76
 
@@ -30,7 +30,11 @@ unrelated statements. Its real re-export may accompany removal of a redundant
30
30
  `export {};` placeholder; do not add empty exports. A `tokens` manifest lists the foundation
31
31
  entry in `companionFiles`: when creating/deleting a canonical token file such as
32
32
  `fonts.css`, include only its required import change there in the same patch.
33
- Preserve other imports/comments and the canonical order. Use `dryRun: true` to
33
+ Preserve other imports/comments and the canonical order. Runtime owner manifests
34
+ also list a `ds.config.json` companion: change only that owner's `inlineStyleFiles`
35
+ and `boundaries.styleWrites` entries together with its source. Unused permissions
36
+ fail the core checks; do not pre-register a boundary without its implementation.
37
+ Use `dryRun: true` to
34
38
  inspect validation; applying re-runs the gates. Fix rejected patches in their
35
39
  owner. Report unsupported coupled changes instead of bypassing a rejection with
36
40
  direct source writes. Shared styles/helpers use `foundation`, root guides use
@@ -193,6 +197,15 @@ source definitions and values; inspecting definitions does not prove UI parity.
193
197
  The tool keeps the shared naming contract; sample templates and their token
194
198
  lists belong to the project.
195
199
 
200
+ A public review pattern may be covered by real token or composition scenarios;
201
+ it needs no redundant standalone entry. Remove an optional scenario only while
202
+ retaining reachable render coverage. Keep directory ownership anchored by its
203
+ runtime entry (`index` or a file matching the directory name) or colocated fixture.
204
+ Import colocated image/font fixtures so MCP serves them through asset resources.
205
+ For a broken-image case, use a local failing fixture, rather than an external
206
+ network failure or a domain-specific CSP exception. Production asset APIs remain
207
+ unrestricted by this fixture guidance.
208
+
196
209
  Scenarios, colocated fixtures and helpers are development-only. Never export them
197
210
  from the runtime barrel or import them in production, including through aliases,
198
211
  re-exports or dynamic imports. They may not import application stores, routers,
@@ -203,6 +216,27 @@ Use local simulated services in fixtures. Tests may import fixtures; load real
203
216
  foundation CSS rather than assigning shared token values in JavaScript. Keep
204
217
  full-app integration harnesses and workflow coverage in application test support.
205
218
 
219
+ Foundation CSS is checked, not an escape hatch. Value sheets define shared tokens
220
+ on the document root/themes; `fonts.css` holds font-face metadata. `base.css` has
221
+ three closed roles: root/body typography, canvas and theme defaults; limited native
222
+ normalization; and global focus indicators/reduced-motion rules. Every selector
223
+ branch, property and value must fit its role. Token-backed control skins, layout,
224
+ interactive state rules and owner hooks are rejected even through `:where()`.
225
+ Do not copy an unchecked upstream preflight or turn a component repair into a reset.
226
+ Keep behavior and skin in their runtime owner. Put demo-only sizing and framing
227
+ in a public review/layout pattern; add component props for a deliberate runtime
228
+ API need. Preserve required capabilities while following ownership rules.
229
+ Browser review still verifies the effects.
230
+
231
+ Invalid metadata and source failures keep the managed compiler alive for repairs.
232
+ Read `ads_status.build` or `ads://project/build`, fix the owner, then wait for current
233
+ successful output. If the builder exited, fix its command and invoke the preview
234
+ again; ADS starts one fresh child without an MCP reconnect. Compiler-code changes
235
+ also reload on the next preview invocation. Reconnect the client when upgrading
236
+ the ADS server package itself. Persistent SSE/notification streams make
237
+ `networkidle` unsuitable: wait for the App's current ready/error state instead.
238
+
239
+
206
240
 
207
241
 
208
242
  Register component/project Oxlint plugins and rules in `ds.config.json`. Use
@@ -210,7 +244,14 @@ scoped `oxlint.overrides` for justified file-specific settings; never edit
210
244
  `.ds/oxlintrc.json`. These overrides configure Oxlint, not the independent
211
245
  architecture checks in `ads check`. Measured renderer properties and documented
212
246
  non-state hooks use explicit `boundaries`; shared token writes and primitive
213
- state overrides cannot be allowlisted. Add generic architectural protections to
247
+ state overrides cannot be allowlisted. When migrating JSX inline geometry to
248
+ imperative writes, port the source and replace its own boundary entries together
249
+ in the owner's patch. Remove obsolete `inlineStyleFiles` entries and unused
250
+ `styleWrites` properties; do not broaden permissions to keep a migration passing.
251
+ Pointer-capture advisories call for review of cancellation, capture loss and
252
+ teardown, including cleanup delegated to hooks. Verify actual cleanup in a browser;
253
+ a locally present handler alone does not prove correctness.
254
+ Add generic architectural protections to
214
255
  ADS core and keep domain/component-specific rules registered in project setup.
215
256
 
216
257
  Read [references/verification.md](references/verification.md) for viewport,
@@ -62,7 +62,11 @@ unrelated statements. Its real re-export may accompany removal of a redundant
62
62
  `export {};` placeholder; do not add empty exports. A `tokens` manifest lists the foundation
63
63
  entry in `companionFiles`: when creating/deleting a canonical token file such as
64
64
  `fonts.css`, include only its required import change there in the same patch.
65
- Preserve other imports/comments and the canonical order. Use `dryRun: true` to
65
+ Preserve other imports/comments and the canonical order. Runtime owner manifests
66
+ also list a `ds.config.json` companion: change only that owner's `inlineStyleFiles`
67
+ and `boundaries.styleWrites` entries together with its source. Unused permissions
68
+ fail the core checks; do not pre-register a boundary without its implementation.
69
+ Use `dryRun: true` to
66
70
  inspect validation; applying re-runs the gates. Fix rejected patches in their
67
71
  owner. Report unsupported coupled changes instead of bypassing a rejection with
68
72
  direct source writes. Shared styles/helpers use `foundation`, root guides use
@@ -331,6 +335,15 @@ source definitions and values; inspecting definitions does not prove UI parity.
331
335
  The tool keeps the shared naming contract; sample templates and their token
332
336
  lists belong to the project.
333
337
 
338
+ A public review pattern may be covered by real token or composition scenarios;
339
+ it needs no redundant standalone entry. Remove an optional scenario only while
340
+ retaining reachable render coverage. Keep directory ownership anchored by its
341
+ runtime entry (`index` or a file matching the directory name) or colocated fixture.
342
+ Import colocated image/font fixtures so MCP serves them through asset resources.
343
+ For a broken-image case, use a local failing fixture, rather than an external
344
+ network failure or a domain-specific CSP exception. Production asset APIs remain
345
+ unrestricted by this fixture guidance.
346
+
334
347
  Scenarios, colocated fixtures and helpers are development-only. Never export them
335
348
  from the runtime barrel or import them in production, including through aliases,
336
349
  re-exports or dynamic imports. They may not import application stores, routers,
@@ -341,6 +354,27 @@ Use local simulated services in fixtures. Tests may import fixtures; load real
341
354
  foundation CSS rather than assigning shared token values in JavaScript. Keep
342
355
  full-app integration harnesses and workflow coverage in application test support.
343
356
 
357
+ Foundation CSS is checked, not an escape hatch. Value sheets define shared tokens
358
+ on the document root/themes; `fonts.css` holds font-face metadata. `base.css` has
359
+ three closed roles: root/body typography, canvas and theme defaults; limited native
360
+ normalization; and global focus indicators/reduced-motion rules. Every selector
361
+ branch, property and value must fit its role. Token-backed control skins, layout,
362
+ interactive state rules and owner hooks are rejected even through `:where()`.
363
+ Do not copy an unchecked upstream preflight or turn a component repair into a reset.
364
+ Keep behavior and skin in their runtime owner. Put demo-only sizing and framing
365
+ in a public review/layout pattern; add component props for a deliberate runtime
366
+ API need. Preserve required capabilities while following ownership rules.
367
+ Browser review still verifies the effects.
368
+
369
+ Invalid metadata and source failures keep the managed compiler alive for repairs.
370
+ Read `ads_status.build` or `ads://project/build`, fix the owner, then wait for current
371
+ successful output. If the builder exited, fix its command and invoke the preview
372
+ again; ADS starts one fresh child without an MCP reconnect. Compiler-code changes
373
+ also reload on the next preview invocation. Reconnect the client when upgrading
374
+ the ADS server package itself. Persistent SSE/notification streams make
375
+ `networkidle` unsuitable: wait for the App's current ready/error state instead.
376
+
377
+
344
378
 
345
379
 
346
380
  Register component/project Oxlint plugins and rules in `ds.config.json`. Use
@@ -348,7 +382,14 @@ scoped `oxlint.overrides` for justified file-specific settings; never edit
348
382
  `.ds/oxlintrc.json`. These overrides configure Oxlint, not the independent
349
383
  architecture checks in `ads check`. Measured renderer properties and documented
350
384
  non-state hooks use explicit `boundaries`; shared token writes and primitive
351
- state overrides cannot be allowlisted. Add generic architectural protections to
385
+ state overrides cannot be allowlisted. When migrating JSX inline geometry to
386
+ imperative writes, port the source and replace its own boundary entries together
387
+ in the owner's patch. Remove obsolete `inlineStyleFiles` entries and unused
388
+ `styleWrites` properties; do not broaden permissions to keep a migration passing.
389
+ Pointer-capture advisories call for review of cancellation, capture loss and
390
+ teardown, including cleanup delegated to hooks. Verify actual cleanup in a browser;
391
+ a locally present handler alone does not prove correctness.
392
+ Add generic architectural protections to
352
393
  ADS core and keep domain/component-specific rules registered in project setup.
353
394
 
354
395
  Examples and composition scenarios must consume the same public components, patterns,
@@ -40,6 +40,35 @@ Token names and prefixes are fixed by ADS architecture; foreign brand prefixes
40
40
  are rejected. Shared assignments belong in token files. Components own
41
41
  `--ds-*`, patterns `--ux-*`, and the showcase `--showcase-*`; token files cannot
42
42
  assign those local variables or override component state.
43
+
44
+ Foundation roles are enforced by `foundation-role`, `foundation-selector` and
45
+ `foundation-import`, alongside the regular token checks. Shared definitions
46
+ belong on the document root or explicit root themes; ordinary declarations belong
47
+ in `base.css`, and font-face descriptors in optional `fonts.css`. The entry imports
48
+ canonical sheets; those sheets cannot import another styling layer. Component
49
+ classes, IDs, data/ARIA hooks, contextual selectors, keyframes and scoped UI rules
50
+ are rejected in the foundation, including nested functional selectors. Raw visual
51
+ values and undefined token references are checked in native defaults too; literal
52
+ font-face metadata keeps its separate asset role.
53
+
54
+ `base.css` has three closed roles:
55
+
56
+ - Document defaults: root/body typography, canvas colors and theme behavior,
57
+ global text selection and document scrollbars.
58
+ - Native resets: permitted normalization properties and values, such as zero
59
+ spacing, inherited control typography, list resets and responsive replaced media.
60
+ - Global accessibility: universal `:focus-visible` outline rules, and reduced-motion
61
+ overrides inside `@media (prefers-reduced-motion: reduce)`.
62
+
63
+ The `foundation-role` check validates every selector branch, property and value.
64
+ Native selectors are not a bypass: token-backed button backgrounds, flex/grid
65
+ layout, component-specific focus rules and hover/disabled skins are rejected.
66
+ Shared token definitions belong in value sheets, not `base.css`. No project token
67
+ names are embedded in this policy. A zero-specificity `:where()` wrapper does not
68
+ change ownership. Put component design and animation in Linaria owners, and
69
+ scenario layout variations in typed public review-pattern props. Browser review
70
+ still checks the effects and accessibility of permitted defaults.
71
+
43
72
  Own primitive markup, focus, hover, selected, disabled, loading, and motion in
44
73
  the primitive. Use typed props for variations. Keep reusable structure and skin
45
74
  in colocated Linaria blocks using `css` and `cx` from `@linaria/atomic`.
@@ -1,3 +1,6 @@
1
+ :root { color-scheme: light; }
2
+ :root[data-theme="dark"] { color-scheme: dark; }
3
+
1
4
  *,
2
5
  *::before,
3
6
  *::after {
@@ -5,7 +8,7 @@
5
8
  }
6
9
 
7
10
  body {
8
- margin: var(--space-0);
11
+ margin: 0;
9
12
  background: var(--bg-canvas);
10
13
  color: var(--text-body);
11
14
  font-family: var(--font-sans);
@@ -1,5 +1,5 @@
1
1
  :root {
2
- color-scheme: light;
2
+
3
3
  --bg-canvas: #faf9f6;
4
4
  --bg-surface: #fff;
5
5
  --bg-hover: #f1efea;
@@ -15,7 +15,7 @@
15
15
  }
16
16
 
17
17
  :root[data-theme="dark"] {
18
- color-scheme: dark;
18
+
19
19
  --bg-canvas: #17191e;
20
20
  --bg-surface: #20232a;
21
21
  --bg-hover: #2c3039;
@@ -55,7 +55,7 @@ If no published distribution exists, build and pack the tooling checkout with
55
55
  From inside an existing test project:
56
56
 
57
57
  ```sh
58
- ads_tool_package="/absolute/path/to/reqvire-org-ads-0.3.1.tgz"
58
+ ads_tool_package="/absolute/path/to/reqvire-org-ads-0.3.2.tgz"
59
59
  npx --yes --package "$ads_tool_package" ads --help
60
60
  npx --yes --package "$ads_tool_package" ads init --framework react --tool-package "$ads_tool_package"
61
61
  npm install
@@ -93,7 +93,8 @@ component rules through the project's plugin adapter and `ds.config.json`, then
93
93
  run its actual build. Use `lint-config` directly for custom pipeline integration
94
94
  or debugging, not as a separate routine setup step.
95
95
 
96
- Use ADS 0.3.1 or later for the shared-resource preview compiler. `mcp` starts stdio
96
+ Use ADS 0.3.2 or later for the closed foundation contract and recoverable
97
+ shared-resource preview compiler. `mcp` starts stdio
97
98
  plus its local website; `showcase` starts the browser-only MCP runtime. Open the existing runtime URL from `ads_status` when
98
99
  connected; otherwise use `npm run dev:showcase` or the installed `ads showcase`.
99
100
  The tool client discovers entries and renders MCP Apps. Init adds the launch
@@ -26,7 +26,11 @@ unrelated statements. Its real re-export may accompany removal of a redundant
26
26
  `export {};` placeholder; do not add empty exports. A `tokens` manifest lists the foundation
27
27
  entry in `companionFiles`: when creating/deleting a canonical token file such as
28
28
  `fonts.css`, include only its required import change there in the same patch.
29
- Preserve other imports/comments and the canonical order. Use `dryRun: true` to
29
+ Preserve other imports/comments and the canonical order. Runtime owner manifests
30
+ also list a `ds.config.json` companion: change only that owner's `inlineStyleFiles`
31
+ and `boundaries.styleWrites` entries together with its source. Unused permissions
32
+ fail the core checks; do not pre-register a boundary without its implementation.
33
+ Use `dryRun: true` to
30
34
  inspect validation; applying re-runs the gates. Fix rejected patches in their
31
35
  owner. Report unsupported coupled changes instead of bypassing a rejection with
32
36
  direct source writes. Shared styles/helpers use `foundation`, root guides use
@@ -298,6 +302,15 @@ source definitions and values; inspecting definitions does not prove UI parity.
298
302
  The tool keeps the shared naming contract; sample templates and their token
299
303
  lists belong to the project.
300
304
 
305
+ A public review pattern may be covered by real token or composition scenarios;
306
+ it needs no redundant standalone entry. Remove an optional scenario only while
307
+ retaining reachable render coverage. Keep directory ownership anchored by its
308
+ runtime entry (`index` or a file matching the directory name) or colocated fixture.
309
+ Import colocated image/font fixtures so MCP serves them through asset resources.
310
+ For a broken-image case, use a local failing fixture, rather than an external
311
+ network failure or a domain-specific CSP exception. Production asset APIs remain
312
+ unrestricted by this fixture guidance.
313
+
301
314
  Scenarios, colocated fixtures and helpers are development-only. Never export them
302
315
  from the runtime barrel or import them in production, including through aliases,
303
316
  re-exports or dynamic imports. They may not import application stores, routers,
@@ -308,6 +321,27 @@ Use local simulated services in fixtures. Tests may import fixtures; load real
308
321
  foundation CSS rather than assigning shared token values in JavaScript. Keep
309
322
  full-app integration harnesses and workflow coverage in application test support.
310
323
 
324
+ Foundation CSS is checked, not an escape hatch. Value sheets define shared tokens
325
+ on the document root/themes; `fonts.css` holds font-face metadata. `base.css` has
326
+ three closed roles: root/body typography, canvas and theme defaults; limited native
327
+ normalization; and global focus indicators/reduced-motion rules. Every selector
328
+ branch, property and value must fit its role. Token-backed control skins, layout,
329
+ interactive state rules and owner hooks are rejected even through `:where()`.
330
+ Do not copy an unchecked upstream preflight or turn a component repair into a reset.
331
+ Keep behavior and skin in their runtime owner. Put demo-only sizing and framing
332
+ in a public review/layout pattern; add component props for a deliberate runtime
333
+ API need. Preserve required capabilities while following ownership rules.
334
+ Browser review still verifies the effects.
335
+
336
+ Invalid metadata and source failures keep the managed compiler alive for repairs.
337
+ Read `ads_status.build` or `ads://project/build`, fix the owner, then wait for current
338
+ successful output. If the builder exited, fix its command and invoke the preview
339
+ again; ADS starts one fresh child without an MCP reconnect. Compiler-code changes
340
+ also reload on the next preview invocation. Reconnect the client when upgrading
341
+ the ADS server package itself. Persistent SSE/notification streams make
342
+ `networkidle` unsuitable: wait for the App's current ready/error state instead.
343
+
344
+
311
345
 
312
346
 
313
347
  Register component/project Oxlint plugins and rules in `ds.config.json`. Use
@@ -315,7 +349,14 @@ scoped `oxlint.overrides` for justified file-specific settings; never edit
315
349
  `.ds/oxlintrc.json`. These overrides configure Oxlint, not the independent
316
350
  architecture checks in `ads check`. Measured renderer properties and documented
317
351
  non-state hooks use explicit `boundaries`; shared token writes and primitive
318
- state overrides cannot be allowlisted. Add generic architectural protections to
352
+ state overrides cannot be allowlisted. When migrating JSX inline geometry to
353
+ imperative writes, port the source and replace its own boundary entries together
354
+ in the owner's patch. Remove obsolete `inlineStyleFiles` entries and unused
355
+ `styleWrites` properties; do not broaden permissions to keep a migration passing.
356
+ Pointer-capture advisories call for review of cancellation, capture loss and
357
+ teardown, including cleanup delegated to hooks. Verify actual cleanup in a browser;
358
+ a locally present handler alone does not prove correctness.
359
+ Add generic architectural protections to
319
360
  ADS core and keep domain/component-specific rules registered in project setup.
320
361
 
321
362
  Write or update the imported item's local README with public usage, required
@@ -55,7 +55,7 @@ If no published distribution exists, build and pack the tooling checkout with
55
55
  From inside an existing test project:
56
56
 
57
57
  ```sh
58
- ads_tool_package="/absolute/path/to/reqvire-org-ads-0.3.1.tgz"
58
+ ads_tool_package="/absolute/path/to/reqvire-org-ads-0.3.2.tgz"
59
59
  npx --yes --package "$ads_tool_package" ads --help
60
60
  npx --yes --package "$ads_tool_package" ads init --framework react --tool-package "$ads_tool_package"
61
61
  npm install
@@ -93,7 +93,8 @@ component rules through the project's plugin adapter and `ds.config.json`, then
93
93
  run its actual build. Use `lint-config` directly for custom pipeline integration
94
94
  or debugging, not as a separate routine setup step.
95
95
 
96
- Use ADS 0.3.1 or later for the shared-resource preview compiler. `mcp` starts stdio
96
+ Use ADS 0.3.2 or later for the closed foundation contract and recoverable
97
+ shared-resource preview compiler. `mcp` starts stdio
97
98
  plus its local website; `showcase` starts the browser-only MCP runtime. Open the existing runtime URL from `ads_status` when
98
99
  connected; otherwise use `npm run dev:showcase` or the installed `ads showcase`.
99
100
  The tool client discovers entries and renders MCP Apps. Init adds the launch
@@ -47,7 +47,11 @@ unrelated statements. Its real re-export may accompany removal of a redundant
47
47
  `export {};` placeholder; do not add empty exports. A `tokens` manifest lists the foundation
48
48
  entry in `companionFiles`: when creating/deleting a canonical token file such as
49
49
  `fonts.css`, include only its required import change there in the same patch.
50
- Preserve other imports/comments and the canonical order. Use `dryRun: true` to
50
+ Preserve other imports/comments and the canonical order. Runtime owner manifests
51
+ also list a `ds.config.json` companion: change only that owner's `inlineStyleFiles`
52
+ and `boundaries.styleWrites` entries together with its source. Unused permissions
53
+ fail the core checks; do not pre-register a boundary without its implementation.
54
+ Use `dryRun: true` to
51
55
  inspect validation; applying re-runs the gates. Fix rejected patches in their
52
56
  owner. Report unsupported coupled changes instead of bypassing a rejection with
53
57
  direct source writes. Shared styles/helpers use `foundation`, root guides use
@@ -188,6 +192,15 @@ source definitions and values; inspecting definitions does not prove UI parity.
188
192
  The tool keeps the shared naming contract; sample templates and their token
189
193
  lists belong to the project.
190
194
 
195
+ A public review pattern may be covered by real token or composition scenarios;
196
+ it needs no redundant standalone entry. Remove an optional scenario only while
197
+ retaining reachable render coverage. Keep directory ownership anchored by its
198
+ runtime entry (`index` or a file matching the directory name) or colocated fixture.
199
+ Import colocated image/font fixtures so MCP serves them through asset resources.
200
+ For a broken-image case, use a local failing fixture, rather than an external
201
+ network failure or a domain-specific CSP exception. Production asset APIs remain
202
+ unrestricted by this fixture guidance.
203
+
191
204
  Scenarios, colocated fixtures and helpers are development-only. Never export them
192
205
  from the runtime barrel or import them in production, including through aliases,
193
206
  re-exports or dynamic imports. They may not import application stores, routers,
@@ -198,6 +211,27 @@ Use local simulated services in fixtures. Tests may import fixtures; load real
198
211
  foundation CSS rather than assigning shared token values in JavaScript. Keep
199
212
  full-app integration harnesses and workflow coverage in application test support.
200
213
 
214
+ Foundation CSS is checked, not an escape hatch. Value sheets define shared tokens
215
+ on the document root/themes; `fonts.css` holds font-face metadata. `base.css` has
216
+ three closed roles: root/body typography, canvas and theme defaults; limited native
217
+ normalization; and global focus indicators/reduced-motion rules. Every selector
218
+ branch, property and value must fit its role. Token-backed control skins, layout,
219
+ interactive state rules and owner hooks are rejected even through `:where()`.
220
+ Do not copy an unchecked upstream preflight or turn a component repair into a reset.
221
+ Keep behavior and skin in their runtime owner. Put demo-only sizing and framing
222
+ in a public review/layout pattern; add component props for a deliberate runtime
223
+ API need. Preserve required capabilities while following ownership rules.
224
+ Browser review still verifies the effects.
225
+
226
+ Invalid metadata and source failures keep the managed compiler alive for repairs.
227
+ Read `ads_status.build` or `ads://project/build`, fix the owner, then wait for current
228
+ successful output. If the builder exited, fix its command and invoke the preview
229
+ again; ADS starts one fresh child without an MCP reconnect. Compiler-code changes
230
+ also reload on the next preview invocation. Reconnect the client when upgrading
231
+ the ADS server package itself. Persistent SSE/notification streams make
232
+ `networkidle` unsuitable: wait for the App's current ready/error state instead.
233
+
234
+
201
235
 
202
236
 
203
237
  Register component/project Oxlint plugins and rules in `ds.config.json`. Use
@@ -205,7 +239,14 @@ scoped `oxlint.overrides` for justified file-specific settings; never edit
205
239
  `.ds/oxlintrc.json`. These overrides configure Oxlint, not the independent
206
240
  architecture checks in `ads check`. Measured renderer properties and documented
207
241
  non-state hooks use explicit `boundaries`; shared token writes and primitive
208
- state overrides cannot be allowlisted. Add generic architectural protections to
242
+ state overrides cannot be allowlisted. When migrating JSX inline geometry to
243
+ imperative writes, port the source and replace its own boundary entries together
244
+ in the owner's patch. Remove obsolete `inlineStyleFiles` entries and unused
245
+ `styleWrites` properties; do not broaden permissions to keep a migration passing.
246
+ Pointer-capture advisories call for review of cancellation, capture loss and
247
+ teardown, including cleanup delegated to hooks. Verify actual cleanup in a browser;
248
+ a locally present handler alone does not prove correctness.
249
+ Add generic architectural protections to
209
250
  ADS core and keep domain/component-specific rules registered in project setup.
210
251
 
211
252
  - Motion constants belong in the project's token layer. Shared token meanings
@@ -0,0 +1,59 @@
1
+ import path from "node:path";
2
+ import ts from "typescript";
3
+ import { isUiCssProperties, styleAccessProperty } from "./boundaries.mjs";
4
+ import { uiOperation } from "./ui-framework.mjs";
5
+
6
+ const nameOf = node => node && (ts.isIdentifier(node) || ts.isStringLiteralLike(node)) ? node.text : null;
7
+
8
+ /** Audit explicit permissions without inferring project-specific style APIs. */
9
+ export function inspectStyleBoundaries({ root, config }, program, report) {
10
+ const checker = program.getTypeChecker(), usage = new Map();
11
+ function inspect(file) {
12
+ if (usage.has(file)) return usage.get(file);
13
+ const result = { inline: false, writes: new Set() }, source = program.getSourceFile(file);
14
+ function visit(node) {
15
+ if ((ts.isJsxAttribute(node) && nameOf(node.name) === "style") || isUiCssProperties(node, checker)) result.inline = true;
16
+ if (ts.isCallExpression(node) && uiOperation(node.expression, checker) === "createElement") {
17
+ const props = node.arguments[1];
18
+ if (props && ts.isObjectLiteralExpression(props) && props.properties.some(property => nameOf(property.name) === "style")) result.inline = true;
19
+ }
20
+ const property = styleAccessProperty(node);
21
+ if (typeof property === "string") result.writes.add(property);
22
+ ts.forEachChild(node, visit);
23
+ }
24
+ if (source) visit(source);
25
+ usage.set(file, result);
26
+ return result;
27
+ }
28
+ const configFile = path.join(root, "ds.config.json");
29
+ for (const file of config.inlineStyleFiles) {
30
+ if (!inspect(path.resolve(root, file)).inline) report(configFile, "stale-style-boundary", `inlineStyleFiles entry ${file} has no JSX/factory style or UI CSSProperties use. Remove the unused permission; migrate source and its own boundary entries together in the owner's patch.`);
31
+ }
32
+ for (const { file, properties } of config.boundaries.styleWrites) {
33
+ const used = inspect(path.resolve(root, file)).writes;
34
+ for (const property of properties) if (!used.has(property)) report(configFile, "stale-style-boundary", `styleWrites entry ${file} never writes ${property}. Remove the unused property in the owner's source/boundary patch.`);
35
+ }
36
+ }
37
+
38
+ /** A local review signal, never proof that delegated cleanup is absent/correct. */
39
+ export function pointerCaptureAdvisories(source) {
40
+ const captures = [], cleanup = [];
41
+ function hasHandler(node) {
42
+ while (node && (ts.isJsxExpression(node) || ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isNonNullExpression(node))) node = node.expression;
43
+ return !!node && ![ts.SyntaxKind.NullKeyword, ts.SyntaxKind.FalseKeyword].includes(node.kind) && !(ts.isIdentifier(node) && node.text === "undefined");
44
+ }
45
+ function visit(node) {
46
+ if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
47
+ if (node.expression.name.text === "setPointerCapture") captures.push(node);
48
+ if (node.expression.name.text === "addEventListener" && node.arguments[0] && ts.isStringLiteralLike(node.arguments[0]) && node.arguments[0].text === "lostpointercapture" && hasHandler(node.arguments[1])) cleanup.push(node);
49
+ }
50
+ if ((ts.isJsxAttribute(node) || ts.isPropertyAssignment(node) || ts.isMethodDeclaration(node)) && /^(?:onLostPointerCapture(?:Capture)?|onlostpointercapture)$/.test(nameOf(node.name) ?? "") && (ts.isMethodDeclaration(node) || hasHandler(node.initializer))) cleanup.push(node);
51
+ if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.EqualsToken && ts.isPropertyAccessExpression(node.left) && node.left.name.text === "onlostpointercapture" && hasHandler(node.right)) cleanup.push(node);
52
+ ts.forEachChild(node, visit);
53
+ }
54
+ visit(source);
55
+ return cleanup.length ? [] : captures.map(node => {
56
+ const position = source.getLineAndCharacterOfPosition(node.getStart(source));
57
+ return { rule: "pointer-capture-review", line: position.line + 1, column: position.character + 1, message: "Pointer capture has no locally visible lostpointercapture cleanup handler. Verify cancellation and capture-loss cleanup, including any delegated hook/helper. This advisory does not prove cleanup is missing or correct." };
58
+ });
59
+ }
package/dist/units.mjs CHANGED
@@ -84,7 +84,19 @@ export function inspectUnits(options) {
84
84
  }
85
85
  walk(dsRoot);
86
86
  files.sort();
87
- const directoryUnits = files.filter(file => /(?:^|\/)Scenario\.[cm]?[jt]sx?$/.test(file)).map(file => slash(path.dirname(file))).sort((a, b) => b.length - a.length);
87
+ // Runtime entry conventions keep directory ownership stable when an optional
88
+ // preview is removed. A flat scenario family still denotes separate owners.
89
+ const sameName = name => name.replace(/[-_]/g, "").toLowerCase();
90
+ const flatFamilies = new Set(files.filter(file => /\.scenario\.[cm]?[jt]sx?$/.test(file)).map(file => path.posix.dirname(file)));
91
+ const directories = new Set(files.filter(file => /(?:^|\/)Scenario\.[cm]?[jt]sx?$/.test(file)).map(file => slash(path.dirname(file))));
92
+ for (const file of files) {
93
+ if (!/^(?:components|patterns|composition)\//.test(file) || !source.test(file)) continue;
94
+ const directory = slash(path.dirname(file)), name = path.basename(file).replace(source, "");
95
+ if (!directory.includes("/") || /\.(?:scenario|fixture|scenario-adapter)$/.test(name)) continue;
96
+ if (flatFamilies.has(directory)) continue;
97
+ if (name === "index" || sameName(name) === sameName(path.posix.basename(directory)) || name === "fixture") directories.add(directory);
98
+ }
99
+ const directoryUnits = [...directories].sort((a, b) => b.length - a.length);
88
100
  const flatUnits = [...new Set(files.filter(file => /^(?:components|patterns|composition)\//.test(file) && source.test(file) && !directoryUnits.some(directory => file.startsWith(directory + "/"))).map(file => {
89
101
  const base = path.basename(file).split(".")[0];
90
102
  return slash(path.join(path.dirname(file), base));
@@ -120,10 +132,15 @@ export function inspectUnits(options) {
120
132
  const file = slash(path.relative(root, path.join(dsRoot, relative)));
121
133
  if (!units.get(id).files.includes(file)) units.get(id).files.push(file);
122
134
  }
135
+ for (const unit of units.values()) if (boundaryCompanion(unit.id)) {
136
+ (unit.companionFiles ??= []).push({ path: "ds.config.json", allowedChanges: "Only this owner's inlineStyleFiles and boundaries.styleWrites entries, together with its source change; preserve all other configuration and peer permissions." });
137
+ }
123
138
  return { units: [...units.values()].sort((a, b) => a.id.localeCompare(b.id)), owner, publicEntry, stylesEntry, tokenDir,
124
139
  relative: file => slash(path.relative(dsRoot, path.resolve(root, file))) };
125
140
  }
126
141
 
142
+ function boundaryCompanion(unit) { return !["configuration", "documentation", "assets"].includes(unit.split("/")[0]); }
143
+
127
144
  export function validateUnitId(unit) {
128
145
  if (typeof unit !== "string" || unit.length > 256 || !/^[A-Za-z0-9_-]+(?:\/[A-Za-z0-9_-]+)*$/.test(unit) || ["components", "patterns", "composition"].includes(unit)) throw fail("Select one ADS unit ID from ads_units or an unoccupied owner path following its colocation convention.");
129
146
  }
@@ -134,6 +151,7 @@ export function checkUnitFiles(options, unit, changes, inventory = inspectUnits(
134
151
  for (const change of changes) {
135
152
  const relative = inventory.relative(change.path);
136
153
  if (allowNew && unit === "configuration") continue; // The candidate must register every setup/extension file.
154
+ if (change.path === "ds.config.json" && boundaryCompanion(unit)) continue; // Validated against only this owner below.
137
155
  if (relative === inventory.publicEntry) continue;
138
156
  if (unit === "tokens" && relative === inventory.stylesEntry) continue; // Controlled foundation import bookkeeping.
139
157
  if (allowNew && change.originalContent === null && (relative.startsWith(unit + "/") || relative.startsWith(unit + "."))) continue;
@@ -153,6 +171,24 @@ export function checkUnitCandidate(afterOptions, unit, changes, before) {
153
171
  if (unit === "configuration" && (entry.id === "configuration" || after.owner(relative) === "configuration")) continue;
154
172
  if (after.owner(relative) !== entry.id) throw fail("A patch cannot change ownership of existing peer units. Choose an unoccupied owner path.", file);
155
173
  }
174
+ const boundaryChange = changes.find(change => change.path === "ds.config.json");
175
+ if (boundaryChange && unit !== "configuration") {
176
+ if (!boundaryCompanion(unit) || typeof boundaryChange.originalContent !== "string" || typeof boundaryChange.content !== "string") throw fail("This unit cannot create/delete configuration.", boundaryChange.path);
177
+ if (!changes.some(change => change.path !== boundaryChange.path && (before.owner(before.relative(change.path)) === unit || after.owner(after.relative(change.path)) === unit))) throw fail("Boundary companions require a source change in the selected owner.", boundaryChange.path);
178
+ function peerPolicy(content, inventory) {
179
+ const config = JSON.parse(content);
180
+ const owns = file => typeof file === "string" && inventory.owner(inventory.relative(file)) === unit;
181
+ config.inlineStyleFiles = (config.inlineStyleFiles ?? []).filter(file => !owns(file));
182
+ config.boundaries = { ...config.boundaries, styleWrites: (config.boundaries?.styleWrites ?? []).filter(entry => !owns(entry.file)) };
183
+ return config;
184
+ }
185
+ function canonical(value) {
186
+ if (Array.isArray(value)) return value.map(canonical);
187
+ if (value && typeof value === "object") return Object.fromEntries(Object.keys(value).sort().map(key => [key, canonical(value[key])]));
188
+ return value;
189
+ }
190
+ if (JSON.stringify(canonical(peerPolicy(boundaryChange.originalContent, before))) !== JSON.stringify(canonical(peerPolicy(boundaryChange.content, after)))) throw fail("Boundary companion patches may change only inlineStyleFiles and styleWrites entries owned by the selected unit; preserve all other configuration and peer permissions.", boundaryChange.path);
191
+ }
156
192
  const foundationChange = changes.find(change => before.relative(change.path) === before.stylesEntry);
157
193
  if (unit === "tokens" && foundationChange) {
158
194
  const { originalContent: current, content: proposed } = foundationChange;
package/docs/ADS.md CHANGED
@@ -169,6 +169,35 @@ custom styling in fixture files. The ADS frame pattern uses `--ux-*`. Foreign br
169
169
  CSS imports the approved token files in order; shared assignments belong in
170
170
  those token files, never in consumer styles or component state overrides.
171
171
 
172
+ Foundation roles are enforced by `foundation-role`, `foundation-selector` and
173
+ `foundation-import`, alongside the regular token checks. Shared definitions
174
+ belong on the document root or explicit root themes; ordinary declarations belong
175
+ in `base.css`, and font-face descriptors in optional `fonts.css`. The entry imports
176
+ canonical sheets; those sheets cannot import another styling layer. Component
177
+ classes, IDs, data/ARIA hooks, contextual selectors, keyframes and scoped UI rules
178
+ are rejected in the foundation, including nested functional selectors. Raw visual
179
+ values and undefined token references are checked in native defaults too; literal
180
+ font-face metadata keeps its separate asset role.
181
+
182
+ `base.css` has three closed roles:
183
+
184
+ - Document defaults: root/body typography, canvas colors and theme behavior,
185
+ global text selection and document scrollbars.
186
+ - Native resets: permitted normalization properties and values, such as zero
187
+ spacing, inherited control typography, list resets and responsive replaced media.
188
+ - Global accessibility: universal `:focus-visible` outline rules, and reduced-motion
189
+ overrides inside `@media (prefers-reduced-motion: reduce)`.
190
+
191
+ The `foundation-role` check validates every selector branch, property and value.
192
+ Native selectors are not a bypass: token-backed button backgrounds, flex/grid
193
+ layout, component-specific focus rules and hover/disabled skins are rejected.
194
+ Shared token definitions belong in value sheets, not `base.css`. No project token
195
+ names are embedded in this policy. A zero-specificity `:where()` wrapper does not
196
+ change ownership. Put component design and animation in Linaria owners, and
197
+ scenario layout variations in typed public review-pattern props. Browser review
198
+ still checks the effects and accessibility of permitted defaults.
199
+
200
+
172
201
  Store original branding and its usage, credits, and licenses in
173
202
  `assets/brand/`, and fonts in `assets/fonts/`. Configure generated icons and
174
203
  asset mounts as described in the [branding guide](GUIDE.md#branding).
@@ -262,10 +291,14 @@ The checks enforce the architecture's ownership boundaries:
262
291
  | Tokens and styles | Fixed token vocabulary, required definitions, declared value types and references, raw visual values, shared-token assignments, foundation import order, and component/pattern namespaces |
263
292
  | Public API and dependencies | Consumers use public ADS imports; ADS implementations and showcase do not import application source; runtime code does not import showcase fixtures |
264
293
  | Interaction policy | Primitive state variables stay inside components; non-state customization hooks require explicit registration |
265
- | Renderer exceptions | Inline JSX styles and imperative style writes require narrow configured boundaries; public props omit inherited style and reject open-ended visual strings |
294
+ | Renderer exceptions | Inline styles and imperative writes require used, narrow configured boundaries; stale entries fail; public props omit inherited style and reject open-ended visual strings |
266
295
  | Preview ownership | Scenarios define no CSS, custom classes, or native controls; all declared tokens and public renderable UI have reachable registered scenarios; token references must exist; legacy catalogue pages are checked when present |
267
296
  | Assets | Source/public collisions and configured generated artifacts in source are rejected |
268
297
 
298
+ Pointer capture without locally visible capture-loss cleanup produces a
299
+ nonblocking advisory. Review delegated cleanup and browser behavior; neither a
300
+ warning nor a handler's presence establishes lifecycle correctness.
301
+
269
302
  Document a new ownership exception with its guard/configuration change and
270
303
  tests. Passing static checks establishes only the policies and source formats
271
304
  they cover. Browser review and behavior tests still verify accessibility,