@reqvire-org/ads 0.3.5 → 0.3.6

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 (43) hide show
  1. package/README.md +5 -1
  2. package/dist/bootstrap-ds/ADS.md +9 -3
  3. package/dist/bootstrap-ds/README.md +10 -0
  4. package/dist/boundaries.mjs +3 -0
  5. package/dist/check.mjs +13 -0
  6. package/dist/config.mjs +6 -2
  7. package/dist/mcp/build-status.mjs +7 -4
  8. package/dist/mcp/context.mjs +2 -2
  9. package/dist/mcp/edit-preparation.mjs +111 -0
  10. package/dist/mcp/mutations.mjs +54 -38
  11. package/dist/mcp/preview-watch.mjs +10 -4
  12. package/dist/mcp/server.mjs +32 -22
  13. package/dist/mcp/text-diff.mjs +19 -0
  14. package/dist/preview-resources.mjs +45 -8
  15. package/dist/preview-scope.mjs +30 -0
  16. package/dist/scenarios-compiler.mjs +24 -17
  17. package/dist/scenarios.mjs +46 -4
  18. package/dist/scoped-tokens.mjs +57 -0
  19. package/dist/skills/ads-break-ui/SKILL.md +4 -2
  20. package/dist/skills/ads-break-ui/references/guarded-editing.md +44 -0
  21. package/dist/skills/ads-break-ui/references/verification.md +6 -0
  22. package/dist/skills/ads-designer/SKILL.md +4 -2
  23. package/dist/skills/ads-designer/assets/bootstrap-ds/ADS.md +9 -3
  24. package/dist/skills/ads-designer/assets/bootstrap-ds/README.md +10 -0
  25. package/dist/skills/ads-designer/references/guarded-editing.md +44 -0
  26. package/dist/skills/ads-import/SKILL.md +4 -2
  27. package/dist/skills/ads-import/references/guarded-editing.md +44 -0
  28. package/dist/skills/ads-import/references/tokens.md +4 -2
  29. package/dist/skills/ads-migrate/SKILL.md +4 -2
  30. package/dist/skills/ads-migrate/references/guarded-editing.md +44 -0
  31. package/dist/skills/ads-motion/SKILL.md +4 -2
  32. package/dist/skills/ads-motion/references/guarded-editing.md +44 -0
  33. package/dist/token-fixtures.mjs +30 -0
  34. package/dist/tokens.mjs +8 -2
  35. package/dist/units.mjs +15 -0
  36. package/dist/verification.mjs +1 -1
  37. package/docs/ADS.md +45 -2
  38. package/docs/ARCHITECTURE.md +21 -0
  39. package/docs/BEHAVIORS.md +11 -0
  40. package/docs/CHANGELOG.md +20 -0
  41. package/docs/GUIDE.md +8 -3
  42. package/docs/MCP.md +60 -5
  43. package/package.json +1 -1
@@ -0,0 +1,44 @@
1
+ # Advanced guarded editing
2
+
3
+ Use `ads_apply_patch` for independent owner edits. `ads_prepare_patch` can create a
4
+ local draft of one owner and its companions; edit that directory with local file
5
+ tools, request `action:"diff"`, then apply the returned patchFile. Discard the
6
+ draft afterward. Remote clients can keep sending inline Git diffs. Neither draft
7
+ preparation nor diff generation validates or changes live source.
8
+
9
+ For genuinely coupled changes, `ads_apply_transaction` accepts 2–8 distinct owner
10
+ patches (32 distinct files and 16 MiB combined). Each patch keeps its ownership and
11
+ companion restrictions. Patches apply in order; later shared-entry hunks match the
12
+ earlier candidate. All guards, registered checks and affected behavior checks run
13
+ on the final candidate before live source is written. Dry-run first. Rejection,
14
+ conflict and cancellation publish nothing. Dependency installation remains local;
15
+ the candidate uses installed node_modules. Never bypass a rejected gate directly.
16
+
17
+ A component/pattern can own constants without expanding the shared vocabulary:
18
+ colocate `tokens.json` with `{ "schemaVersion":1, "namespace":"--<owner>-" }`
19
+ and a definition-only `tokens.css`, imported by its runtime source. Flat owners use
20
+ `<Owner>.tokens.json`/`<Owner>.tokens.css`. Namespaces are unique and cannot overlap
21
+ shared or --ds-/--ux-/--showcase- variables. Only that owner consumes/defines its
22
+ local tokens; aliases can use shared or own tokens. UI styles stay in Linaria.
23
+ Add a colocated token scenario with `kind:"tokens"`, real fixture `samples` and a
24
+ public review pattern. These inputs belong to the same owner and need real sample
25
+ coverage. Manifests and fixtures stay out of the product bundle.
26
+
27
+ `ads_prepare_token_samples({id, bindings?})` preserves existing project samples,
28
+ removes undeclared names and returns an owner patchFile. Supply explicit
29
+ `{token,property,probe}` bindings for new samples; unresolved tokens stay reported.
30
+ ADS does not infer visual meaning or fabricate renderers. Apply through the guard,
31
+ then inspect the actual App sample effects.
32
+
33
+ An empty .gitkeep can be deleted alongside new source beneath it in the selected
34
+ owner. Nonempty markers and other hidden files stay protected. A redundant
35
+ Scenario.tsx can be deleted when real coverage remains elsewhere; removing a
36
+ scenario does not erase ownership of the unit's runtime source.
37
+
38
+ Keep contextual public parts public when they are intended consumer API. Demonstrate
39
+ them through their actual public composition/customization path and list their real
40
+ render references in covers. Do not count internal production renders as public API
41
+ coverage. Behavioral APIs use exercises and registered guarantees instead.
42
+ Scenario controls still use public UI; native link/button markup belongs in an
43
+ owned public primitive/pattern or an owner-local test, including polymorphic/slot
44
+ adaptation. Do not add demo-only component props or global styles for review glue.
@@ -26,6 +26,12 @@ The showcase keeps a notification stream open. Wait for document loading and the
26
26
  selected App's ready/error state; `networkidle` is not a readiness condition.
27
27
  For tall previews, scroll the App document to its last example and verify it is
28
28
  reachable without the host shell scrolling or clipping the preview.
29
+ Compare references under equivalent framing. With `content-visibility`, App
30
+ readiness can precede off-screen layout: bring measured content into view and
31
+ wait for observable geometry to settle. Record initial placeholder behavior
32
+ separately when relevant; a framed/top-level difference alone does not identify
33
+ an ADS defect. Wait for actual focus/positioning readiness for keyboard checks;
34
+ exercise rapid input as a separate case instead of adding arbitrary delays.
29
35
  Capture the fixture, viewport/container size, theme, zoom, and actions alongside
30
36
  each failure. CSS viewport emulation is useful; it is not evidence of real
31
37
  mobile keyboard, browser chrome, or assistive-technology behavior.
@@ -68,8 +68,8 @@ and `boundaries.styleWrites` entries together with its source. Unused permission
68
68
  fail the core checks; do not pre-register a boundary without its implementation.
69
69
  Use `dryRun: true` to
70
70
  inspect validation; applying re-runs the gates. Fix rejected patches in their
71
- owner. Report unsupported coupled changes instead of bypassing a rejection with
72
- direct source writes. Shared styles/helpers use `foundation`, root guides use
71
+ owner. Use `ads_apply_transaction` for genuinely coupled owner patches;
72
+ never bypass a rejection with direct source writes. Shared styles/helpers use `foundation`, root guides use
73
73
  `documentation`, and branding/fonts use their discovered `assets/*` unit.
74
74
  Intentional configuration, rule exceptions, registered guard code and compiler
75
75
  changes use `configuration`; validate the proposed settings and keep mandatory
@@ -77,6 +77,8 @@ ADS gates. For binary/large assets, generate `git diff --binary --full-index` in
77
77
  `.ds/patches/<name>.patch` and send `patchFile` instead of inline `patch`, so bytes
78
78
  stay out of model context. Binary changes require Git. When editing is disabled,
79
79
  use the client's normal source-editing tools.
80
+ For owner drafts, token-sample reconciliation, coupled transactions and local
81
+ constant namespaces, read [guarded-editing.md](references/guarded-editing.md).
80
82
 
81
83
  Controllers are UI behavior owners: private helpers stay with their component or
82
84
  pattern; reusable controllers use discovered `controllers/<Name>` units. Use the
@@ -36,8 +36,14 @@ Projects sharing this ADS use the same token names, meanings, and value types;
36
36
  their concrete values and theme overrides can differ. Reuse existing local values
37
37
  when importing components/patterns, add missing shared definitions, and import
38
38
  source token values only when adopting that theme is requested.
39
- Token names and prefixes are fixed by ADS architecture; foreign brand prefixes
40
- are rejected. Shared assignments belong in token files. Components own
39
+ Shared token names and prefixes follow the ADS contract. A component/pattern may
40
+ register one unique local namespace in colocated `tokens.json` and define it in
41
+ `tokens.css`, imported by its runtime. Flat owners use `<Owner>.tokens.json` and
42
+ `<Owner>.tokens.css`. These definition-only sheets contain root/theme custom
43
+ properties, not UI selectors or ordinary styles. Other owners consume shared
44
+ tokens; they cannot override or consume a peer's local namespace. A colocated
45
+ `kind:"tokens"` scenario needs real samples for every local token. Shared
46
+ assignments remain in the shared token files. Components own
41
47
  `--ds-*`, patterns `--ux-*`, and the showcase `--showcase-*`; token files cannot
42
48
  assign those local variables or override component state.
43
49
 
@@ -183,7 +189,7 @@ mounting, correct props, every branch/state or visual parity. The older
183
189
 
184
190
  `token-scenario-reference` rejects undeclared token names in metadata and reachable
185
191
  inputs. `token-scenario-render` rejects claimed tokens with no reachable input
186
- reference; `token-scenario-coverage` requires every discovered foundation token
192
+ reference; `token-scenario-coverage` requires every discovered shared or owner-local token
187
193
  to have coverage. A new or removed token therefore needs a scenario/fixture
188
194
  update. All three are errors in `ads check`, run by `lint:ds` and the build.
189
195
  Coverage is static reachability, not proof that every branch actually renders
@@ -62,6 +62,10 @@ the public `TokenPreview` pattern owns their presentation and applies the real C
62
62
  variables. Add/remove samples alongside token definitions. `ads check` rejects
63
63
  missing coverage and invented names; the App verifies the declared CSS bindings
64
64
  in the browser. Extend or replace the project-owned pattern as your tokens evolve.
65
+ `ads_prepare_token_samples` can prepare a fixture patch from explicit project
66
+ property/probe bindings. It preserves existing samples and reports unmapped tokens;
67
+ it does not guess how a new value should look. Component/pattern constants can use
68
+ the colocated namespace contract in [ADS.md](ADS.md), with their own token scenario.
65
69
 
66
70
  Create a component under `design-system/components/`, export it through `@ds`,
67
71
  and colocate `Scenario.tsx` plus `fixture.ts` (or `<Owner>.scenario.tsx` and
@@ -79,6 +83,8 @@ stale retained output. Switching or rebuilding a scenario unmounts its previous
79
83
  state. `showcase/mcp/main.tsx` exports `mountScenario(Scenario, container)` and
80
84
  returns an unmount callback; it is not the shell. Keep the saved `framework`,
81
85
  compiler and mount adapter consistent.
86
+ Managed sessions own their graph output; standalone prebuilds cannot replace it.
87
+ Verified generated graph snapshots preserve warm-start reuse across sessions.
82
88
 
83
89
  The application and previews use the same foundation CSS and Linaria pipeline.
84
90
  The source barrel loads the foundation once. The private `build:lib` script uses
@@ -98,3 +104,7 @@ from a user-selected external repository, preserving revisions and licenses.
98
104
  inspection builds it automatically, then loads selected entries. Browser review
99
105
  still checks layout, accessibility, keyboard/focus, states, themes and motion. A clean
100
106
  build or delivered HTML alone does not establish those behaviors.
107
+ Opt-in MCP editing uses owner Git patches. `ads_prepare_patch` creates a temporary
108
+ owner draft/diff; `ads_apply_transaction` validates bounded coupled owner patches
109
+ against one final candidate. Preparation changes no live source. Keep all guards
110
+ and review the rendered result after applying.
@@ -0,0 +1,44 @@
1
+ # Advanced guarded editing
2
+
3
+ Use `ads_apply_patch` for independent owner edits. `ads_prepare_patch` can create a
4
+ local draft of one owner and its companions; edit that directory with local file
5
+ tools, request `action:"diff"`, then apply the returned patchFile. Discard the
6
+ draft afterward. Remote clients can keep sending inline Git diffs. Neither draft
7
+ preparation nor diff generation validates or changes live source.
8
+
9
+ For genuinely coupled changes, `ads_apply_transaction` accepts 2–8 distinct owner
10
+ patches (32 distinct files and 16 MiB combined). Each patch keeps its ownership and
11
+ companion restrictions. Patches apply in order; later shared-entry hunks match the
12
+ earlier candidate. All guards, registered checks and affected behavior checks run
13
+ on the final candidate before live source is written. Dry-run first. Rejection,
14
+ conflict and cancellation publish nothing. Dependency installation remains local;
15
+ the candidate uses installed node_modules. Never bypass a rejected gate directly.
16
+
17
+ A component/pattern can own constants without expanding the shared vocabulary:
18
+ colocate `tokens.json` with `{ "schemaVersion":1, "namespace":"--<owner>-" }`
19
+ and a definition-only `tokens.css`, imported by its runtime source. Flat owners use
20
+ `<Owner>.tokens.json`/`<Owner>.tokens.css`. Namespaces are unique and cannot overlap
21
+ shared or --ds-/--ux-/--showcase- variables. Only that owner consumes/defines its
22
+ local tokens; aliases can use shared or own tokens. UI styles stay in Linaria.
23
+ Add a colocated token scenario with `kind:"tokens"`, real fixture `samples` and a
24
+ public review pattern. These inputs belong to the same owner and need real sample
25
+ coverage. Manifests and fixtures stay out of the product bundle.
26
+
27
+ `ads_prepare_token_samples({id, bindings?})` preserves existing project samples,
28
+ removes undeclared names and returns an owner patchFile. Supply explicit
29
+ `{token,property,probe}` bindings for new samples; unresolved tokens stay reported.
30
+ ADS does not infer visual meaning or fabricate renderers. Apply through the guard,
31
+ then inspect the actual App sample effects.
32
+
33
+ An empty .gitkeep can be deleted alongside new source beneath it in the selected
34
+ owner. Nonempty markers and other hidden files stay protected. A redundant
35
+ Scenario.tsx can be deleted when real coverage remains elsewhere; removing a
36
+ scenario does not erase ownership of the unit's runtime source.
37
+
38
+ Keep contextual public parts public when they are intended consumer API. Demonstrate
39
+ them through their actual public composition/customization path and list their real
40
+ render references in covers. Do not count internal production renders as public API
41
+ coverage. Behavioral APIs use exercises and registered guarantees instead.
42
+ Scenario controls still use public UI; native link/button markup belongs in an
43
+ owned public primitive/pattern or an owner-local test, including polymorphic/slot
44
+ adaptation. Do not add demo-only component props or global styles for review glue.
@@ -32,8 +32,8 @@ and `boundaries.styleWrites` entries together with its source. Unused permission
32
32
  fail the core checks; do not pre-register a boundary without its implementation.
33
33
  Use `dryRun: true` to
34
34
  inspect validation; applying re-runs the gates. Fix rejected patches in their
35
- owner. Report unsupported coupled changes instead of bypassing a rejection with
36
- direct source writes. Shared styles/helpers use `foundation`, root guides use
35
+ owner. Use `ads_apply_transaction` for genuinely coupled owner patches;
36
+ never bypass a rejection with direct source writes. Shared styles/helpers use `foundation`, root guides use
37
37
  `documentation`, and branding/fonts use their discovered `assets/*` unit.
38
38
  Intentional configuration, rule exceptions, registered guard code and compiler
39
39
  changes use `configuration`; validate the proposed settings and keep mandatory
@@ -41,6 +41,8 @@ ADS gates. For binary/large assets, generate `git diff --binary --full-index` in
41
41
  `.ds/patches/<name>.patch` and send `patchFile` instead of inline `patch`, so bytes
42
42
  stay out of model context. Binary changes require Git. When editing is disabled,
43
43
  use the client's normal source-editing tools.
44
+ For owner drafts, token-sample reconciliation, coupled transactions and local
45
+ constant namespaces, read [guarded-editing.md](references/guarded-editing.md).
44
46
 
45
47
  Controllers are UI behavior owners: private helpers stay with their component or
46
48
  pattern; reusable controllers use discovered `controllers/<Name>` units. Use the
@@ -0,0 +1,44 @@
1
+ # Advanced guarded editing
2
+
3
+ Use `ads_apply_patch` for independent owner edits. `ads_prepare_patch` can create a
4
+ local draft of one owner and its companions; edit that directory with local file
5
+ tools, request `action:"diff"`, then apply the returned patchFile. Discard the
6
+ draft afterward. Remote clients can keep sending inline Git diffs. Neither draft
7
+ preparation nor diff generation validates or changes live source.
8
+
9
+ For genuinely coupled changes, `ads_apply_transaction` accepts 2–8 distinct owner
10
+ patches (32 distinct files and 16 MiB combined). Each patch keeps its ownership and
11
+ companion restrictions. Patches apply in order; later shared-entry hunks match the
12
+ earlier candidate. All guards, registered checks and affected behavior checks run
13
+ on the final candidate before live source is written. Dry-run first. Rejection,
14
+ conflict and cancellation publish nothing. Dependency installation remains local;
15
+ the candidate uses installed node_modules. Never bypass a rejected gate directly.
16
+
17
+ A component/pattern can own constants without expanding the shared vocabulary:
18
+ colocate `tokens.json` with `{ "schemaVersion":1, "namespace":"--<owner>-" }`
19
+ and a definition-only `tokens.css`, imported by its runtime source. Flat owners use
20
+ `<Owner>.tokens.json`/`<Owner>.tokens.css`. Namespaces are unique and cannot overlap
21
+ shared or --ds-/--ux-/--showcase- variables. Only that owner consumes/defines its
22
+ local tokens; aliases can use shared or own tokens. UI styles stay in Linaria.
23
+ Add a colocated token scenario with `kind:"tokens"`, real fixture `samples` and a
24
+ public review pattern. These inputs belong to the same owner and need real sample
25
+ coverage. Manifests and fixtures stay out of the product bundle.
26
+
27
+ `ads_prepare_token_samples({id, bindings?})` preserves existing project samples,
28
+ removes undeclared names and returns an owner patchFile. Supply explicit
29
+ `{token,property,probe}` bindings for new samples; unresolved tokens stay reported.
30
+ ADS does not infer visual meaning or fabricate renderers. Apply through the guard,
31
+ then inspect the actual App sample effects.
32
+
33
+ An empty .gitkeep can be deleted alongside new source beneath it in the selected
34
+ owner. Nonempty markers and other hidden files stay protected. A redundant
35
+ Scenario.tsx can be deleted when real coverage remains elsewhere; removing a
36
+ scenario does not erase ownership of the unit's runtime source.
37
+
38
+ Keep contextual public parts public when they are intended consumer API. Demonstrate
39
+ them through their actual public composition/customization path and list their real
40
+ render references in covers. Do not count internal production renders as public API
41
+ coverage. Behavioral APIs use exercises and registered guarantees instead.
42
+ Scenario controls still use public UI; native link/button markup belongs in an
43
+ owned public primitive/pattern or an owner-local test, including polymorphic/slot
44
+ adaptation. Do not add demo-only component props or global styles for review glue.
@@ -47,9 +47,11 @@ overwriting destination theme behavior during an item import.
47
47
 
48
48
  ## Explicit `tokens` mode imports values
49
49
 
50
- ADS token names and prefixes are fixed. New definitions must follow the existing
50
+ Shared ADS token names and prefixes retain their semantics. Owner-local constants
51
+ may use the registered namespace contract described in [guarded-editing.md](guarded-editing.md);
52
+ they do not create a second shared branding vocabulary. New shared definitions follow the existing
51
53
  architecture; components use `--ds-*`, patterns `--ux-*`, and owned scenario review layouts
52
- `--ux-*` (`--showcase-*` is reserved, not permission for fixture CSS). Never add source-brand aliases or configurable prefix support.
54
+ `--ux-*` (`--showcase-*` is reserved, not permission for fixture CSS). Keep shared names canonical; owner-local namespaces remain separate, explicit contracts.
53
55
  Normalize nonconforming source references to canonical ADS names during import,
54
56
  preserving destination values unless this mode explicitly requests changes.
55
57
 
@@ -33,8 +33,8 @@ and `boundaries.styleWrites` entries together with its source. Unused permission
33
33
  fail the core checks; do not pre-register a boundary without its implementation.
34
34
  Use `dryRun: true` to
35
35
  inspect validation; applying re-runs the gates. Fix rejected patches in their
36
- owner. Report unsupported coupled changes instead of bypassing a rejection with
37
- direct source writes. Shared styles/helpers use `foundation`, root guides use
36
+ owner. Use `ads_apply_transaction` for genuinely coupled owner patches;
37
+ never bypass a rejection with direct source writes. Shared styles/helpers use `foundation`, root guides use
38
38
  `documentation`, and branding/fonts use their discovered `assets/*` unit.
39
39
  Intentional configuration, rule exceptions, registered guard code and compiler
40
40
  changes use `configuration`; validate the proposed settings and keep mandatory
@@ -42,6 +42,8 @@ ADS gates. For binary/large assets, generate `git diff --binary --full-index` in
42
42
  `.ds/patches/<name>.patch` and send `patchFile` instead of inline `patch`, so bytes
43
43
  stay out of model context. Binary changes require Git. When editing is disabled,
44
44
  use the client's normal source-editing tools.
45
+ For owner drafts, token-sample reconciliation, coupled transactions and local
46
+ constant namespaces, read [guarded-editing.md](references/guarded-editing.md).
45
47
 
46
48
 
47
49
  Controllers are UI behavior owners: private helpers stay with their component or
@@ -0,0 +1,44 @@
1
+ # Advanced guarded editing
2
+
3
+ Use `ads_apply_patch` for independent owner edits. `ads_prepare_patch` can create a
4
+ local draft of one owner and its companions; edit that directory with local file
5
+ tools, request `action:"diff"`, then apply the returned patchFile. Discard the
6
+ draft afterward. Remote clients can keep sending inline Git diffs. Neither draft
7
+ preparation nor diff generation validates or changes live source.
8
+
9
+ For genuinely coupled changes, `ads_apply_transaction` accepts 2–8 distinct owner
10
+ patches (32 distinct files and 16 MiB combined). Each patch keeps its ownership and
11
+ companion restrictions. Patches apply in order; later shared-entry hunks match the
12
+ earlier candidate. All guards, registered checks and affected behavior checks run
13
+ on the final candidate before live source is written. Dry-run first. Rejection,
14
+ conflict and cancellation publish nothing. Dependency installation remains local;
15
+ the candidate uses installed node_modules. Never bypass a rejected gate directly.
16
+
17
+ A component/pattern can own constants without expanding the shared vocabulary:
18
+ colocate `tokens.json` with `{ "schemaVersion":1, "namespace":"--<owner>-" }`
19
+ and a definition-only `tokens.css`, imported by its runtime source. Flat owners use
20
+ `<Owner>.tokens.json`/`<Owner>.tokens.css`. Namespaces are unique and cannot overlap
21
+ shared or --ds-/--ux-/--showcase- variables. Only that owner consumes/defines its
22
+ local tokens; aliases can use shared or own tokens. UI styles stay in Linaria.
23
+ Add a colocated token scenario with `kind:"tokens"`, real fixture `samples` and a
24
+ public review pattern. These inputs belong to the same owner and need real sample
25
+ coverage. Manifests and fixtures stay out of the product bundle.
26
+
27
+ `ads_prepare_token_samples({id, bindings?})` preserves existing project samples,
28
+ removes undeclared names and returns an owner patchFile. Supply explicit
29
+ `{token,property,probe}` bindings for new samples; unresolved tokens stay reported.
30
+ ADS does not infer visual meaning or fabricate renderers. Apply through the guard,
31
+ then inspect the actual App sample effects.
32
+
33
+ An empty .gitkeep can be deleted alongside new source beneath it in the selected
34
+ owner. Nonempty markers and other hidden files stay protected. A redundant
35
+ Scenario.tsx can be deleted when real coverage remains elsewhere; removing a
36
+ scenario does not erase ownership of the unit's runtime source.
37
+
38
+ Keep contextual public parts public when they are intended consumer API. Demonstrate
39
+ them through their actual public composition/customization path and list their real
40
+ render references in covers. Do not count internal production renders as public API
41
+ coverage. Behavioral APIs use exercises and registered guarantees instead.
42
+ Scenario controls still use public UI; native link/button markup belongs in an
43
+ owned public primitive/pattern or an owner-local test, including polymorphic/slot
44
+ adaptation. Do not add demo-only component props or global styles for review glue.
@@ -53,8 +53,8 @@ and `boundaries.styleWrites` entries together with its source. Unused permission
53
53
  fail the core checks; do not pre-register a boundary without its implementation.
54
54
  Use `dryRun: true` to
55
55
  inspect validation; applying re-runs the gates. Fix rejected patches in their
56
- owner. Report unsupported coupled changes instead of bypassing a rejection with
57
- direct source writes. Shared styles/helpers use `foundation`, root guides use
56
+ owner. Use `ads_apply_transaction` for genuinely coupled owner patches;
57
+ never bypass a rejection with direct source writes. Shared styles/helpers use `foundation`, root guides use
58
58
  `documentation`, and branding/fonts use their discovered `assets/*` unit.
59
59
  Intentional configuration, rule exceptions, registered guard code and compiler
60
60
  changes use `configuration`; validate the proposed settings and keep mandatory
@@ -62,6 +62,8 @@ ADS gates. For binary/large assets, generate `git diff --binary --full-index` in
62
62
  `.ds/patches/<name>.patch` and send `patchFile` instead of inline `patch`, so bytes
63
63
  stay out of model context. Binary changes require Git. When editing is disabled,
64
64
  use the client's normal source-editing tools.
65
+ For owner drafts, token-sample reconciliation, coupled transactions and local
66
+ constant namespaces, read [guarded-editing.md](references/guarded-editing.md).
65
67
 
66
68
 
67
69
  Controllers are UI behavior owners: private helpers stay with their component or
@@ -0,0 +1,44 @@
1
+ # Advanced guarded editing
2
+
3
+ Use `ads_apply_patch` for independent owner edits. `ads_prepare_patch` can create a
4
+ local draft of one owner and its companions; edit that directory with local file
5
+ tools, request `action:"diff"`, then apply the returned patchFile. Discard the
6
+ draft afterward. Remote clients can keep sending inline Git diffs. Neither draft
7
+ preparation nor diff generation validates or changes live source.
8
+
9
+ For genuinely coupled changes, `ads_apply_transaction` accepts 2–8 distinct owner
10
+ patches (32 distinct files and 16 MiB combined). Each patch keeps its ownership and
11
+ companion restrictions. Patches apply in order; later shared-entry hunks match the
12
+ earlier candidate. All guards, registered checks and affected behavior checks run
13
+ on the final candidate before live source is written. Dry-run first. Rejection,
14
+ conflict and cancellation publish nothing. Dependency installation remains local;
15
+ the candidate uses installed node_modules. Never bypass a rejected gate directly.
16
+
17
+ A component/pattern can own constants without expanding the shared vocabulary:
18
+ colocate `tokens.json` with `{ "schemaVersion":1, "namespace":"--<owner>-" }`
19
+ and a definition-only `tokens.css`, imported by its runtime source. Flat owners use
20
+ `<Owner>.tokens.json`/`<Owner>.tokens.css`. Namespaces are unique and cannot overlap
21
+ shared or --ds-/--ux-/--showcase- variables. Only that owner consumes/defines its
22
+ local tokens; aliases can use shared or own tokens. UI styles stay in Linaria.
23
+ Add a colocated token scenario with `kind:"tokens"`, real fixture `samples` and a
24
+ public review pattern. These inputs belong to the same owner and need real sample
25
+ coverage. Manifests and fixtures stay out of the product bundle.
26
+
27
+ `ads_prepare_token_samples({id, bindings?})` preserves existing project samples,
28
+ removes undeclared names and returns an owner patchFile. Supply explicit
29
+ `{token,property,probe}` bindings for new samples; unresolved tokens stay reported.
30
+ ADS does not infer visual meaning or fabricate renderers. Apply through the guard,
31
+ then inspect the actual App sample effects.
32
+
33
+ An empty .gitkeep can be deleted alongside new source beneath it in the selected
34
+ owner. Nonempty markers and other hidden files stay protected. A redundant
35
+ Scenario.tsx can be deleted when real coverage remains elsewhere; removing a
36
+ scenario does not erase ownership of the unit's runtime source.
37
+
38
+ Keep contextual public parts public when they are intended consumer API. Demonstrate
39
+ them through their actual public composition/customization path and list their real
40
+ render references in covers. Do not count internal production renders as public API
41
+ coverage. Behavioral APIs use exercises and registered guarantees instead.
42
+ Scenario controls still use public UI; native link/button markup belongs in an
43
+ owned public primitive/pattern or an owner-local test, including polymorphic/slot
44
+ adaptation. Do not add demo-only component props or global styles for review glue.
@@ -0,0 +1,30 @@
1
+ import { inspectTokens } from "./tokens.mjs";
2
+ import { inspectScenarios, tokenSampleSource } from "./scenarios.mjs";
3
+ import { inspectUnits } from "./units.mjs";
4
+ import { validTokenSamples } from "./token-samples.mjs";
5
+ import { textDiff } from "./mcp/text-diff.mjs";
6
+
7
+ /** Preserve project bindings; new meanings always need project-selected inputs. */
8
+ export function reconcileTokenSamples(options, {id, bindings = []}) {
9
+ if (bindings.length && !validTokenSamples(bindings)) throw new Error("Bindings need distinct {token, property, probe} CSS samples.");
10
+ const inventory = inspectTokens(options.root), registry = inspectScenarios(options), units = inspectUnits(options);
11
+ const scenario = registry.scenarios.find(entry => entry.id === id && entry.kind === "tokens");
12
+ if (!scenario) throw new Error("Select an exact token scenario ID from ads_scenarios.");
13
+ const owner = units.owner(units.relative(scenario.file));
14
+ const owned = new Set(inventory.tokens.filter(token => token.owner === owner).map(token => token.name));
15
+ if (bindings.some(sample => !owned.has(sample.token))) throw new Error("New sample bindings must name real tokens from this scenario's owner.");
16
+ const removed = scenario.samples.filter(sample => !owned.has(sample.token)).map(sample => sample.token);
17
+ const samples = new Map(scenario.samples.filter(sample => owned.has(sample.token)).map(sample => [`${sample.token}:${sample.property}`, sample]));
18
+ for (const sample of bindings) samples.set(`${sample.token}:${sample.property}`, sample);
19
+ if (!samples.size) throw new Error("A token scenario needs at least one real sample; supply a binding or remove the obsolete scenario with an owner patch.");
20
+ const represented = new Set(registry.scenarios.filter(entry => entry.kind === "tokens" && entry.id !== id).flatMap(entry => entry.samples.map(sample => sample.token)));
21
+ for (const sample of samples.values()) represented.add(sample.token);
22
+ const unresolved = [...owned].filter(name => !represented.has(name));
23
+ const source = tokenSampleSource(options,scenario);
24
+ const list = [...samples.values()];
25
+ const current = scenario.samples;
26
+ const next = JSON.stringify(current) === JSON.stringify(list) ? source.content : source.content.slice(0,source.start) + JSON.stringify(list,null,2) + source.content.slice(source.end);
27
+ return {unit:owner,id,file:source.file,sampleCount:list.length,removed,unresolved,
28
+ patch:textDiff(source.file,source.content,next),
29
+ guidance:"Bindings and probes belong to the project. No token-name/type heuristic chooses a renderer or probe. Apply the proposed owner patch through ads_apply_patch, then verify its real preview."};
30
+ }
package/dist/tokens.mjs CHANGED
@@ -6,6 +6,7 @@ import { loadConfig, sourceFiles } from "./config.mjs";
6
6
  import { foundationFiles, isSharedToken, tokenContract } from "./token-policy.mjs";
7
7
  import { dimensionValue, numericCssUnit } from "./css-values.mjs";
8
8
  import { checkComponentVariable } from "./boundaries.mjs";
9
+ import { scopedTokens, scopedTokenOwner } from "./scoped-tokens.mjs";
9
10
 
10
11
  export { tokenContract };
11
12
 
@@ -49,7 +50,10 @@ export function inspectTokens(directory) {
49
50
  const { root, dsRoot, config } = options;
50
51
  const definitions = new Map();
51
52
  const diagnostics = [];
53
+ const scoped = scopedTokens(options);
54
+ diagnostics.push(...scoped.diagnostics);
52
55
  const files = sourceFiles(path.join(dsRoot, config.tokenDir)).filter((file) => file.endsWith(".css"));
56
+ files.push(...scoped.definitions.map(entry => path.join(root, entry.file)));
53
57
  files.sort((a, b) => foundationFiles.indexOf(path.basename(a)) - foundationFiles.indexOf(path.basename(b)) || a.localeCompare(b));
54
58
  for (const file of files) {
55
59
  let css;
@@ -68,6 +72,7 @@ export function inspectTokens(directory) {
68
72
  column: decl.source.start.column,
69
73
  scope: context(decl),
70
74
  value: decl.value,
75
+ ...(scoped.definitions.some(entry => entry.file === path.relative(root, file).replaceAll("\\", "/")) ? { owner:scoped.definitions.find(entry => entry.file === path.relative(root, file).replaceAll("\\", "/")).unit } : {}),
71
76
  };
72
77
  if (!definitions.has(decl.prop)) definitions.set(decl.prop, []);
73
78
  definitions.get(decl.prop).push(definition);
@@ -107,7 +112,8 @@ export function inspectTokens(directory) {
107
112
  }
108
113
  const resolvedReferences = new Map();
109
114
  for (const ref of references(entry.value)) {
110
- if (!isSharedToken(ref.name)) report("token-name", name, entry, `${name} references ${ref.name}, which does not follow ADS's fixed token naming contract.`);
115
+ const local = scopedTokenOwner(options, ref.name);
116
+ if (local ? entry.owner !== local.unit : !isSharedToken(ref.name)) report("token-name", name, entry, `${name} references ${ref.name} outside its shared/owner token contract.`);
111
117
  if (!scope.has(ref.name) && !ref.fallback) report("token-reference", name, entry, `${name} references undefined ${ref.name}.`);
112
118
  else if (scope.has(ref.name)) resolvedReferences.set(ref.name, resolveAlias(ref.name, [...chain, name]));
113
119
  }
@@ -137,7 +143,7 @@ export function inspectTokens(directory) {
137
143
  }
138
144
  return {
139
145
  contractVersion: tokenContract.version,
140
- tokens: [...definitions].sort(([a], [b]) => a.localeCompare(b)).map(([name, entries]) => ({ name, ...tokenContract.tokens[name], definitions: entries })),
146
+ tokens: [...definitions].sort(([a], [b]) => a.localeCompare(b)).map(([name, entries]) => ({ name, ...tokenContract.tokens[name], ...(scopedTokenOwner(options, name) ? {owner:scopedTokenOwner(options, name).unit, namespace:scopedTokenOwner(options, name).namespace} : {owner:"tokens"}), definitions: entries })),
141
147
  diagnostics,
142
148
  };
143
149
  }
package/dist/units.mjs CHANGED
@@ -100,6 +100,7 @@ export function inspectUnits(options) {
100
100
  const sameName = name => name.replace(/[-_]/g, "").toLowerCase();
101
101
  const flatFamilies = new Set(files.filter(file => /\.scenario\.[cm]?[jt]sx?$/.test(file)).map(file => path.posix.dirname(file)));
102
102
  const directories = new Set(files.filter(file => /(?:^|\/)Scenario\.[cm]?[jt]sx?$/.test(file)).map(file => slash(path.dirname(file))));
103
+ for (const file of files.filter(file => /^(?:components|patterns)\/.+\/tokens\.json$/.test(file))) directories.add(path.posix.dirname(file));
103
104
  for (const file of files) {
104
105
  if (!/^(?:components|patterns|controllers|composition)\//.test(file) || !source.test(file)) continue;
105
106
  const directory = slash(path.dirname(file)), name = path.basename(file).replace(source, "");
@@ -145,6 +146,12 @@ export function inspectUnits(options) {
145
146
  }
146
147
  for (const unit of units.values()) if (boundaryCompanion(unit.id)) {
147
148
  (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." });
149
+ if (/^(?:components|patterns|controllers|composition)\//.test(unit.id)) {
150
+ for (let directory = unit.id; directory !== "."; directory = path.posix.dirname(directory)) {
151
+ const marker = path.join(dsRoot, directory, ".gitkeep");
152
+ if (existsSync(marker) && readFileSync(marker).length === 0) (unit.companionFiles ??= []).push({ path: slash(path.relative(root, marker)), allowedChanges: "Delete this empty placeholder only while adding real source in the selected owner below it." });
153
+ }
154
+ }
148
155
  }
149
156
  return { units: [...units.values()].sort((a, b) => a.id.localeCompare(b.id)), owner, publicEntry, stylesEntry, tokenDir,
150
157
  relative: file => slash(path.relative(dsRoot, path.resolve(root, file))) };
@@ -161,6 +168,14 @@ export function checkUnitFiles(options, unit, changes, inventory = inspectUnits(
161
168
  validateUnitId(unit);
162
169
  for (const change of changes) {
163
170
  const relative = inventory.relative(change.path);
171
+ if (path.posix.basename(relative) === ".gitkeep") {
172
+ const directory = path.posix.dirname(relative);
173
+ if (change.originalContent !== "" || change.content !== null || !/^(?:components|patterns|controllers|composition)\//.test(relative)
174
+ || !(unit === directory || unit.startsWith(directory + "/"))
175
+ || !changes.some(item => item.originalContent === null && item.content !== null && source.test(item.path)
176
+ && (inventory.relative(item.path).startsWith(unit + "/") || inventory.relative(item.path).startsWith(unit + ".")))) throw fail("Only delete an existing empty .gitkeep while adding source in the selected owner beneath it. Other hidden-file changes are not allowed.", change.path);
177
+ continue;
178
+ }
164
179
  if (allowNew && unit === "configuration") continue; // The candidate must register every setup/extension file.
165
180
  if (change.path === "ds.config.json" && boundaryCompanion(unit)) continue; // Validated against only this owner below.
166
181
  if (relative === inventory.publicEntry) continue;
@@ -193,7 +193,7 @@ export function createVerificationController(root) {
193
193
  if (!current) status = "stale";
194
194
  if (signal.aborted) status = "cancelled";
195
195
  results.push({ unit: check.unit, check: check.id, file: check.file, case: check.case, profile: profileName, status, current, revision: check.inputRevision, runId, completedAt: new Date().toISOString(),
196
- previewRevision: profile.mode === "browser" ? readPreviewManifest(options)?.revision ?? null : null,
196
+ previewRevision: profile.mode === "browser" ? host?.previewRevision() ?? readPreviewManifest(options)?.revision ?? null : null,
197
197
  message: runnerError?.message ?? (!matches.length ? "The selected case did not execute/report. Missing and skipped cases do not pass." : matches.filter(item => item.status !== "passed").map(item => String(item.message ?? item.status).slice(0, 2000)).join("\n")), report: slash(path.relative(root, report)) });
198
198
  }
199
199
  if (signal.aborted) break;
package/docs/ADS.md CHANGED
@@ -7,7 +7,8 @@ UX, and Skin pillars. [GUIDE.md](GUIDE.md) gives setup and configuration steps.
7
7
  ## Ownership boundary
8
8
 
9
9
  MCP source editing is opt-in through `ads mcp --mutations`. Its write operation
10
- accepts a Git unified diff for one ADS unit and validates its source, scenario and
10
+ accepts a Git unified diff for one ADS unit (or bounded owner patches for coupled
11
+ transactions) and validates its source, scenario and
11
12
  fixture changes against core checks, registered Oxlint rules and project checks
12
13
  before application. This includes
13
14
  foundation, documentation, original assets and an explicit configuration unit for
@@ -146,7 +147,7 @@ mounting, correct props, every branch/state or visual parity. The older
146
147
 
147
148
  `token-scenario-reference` rejects undeclared token names in metadata and reachable
148
149
  inputs. `token-scenario-render` rejects claimed tokens with no reachable input
149
- reference; `token-scenario-coverage` requires every discovered foundation token
150
+ reference; `token-scenario-coverage` requires every discovered shared or owner-local token
150
151
  to have coverage. A new or removed token therefore needs a scenario/fixture
151
152
  update. All three are errors in `ads check`, run by `lint:ds` and the build.
152
153
  Coverage is static reachability, not proof that every branch actually renders
@@ -168,6 +169,48 @@ preview builds; review meaningful states in both themes and sizes.
168
169
 
169
170
  ## Shared token contract
170
171
 
172
+ ### Owner-local tokens
173
+
174
+ Shared semantics remain in the configured token foundation. Constants specific
175
+ to one component or pattern may live beside it:
176
+
177
+ ```text
178
+ components/Notice/
179
+ Notice.tsx # imports ./tokens.css; UI styling stays in Linaria
180
+ tokens.json # {"schemaVersion":1,"namespace":"--notice-"}
181
+ tokens.css # :root { --notice-width: 356px; }
182
+ Scenario.tsx
183
+ fixture.ts
184
+ Tokens.scenario.tsx # kind: "tokens", public review pattern + samples
185
+ Tokens.fixture.ts
186
+ ```
187
+
188
+ Flat owners use `<Owner>.tokens.json` and `<Owner>.tokens.css`. A unit has one
189
+ explicit namespace, unique across owners and separate from shared/runtime-variable
190
+ prefixes. The stylesheet contains only namespaced custom-property definitions
191
+ under root/theme conditions; imports, selectors for UI, ordinary declarations,
192
+ font faces and animations remain outside it. Its runtime owner imports it, so
193
+ product and preview consume the same values. Raw UI styling remains token-backed.
194
+
195
+ Local token aliases may reference shared tokens or their own namespace, with
196
+ undefined references and cycles rejected. Other owners consume shared tokens;
197
+ they cannot redefine, write or directly consume another owner's local namespace.
198
+ Measured runtime variables retain their existing exact `styleWrites` boundary.
199
+ `ads_tokens` returns each token's owner, definitions and local namespace.
200
+
201
+ A colocated scenario sets `kind:"tokens"` and declares real fixture `samples`
202
+ consumed by a public review pattern. Its ID still derives from its source path.
203
+ Every local token needs this real sample coverage, just like shared tokens.
204
+ Definitions, metadata, fixture and token scenario belong to the same edit unit.
205
+ Manifests/fixtures are development inputs; production includes the runtime's
206
+ imported CSS definitions, never the preview adapter or tooling manifest.
207
+
208
+ Use `ads_prepare_token_samples` to propose sample reconciliation from explicit
209
+ project property/probe bindings. The tool preserves project sample meaning and
210
+ reports missing mappings instead of choosing them from token prefixes.
211
+
212
+ ### Shared definitions
213
+
171
214
  Keep compatible token names, meanings, value types, and alias relationships;
172
215
  project values and theme overrides may differ. The
173
216
  [Data pillar](ARCHITECTURE.md#data-gives-implementations-a-common-language)
@@ -205,6 +205,13 @@ scenarios unmounts the previous view while keeping unchanged dependencies cached
205
205
  Persistence is a host lifecycle choice; a new external widget loads the same
206
206
  resources independently. Resource sharing is scoped to an App instance.
207
207
 
208
+ Managed graph builders publish into a private session directory beside the
209
+ configured manifest. Logical scenario IDs and resource URIs stay unchanged;
210
+ standalone builds retain the configured output and cannot overwrite a live
211
+ session's success record. Verified compiled snapshots form a bounded shared
212
+ warm-start cache. Source, compiler, dependency and output hashes still gate reuse.
213
+ Ordinary shutdown removes owned session output while retaining that cache.
214
+
208
215
  ```mermaid
209
216
  flowchart LR
210
217
  Host[Minimal host: navigation and MCP client] --> Proxy[Separate-origin sandbox proxy]
@@ -342,6 +349,13 @@ replace the managed process while retaining the MCP connection. Normal build gat
342
349
  remain in place for source changes made through other editors; preview review remains
343
350
  necessary after either editing path.
344
351
 
352
+ For genuinely coupled changes, a bounded transaction accepts one Git patch per
353
+ distinct owner and validates a single complete candidate before publication.
354
+ Each patch retains its ownership and companion rules. Local owner drafts remove
355
+ the need for a separate project mirror; token-sample reconciliation preserves
356
+ project bindings and reports unresolved mappings. Neither preparation changes live
357
+ source or substitutes for guarded application and rendered review.
358
+
345
359
  Required shared-entry bookkeeping can accompany its owner: component/pattern
346
360
  re-exports, canonical foundation imports for token files created or deleted, and
347
361
  only that owner’s style-boundary entries in project configuration in the same
@@ -391,3 +405,10 @@ review pattern. The App then verifies rendered samples in the actual browser by
391
405
  probing those bindings and restoring them. The tool supplies no token/property
392
406
  classification or visual sample palette. A source build alone is not a rendered
393
407
  verification, and the browser check does not judge design quality.
408
+
409
+ An optional owner-local token namespace keeps component/pattern constants beside
410
+ their consumer. Its definition-only stylesheet contains root/theme custom
411
+ properties and is imported by the runtime owner; UI styling remains in Linaria.
412
+ Namespaces, aliases, ownership and actual colocated sample coverage are checked.
413
+ The shared foundation remains the cross-owner vocabulary. See the
414
+ [owner-token contract](ADS.md#owner-local-tokens).