@reqvire-org/ads 0.3.5 → 0.3.7

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 (51) hide show
  1. package/README.md +5 -1
  2. package/dist/atomic-composition.mjs +183 -0
  3. package/dist/bootstrap-ds/ADS.md +9 -3
  4. package/dist/bootstrap-ds/README.md +10 -0
  5. package/dist/boundaries.mjs +3 -0
  6. package/dist/check.mjs +16 -0
  7. package/dist/cli.mjs +1 -1
  8. package/dist/config.mjs +6 -2
  9. package/dist/mcp/build-status.mjs +7 -4
  10. package/dist/mcp/context.mjs +2 -2
  11. package/dist/mcp/edit-preparation.mjs +111 -0
  12. package/dist/mcp/mutations.mjs +54 -38
  13. package/dist/mcp/preview-watch.mjs +10 -4
  14. package/dist/mcp/server.mjs +36 -24
  15. package/dist/mcp/text-diff.mjs +19 -0
  16. package/dist/preview-resources.mjs +45 -8
  17. package/dist/preview-scope.mjs +30 -0
  18. package/dist/public-api.mjs +37 -1
  19. package/dist/scenarios-compiler.mjs +24 -17
  20. package/dist/scenarios.mjs +58 -6
  21. package/dist/scoped-tokens.mjs +57 -0
  22. package/dist/skills/ads-break-ui/SKILL.md +10 -2
  23. package/dist/skills/ads-break-ui/references/atomic-styles.md +121 -0
  24. package/dist/skills/ads-break-ui/references/guarded-editing.md +44 -0
  25. package/dist/skills/ads-break-ui/references/verification.md +6 -0
  26. package/dist/skills/ads-designer/SKILL.md +10 -2
  27. package/dist/skills/ads-designer/assets/bootstrap-ds/ADS.md +9 -3
  28. package/dist/skills/ads-designer/assets/bootstrap-ds/README.md +10 -0
  29. package/dist/skills/ads-designer/references/atomic-styles.md +121 -0
  30. package/dist/skills/ads-designer/references/guarded-editing.md +44 -0
  31. package/dist/skills/ads-import/SKILL.md +10 -2
  32. package/dist/skills/ads-import/references/atomic-styles.md +121 -0
  33. package/dist/skills/ads-import/references/guarded-editing.md +44 -0
  34. package/dist/skills/ads-import/references/tokens.md +4 -2
  35. package/dist/skills/ads-migrate/SKILL.md +10 -2
  36. package/dist/skills/ads-migrate/references/atomic-styles.md +121 -0
  37. package/dist/skills/ads-migrate/references/guarded-editing.md +44 -0
  38. package/dist/skills/ads-motion/SKILL.md +10 -2
  39. package/dist/skills/ads-motion/references/atomic-styles.md +121 -0
  40. package/dist/skills/ads-motion/references/guarded-editing.md +44 -0
  41. package/dist/token-fixtures.mjs +30 -0
  42. package/dist/tokens.mjs +8 -2
  43. package/dist/units.mjs +15 -0
  44. package/dist/verification.mjs +1 -1
  45. package/docs/ADS.md +59 -2
  46. package/docs/ARCHITECTURE.md +27 -0
  47. package/docs/BEHAVIORS.md +20 -0
  48. package/docs/CHANGELOG.md +32 -0
  49. package/docs/GUIDE.md +8 -3
  50. package/docs/MCP.md +70 -5
  51. package/package.json +4 -2
@@ -0,0 +1,121 @@
1
+ # Atomic style composition
2
+
3
+ Use this guide when creating or migrating component/pattern styles, changing
4
+ base/size/state combinations, or reviewing `atomic-composition-review` warnings.
5
+ It is also available as `ads://guides/atomic-styles` through ADS MCP.
6
+
7
+ ## Why valid tokens can still render incorrectly
8
+
9
+ Atomic Linaria extracts declarations into reusable classes. `cx` from
10
+ `@linaria/atomic` or `@linaria/core` replaces earlier atoms with the same property
11
+ and selector/condition key. It does not expand every CSS shorthand or resolve
12
+ every overlap between physical and logical properties.
13
+
14
+ For example, these declarations have different atomic keys:
15
+
16
+ ```tsx
17
+ const base = css`
18
+ padding: 0;
19
+ `;
20
+ const small = css`
21
+ padding-inline: var(--space-6);
22
+ `;
23
+
24
+ cx(base, small);
25
+ ```
26
+
27
+ Both classes survive. Both declarations can affect horizontal padding in a
28
+ horizontal writing mode. In the inspected Linaria implementation, `padding` and
29
+ `padding-inline` receive equal specificity. If a reset atom is emitted later,
30
+ it can override the intended size. Shared atom reuse and CSS splitting can make
31
+ this visible in a larger build even when an isolated view appears correct.
32
+ The order of class names in HTML does not decide equal-specificity rule order.
33
+ Valid tokens, successful extraction and a passing source check do not establish
34
+ the intended computed value.
35
+
36
+ See Linaria's [class merging implementation](https://github.com/callstack/linaria/blob/master/packages/core/src/cx.ts)
37
+ and [property priority implementation](https://github.com/callstack/linaria/blob/master/packages/atomic/src/processors/helpers/propertyPriority.ts).
38
+ Inspect the project's installed version when investigating its output.
39
+
40
+ ## Prefer matching properties across composed blocks
41
+
42
+ Express the reset and overrides using the same property vocabulary:
43
+
44
+ ```tsx
45
+ const base = css`
46
+ padding-block: 0;
47
+ padding-inline: 0;
48
+ `;
49
+ const small = css`
50
+ padding-inline: var(--space-6);
51
+ `;
52
+ const regular = css`
53
+ padding-block: var(--space-4);
54
+ padding-inline: var(--space-8);
55
+ `;
56
+
57
+ cx(base, compact ? small : regular);
58
+ ```
59
+
60
+ Now each selected size replaces the corresponding base atom through `cx`.
61
+ Review every size, including the default and icon-only variants. Changing only
62
+ one size can leave another size mixing shorthand and logical properties.
63
+ The names and tokens above are illustrative: use the project's real API and
64
+ declared tokens. Preserve its intended dimensions and supported writing modes.
65
+
66
+ The same reasoning applies to margin, inset, logical borders/corners, dimensions
67
+ and overflow. For example, prefer `margin-inline: 0` plus a size's
68
+ `margin-inline: var(...)` over combining `margin: 0` with that logical override.
69
+ Choose consistent physical properties when the product deliberately positions
70
+ against physical edges; logical properties are not mandatory.
71
+
72
+ An intentional state override may have a different selector or condition:
73
+
74
+ ```tsx
75
+ const treatment = css`
76
+ padding-inline: var(--space-6);
77
+ &:hover { padding-inline: var(--space-8); }
78
+ `;
79
+ ```
80
+
81
+ This is a distinct state, not two competing base atoms. Whether changing padding
82
+ on hover is desirable remains a product decision.
83
+
84
+ ## Respond to a warning
85
+
86
+ 1. Follow the warning's related file/line locations and inspect the actual `cx`
87
+ composition. Variant lookups may contribute several possible combinations.
88
+ 2. In a current built preview, inspect computed properties and the winning CSS
89
+ rules. Establish whether extraction order, specificity, writing mode or an
90
+ intentional override explains the result.
91
+ 3. For accidental overlap, align property names/granularity across the base and
92
+ affected variants. Use Linaria's `cx`, preserve tokens and fix the owning
93
+ component/pattern. Reordering class strings does not remove overlapping keys.
94
+ 4. Verify the affected sizes/states and supported directions in the normal app
95
+ and preview build. Add a registered rendered check when a specific geometry
96
+ or computed-style guarantee matters.
97
+
98
+ Increasing specificity, adding `!important`, copying styles into scenarios or
99
+ moving component styling into `base.css` can hide the symptom while retaining
100
+ the conflict. Prefer resolving the composition itself. If an overlap is
101
+ intentional, document why its precedence is stable and verify the resulting
102
+ behavior rather than changing product capabilities to silence a warning.
103
+
104
+ ## What ADS checks
105
+
106
+ `atomic-composition-review` is a nonblocking warning, returned under `advisories`
107
+ with `severity: "warning"`. CLI checks still exit successfully when there are no
108
+ errors; MCP checks and mutations preserve the warning and its guidance URI.
109
+
110
+ The source review examines atomic Linaria templates and statically traceable
111
+ `cx` arguments, including aliases, local imports, nested composition, conditional
112
+ expressions and variant-object lookups. It checks known overlaps involving box
113
+ spacing, inset, borders/corners, dimensions, overflow, backgrounds, typography,
114
+ motion and common layout shorthands, plus `all` resets. It compares shared selector/condition contexts; separate alternatives
115
+ within one conditional or lookup are not assumed to apply together.
116
+
117
+ This is a bounded source review, not a CSS engine or a proof of precedence. It
118
+ does not enumerate every external class, runtime function result, selector
119
+ interaction or overlapping media query. An empty warning list does not establish
120
+ visual correctness. Actual failed registered browser/behavior checks remain
121
+ blocking; potentially intentional source overlaps remain warnings.
@@ -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)
@@ -324,6 +367,13 @@ Pointer capture without locally visible capture-loss cleanup produces a
324
367
  nonblocking advisory. Review delegated cleanup and browser behavior; neither a
325
368
  warning nor a handler's presence establishes lifecycle correctness.
326
369
 
370
+ Potential atomic CSS overlaps produce `atomic-composition-review` warnings with
371
+ related declarations and a guidance URI. They are nonblocking, including during
372
+ guarded mutations: valid CSS can intentionally overlap. Read
373
+ [atomic style composition](../skills/ads-designer/references/atomic-styles.md) for causes, matching-property fixes
374
+ and the source review's limits. Registered rendered checks can enforce a
375
+ project's intended computed geometry; token validity alone cannot establish it.
376
+
327
377
  Document a new ownership exception with its guard/configuration change and
328
378
  tests. Passing static checks establishes only the policies and source formats
329
379
  they cover. Browser review and behavior tests still verify accessibility,
@@ -347,6 +397,13 @@ Validate accepted and rejected cases, configuration, and false-positive limits.
347
397
 
348
398
  ## Bootstrap and updates
349
399
 
400
+ The atomic composition guide lives in each skill's `references/atomic-styles.md`.
401
+ The maintained copy is in `skills/ads-designer/references/atomic-styles.md`;
402
+ MCP serves that packaged skill reference. `npm run sync:skill-references` copies
403
+ it to the other skills for standalone use; `npm run check:skill-references`
404
+ rejects drift during the tool build. Published packages include these references
405
+ under `dist/skills/`.
406
+
350
407
  Root [bootstrap-ds/](../bootstrap-ds/README.md) is the canonical empty scaffold.
351
408
  Its identical copy under `skills/ads-designer/assets/bootstrap-ds/` makes the
352
409
  skill self-contained. Run `npm run sync:skill-starter` after edits and
@@ -122,6 +122,12 @@ Linaria's [colocation and extraction model](https://github.com/callstack/linaria
122
122
  and [atomic composition model](https://github.com/callstack/linaria/blob/master/docs/ATOMIC_CSS.md)
123
123
  support this combination of local ownership and compiled styling.
124
124
 
125
+ Atomic merging matches property/selector/condition keys; it does not resolve
126
+ every shorthand/logical-property overlap. ADS reports detectable composition
127
+ risks as nonblocking warnings with guidance, while registered rendered checks
128
+ can enforce actual geometry. See the skill-local
129
+ [atomic style composition guide](../skills/ads-designer/references/atomic-styles.md).
130
+
125
131
  Airbnb's 2022 [migration report](https://medium.com/airbnb-engineering/airbnbs-trip-to-linaria-dc169230bd12)
126
132
  provides practical evidence for this choice: performance was its leading
127
133
  criterion, with separate CSS caching and colocated authoring among the benefits.
@@ -205,6 +211,13 @@ scenarios unmounts the previous view while keeping unchanged dependencies cached
205
211
  Persistence is a host lifecycle choice; a new external widget loads the same
206
212
  resources independently. Resource sharing is scoped to an App instance.
207
213
 
214
+ Managed graph builders publish into a private session directory beside the
215
+ configured manifest. Logical scenario IDs and resource URIs stay unchanged;
216
+ standalone builds retain the configured output and cannot overwrite a live
217
+ session's success record. Verified compiled snapshots form a bounded shared
218
+ warm-start cache. Source, compiler, dependency and output hashes still gate reuse.
219
+ Ordinary shutdown removes owned session output while retaining that cache.
220
+
208
221
  ```mermaid
209
222
  flowchart LR
210
223
  Host[Minimal host: navigation and MCP client] --> Proxy[Separate-origin sandbox proxy]
@@ -342,6 +355,13 @@ replace the managed process while retaining the MCP connection. Normal build gat
342
355
  remain in place for source changes made through other editors; preview review remains
343
356
  necessary after either editing path.
344
357
 
358
+ For genuinely coupled changes, a bounded transaction accepts one Git patch per
359
+ distinct owner and validates a single complete candidate before publication.
360
+ Each patch retains its ownership and companion rules. Local owner drafts remove
361
+ the need for a separate project mirror; token-sample reconciliation preserves
362
+ project bindings and reports unresolved mappings. Neither preparation changes live
363
+ source or substitutes for guarded application and rendered review.
364
+
345
365
  Required shared-entry bookkeeping can accompany its owner: component/pattern
346
366
  re-exports, canonical foundation imports for token files created or deleted, and
347
367
  only that owner’s style-boundary entries in project configuration in the same
@@ -391,3 +411,10 @@ review pattern. The App then verifies rendered samples in the actual browser by
391
411
  probing those bindings and restoring them. The tool supplies no token/property
392
412
  classification or visual sample palette. A source build alone is not a rendered
393
413
  verification, and the browser check does not judge design quality.
414
+
415
+ An optional owner-local token namespace keeps component/pattern constants beside
416
+ their consumer. Its definition-only stylesheet contains root/theme custom
417
+ properties and is imported by the runtime owner; UI styling remains in Linaria.
418
+ Namespaces, aliases, ownership and actual colocated sample coverage are checked.
419
+ The shared foundation remains the cross-owner vocabulary. See the
420
+ [owner-token contract](ADS.md#owner-local-tokens).
package/docs/BEHAVIORS.md CHANGED
@@ -95,6 +95,15 @@ metadata string does not count. Dynamically passed behavior references produce a
95
95
  review advisory when a direct call cannot be established; verify those through
96
96
  registered execution checks rather than changing the API for the analyzer.
97
97
 
98
+ Callable exports may be functions, callable objects with attached methods, or
99
+ factory results. ADS uses their TypeScript call/construct signatures, including
100
+ the project's library declarations; it does not execute modules or maintain a
101
+ list of recognized factory names. The fast source guard resolves ambiguous public
102
+ values lazily and shares that type discovery within the check. Untyped `any`
103
+ values and objects containing methods do not establish a callable API.
104
+ An invalid `exercises` entry names the offending value and fails validation while
105
+ retaining independent render evidence for `covers`.
106
+
98
107
  Public APIs appear in `ads_catalog` with `apiKind: "ui"` or `"behavior"` and their
99
108
  scenario links. `ads_behaviors` joins contracts to demonstrations and evidence.
100
109
  A demonstrated API is not automatically a verified guarantee.
@@ -145,6 +154,17 @@ Write meaningful assertions for the registered guarantee. The helper waits for
145
154
  the current selected scenario inside the real sandboxed MCP App. It uses `load`
146
155
  and explicit readiness, not `networkidle`: MCP keeps event connections open.
147
156
 
157
+ Compare reference and candidate under equivalent framing. In a reported Chrome
158
+ comparison, `content-visibility` initially used placeholder geometry inside a
159
+ cross-origin App iframe for both ADS and the upstream reference; the same content
160
+ measured differently as a top-level page. This is an observed browser/layout
161
+ condition, not evidence of an ADS defect. App readiness does not imply that every
162
+ off-screen item has rendered. For geometry assertions, bring the relevant content
163
+ into view and wait for its measured layout to settle. Record the framing and
164
+ visibility state; test initial placeholder behavior separately when it matters to
165
+ the guarantee. Wait for focus and positioning readiness before normal keyboard
166
+ comparisons, and test rapid input separately rather than hiding it behind delays.
167
+
148
168
  `mode: "browser"` starts an isolated managed showcase on an automatic port. Test
149
169
  requests contain its scenario URLs, and cleanup closes its host/compiler tree.
150
170
  Mutation candidates use their own source, generated graph and server; a passing
package/docs/CHANGELOG.md CHANGED
@@ -2,6 +2,38 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.3.7
6
+
7
+ - Warn about statically traceable atomic CSS property overlaps without blocking
8
+ checks or guarded edits. Include source pairs, concrete correction guidance and
9
+ a guide inside every ADS skill's references folder, also served by MCP.
10
+ Keep regression cases
11
+ generic and validate computed geometry in a dummy compiled preview.
12
+
13
+ - Preserve callable-object and factory-result recognition in the fast source
14
+ guard using lazy project type discovery. Name invalid scenario `exercises`
15
+ entries without discarding independent UI render coverage.
16
+
17
+ ## 0.3.6
18
+
19
+ - Isolate managed resource-graph output by session; reuse verified compiled
20
+ snapshots across warm starts without letting standalone builds overwrite live
21
+ preview success records.
22
+ - Add component/pattern-owned token namespaces, closed definition-only sheets,
23
+ discovery, alias/ownership checks and real colocated token-preview coverage.
24
+ - Add owner draft/diff preparation and project-directed token sample reconciliation.
25
+ Keep all source writes behind guarded Git patches.
26
+ - Add bounded coupled-owner transactions, validating all guards, registered checks
27
+ and affected behavior checks against the final candidate before publication.
28
+ - Permit empty .gitkeep deletion alongside real new owner source. Retain hidden
29
+ file restrictions, render coverage and public scenario-control ownership.
30
+ - Document framed content-visibility geometry as a browser comparison condition,
31
+ with observable layout/focus readiness and separate rapid-input checks.
32
+
33
+ - Use MCP display titles throughout the diagnostic explorer, with annotation-title
34
+ and tool-name fallbacks. Publish configured App titles in tool metadata and keep
35
+ machine-readable names for protocol calls.
36
+
5
37
  ## 0.3.5
6
38
 
7
39
  - Bound preview-watch test teardown, publish selection requests atomically and
package/docs/GUIDE.md CHANGED
@@ -364,7 +364,11 @@ for edits made by other tools.
364
364
  categories, undefined aliases, alias cycles, and individual theme/media
365
365
  overrides. Project-specific values and additional definitions within ADS's fixed
366
366
  naming contract are allowed. The contract's `naming` section lists the fixed
367
- shared families and names; projects cannot configure alternative prefixes.
367
+ shared families and names; projects cannot rename those shared semantics.
368
+ Component/pattern constants can use an explicit unique owner-local namespace and
369
+ definition-only stylesheet, with the same real-token preview completeness rules.
370
+ See [owner-local tokens](ADS.md#owner-local-tokens). Unregistered foreign names
371
+ remain rejected.
368
372
  `--ds-*`, `--ux-*`, and `--showcase-*` variables remain owned by their layers,
369
373
  including when a file is inside `tokens/`. Shared token assignments outside
370
374
  `tokens/` fail. Source styles also check shared references for undeclared names.
@@ -498,8 +502,9 @@ in owned component/pattern styles, with tokens supplying visual values.
498
502
 
499
503
  ADS has fixed token names and ownership namespaces. Change values in the existing
500
504
  token files; `--accent`, `--font-sans`, `--radius-md`, and the other shared roles
501
- keep their meanings across projects. Foreign prefixes such as `--nuv-*` or
502
- `--brand-*` fail. Components own `--ds-*`, patterns own `--ux-*`, and the showcase
505
+ keep their meanings across projects. Foreign shared prefixes such as `--nuv-*` or
506
+ `--brand-*` fail; a registered owner-local constant namespace is separate from
507
+ the shared branding vocabulary. Components own `--ds-*`, patterns own `--ux-*`, and the showcase
503
508
  reserves `--showcase-*` for showcase boundaries. Fixture files still define no
504
509
  custom styling; the frame pattern uses `--ux-*`. Branding never assigns
505
510
  component state variables.