@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.
- package/README.md +5 -1
- package/dist/atomic-composition.mjs +183 -0
- package/dist/bootstrap-ds/ADS.md +9 -3
- package/dist/bootstrap-ds/README.md +10 -0
- package/dist/boundaries.mjs +3 -0
- package/dist/check.mjs +16 -0
- package/dist/cli.mjs +1 -1
- package/dist/config.mjs +6 -2
- package/dist/mcp/build-status.mjs +7 -4
- package/dist/mcp/context.mjs +2 -2
- package/dist/mcp/edit-preparation.mjs +111 -0
- package/dist/mcp/mutations.mjs +54 -38
- package/dist/mcp/preview-watch.mjs +10 -4
- package/dist/mcp/server.mjs +36 -24
- package/dist/mcp/text-diff.mjs +19 -0
- package/dist/preview-resources.mjs +45 -8
- package/dist/preview-scope.mjs +30 -0
- package/dist/public-api.mjs +37 -1
- package/dist/scenarios-compiler.mjs +24 -17
- package/dist/scenarios.mjs +58 -6
- package/dist/scoped-tokens.mjs +57 -0
- package/dist/skills/ads-break-ui/SKILL.md +10 -2
- package/dist/skills/ads-break-ui/references/atomic-styles.md +121 -0
- package/dist/skills/ads-break-ui/references/guarded-editing.md +44 -0
- package/dist/skills/ads-break-ui/references/verification.md +6 -0
- package/dist/skills/ads-designer/SKILL.md +10 -2
- package/dist/skills/ads-designer/assets/bootstrap-ds/ADS.md +9 -3
- package/dist/skills/ads-designer/assets/bootstrap-ds/README.md +10 -0
- package/dist/skills/ads-designer/references/atomic-styles.md +121 -0
- package/dist/skills/ads-designer/references/guarded-editing.md +44 -0
- package/dist/skills/ads-import/SKILL.md +10 -2
- package/dist/skills/ads-import/references/atomic-styles.md +121 -0
- package/dist/skills/ads-import/references/guarded-editing.md +44 -0
- package/dist/skills/ads-import/references/tokens.md +4 -2
- package/dist/skills/ads-migrate/SKILL.md +10 -2
- package/dist/skills/ads-migrate/references/atomic-styles.md +121 -0
- package/dist/skills/ads-migrate/references/guarded-editing.md +44 -0
- package/dist/skills/ads-motion/SKILL.md +10 -2
- package/dist/skills/ads-motion/references/atomic-styles.md +121 -0
- package/dist/skills/ads-motion/references/guarded-editing.md +44 -0
- package/dist/token-fixtures.mjs +30 -0
- package/dist/tokens.mjs +8 -2
- package/dist/units.mjs +15 -0
- package/dist/verification.mjs +1 -1
- package/docs/ADS.md +59 -2
- package/docs/ARCHITECTURE.md +27 -0
- package/docs/BEHAVIORS.md +20 -0
- package/docs/CHANGELOG.md +32 -0
- package/docs/GUIDE.md +8 -3
- package/docs/MCP.md +70 -5
- 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
|
-
|
|
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;
|
package/dist/verification.mjs
CHANGED
|
@@ -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
|
|
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
|
|
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
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -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
|
|
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
|
|
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.
|