@reqvire-org/ads 0.3.0 → 0.3.1

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 (47) hide show
  1. package/README.md +1 -1
  2. package/dist/bootstrap-ds/ADS.md +18 -7
  3. package/dist/bootstrap-ds/README.md +12 -10
  4. package/dist/bootstrap-ds/design-system/index.ts +1 -1
  5. package/dist/bootstrap-ds/design-system/patterns/token-preview/Scenario.tsx +4 -0
  6. package/dist/bootstrap-ds/design-system/patterns/token-preview/TokenPreview.tsx +31 -0
  7. package/dist/bootstrap-ds/design-system/patterns/token-preview/fixture.ts +8 -0
  8. package/dist/bootstrap-ds/design-system/tokens/Foundation.fixture.ts +44 -43
  9. package/dist/bootstrap-ds/design-system/tokens/Foundation.scenario.tsx +3 -13
  10. package/dist/bootstrap-ds/ds.config.json +10 -0
  11. package/dist/check.mjs +2 -9
  12. package/dist/init.mjs +22 -5
  13. package/dist/mcp/apps.mjs +2 -2
  14. package/dist/mcp/context.mjs +1 -1
  15. package/dist/mcp/preview-widget.bundle.js +40 -38
  16. package/dist/mcp/project-widget.bundle.js +42 -38
  17. package/dist/mcp/prompts.mjs +1 -1
  18. package/dist/mcp/server.mjs +5 -5
  19. package/dist/preview-resources.mjs +1 -1
  20. package/dist/scenarios.mjs +16 -5
  21. package/dist/skills/ads-assimilate/SKILL.md +28 -12
  22. package/dist/skills/ads-assimilate/references/cli.md +2 -2
  23. package/dist/skills/ads-break-ui/SKILL.md +28 -12
  24. package/dist/skills/ads-break-ui/references/verification.md +4 -0
  25. package/dist/skills/ads-designer/SKILL.md +30 -14
  26. package/dist/skills/ads-designer/assets/bootstrap-ds/ADS.md +18 -7
  27. package/dist/skills/ads-designer/assets/bootstrap-ds/README.md +12 -10
  28. package/dist/skills/ads-designer/assets/bootstrap-ds/design-system/index.ts +1 -1
  29. package/dist/skills/ads-designer/assets/bootstrap-ds/design-system/patterns/token-preview/Scenario.tsx +4 -0
  30. package/dist/skills/ads-designer/assets/bootstrap-ds/design-system/patterns/token-preview/TokenPreview.tsx +31 -0
  31. package/dist/skills/ads-designer/assets/bootstrap-ds/design-system/patterns/token-preview/fixture.ts +8 -0
  32. package/dist/skills/ads-designer/assets/bootstrap-ds/design-system/tokens/Foundation.fixture.ts +44 -43
  33. package/dist/skills/ads-designer/assets/bootstrap-ds/design-system/tokens/Foundation.scenario.tsx +3 -13
  34. package/dist/skills/ads-designer/assets/bootstrap-ds/ds.config.json +10 -0
  35. package/dist/skills/ads-designer/references/cli.md +2 -2
  36. package/dist/skills/ads-import/SKILL.md +28 -12
  37. package/dist/skills/ads-import/references/cli.md +2 -2
  38. package/dist/skills/ads-motion/SKILL.md +28 -12
  39. package/dist/token-policy.mjs +12 -0
  40. package/dist/token-samples.mjs +8 -0
  41. package/dist/units.mjs +34 -10
  42. package/docs/ADS.md +23 -9
  43. package/docs/ARCHITECTURE.md +12 -0
  44. package/docs/CHANGELOG.md +16 -1
  45. package/docs/GUIDE.md +9 -6
  46. package/docs/MCP.md +44 -2
  47. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  export const reasoning = `ADS is development tooling. The project owns its components, patterns, token values and branding.
2
2
  Start with ads_status and use its projectRoot, workflow and project commands; the MCP workspace may be nested inside the client repository. If ADS tools are not exposed, read ads://project/guide then ads://project/status. Find existing public UI with ads_catalog and load one relevant ADS workflow via ads_skills/ads_read_skill. UI implementation/refinement uses ads-designer; library adaptation ads-assimilate; ADS transfer ads-import; motion ads-motion; stress testing ads-break-ui. Domain-model workflows are separate. Inspect the relevant public API, local guide, token definitions and real showcase fixture before proposing changes. Keep source/instruction discovery scoped to the target project, ancestry and selected external sources; broad scans of /mnt or home are not needed to discover this ADS.
3
- When ads_status.mutations.enabled, discover one owner with ads_units, read relevant ranges with ads_read_file and send only a Git unified diff through ads_apply_patch({unit,patch,dryRun}). A unit includes its own scenarios/fixtures; imported peers remain separate. tokens is the token foundation unit. Only that owner's public re-exports may change in the shared entry. Use dryRun to review validation. Rejected patches leave source unchanged; fix diagnostics rather than bypassing checks with direct writes. Shared globals use foundation; guides use documentation; assets use discovered assets/* units. Intentional configuration, registered project guards/builders and rule exceptions use configuration, which validates the proposed settings and refreshes its compiler. Keep mandatory ADS gates. For large/binary diffs, pass patchFile pointing to .ds/patches/<name>.patch rather than loading bytes into context; Git binary patches require installed Git. When editing is disabled, use local editing tools.
3
+ When ads_status.mutations.enabled, discover one owner with ads_units, read relevant ranges with ads_read_file and send only a Git unified diff through ads_apply_patch({unit,patch,dryRun}). A unit includes its own scenarios/fixtures; imported peers remain separate. tokens is the token foundation unit. Only that owner's public re-exports may change in the shared entry; preserve unrelated statements including export {};. Token manifests list companionFiles: include required foundation import changes only for canonical token files created/deleted in the same patch, preserving other imports/comments and canonical order. Use dryRun to review validation. Rejected patches leave source unchanged; fix diagnostics rather than bypassing checks with direct writes. Shared globals use foundation; guides use documentation; assets use discovered assets/* units. Intentional configuration, registered project guards/builders and rule exceptions use configuration, which validates the proposed settings and refreshes its compiler. Keep mandatory ADS gates. For large/binary diffs, pass patchFile pointing to .ds/patches/<name>.patch rather than loading bytes into context; Git binary patches require installed Git. When editing is disabled, use local editing tools.
4
4
  Reason through Data (design-token values, semantic roles and relationships), UX (structure, behavior, accessibility, states and layout), and Skin (token-backed identity and visual states). Business data is a separate application-owned input, supplied through typed props or injected callbacks/services. The project uses Linaria extraction for owned styles; MCP adds no production runtime.
5
5
  Choose an existing public component or pattern first. Keep application data/routing/effects in the application and fixture data/state beside its scenario. Change presentation in its owning ADS implementation, then demonstrate the same public API in the showcase. Do not create a widget-only replica or override component internals.
6
6
  For token source inspection, call ads_tokens with a focused query. For token samples, components, patterns and composition, use ads_scenarios to select a path-derived id and invoke ads_show_scenario. Showcase and MCP mount that exact entry and its colocated fixture; no page initialization is required. The showcase is a minimal tool-owned shell plus an MCP client and Apps host. Its menus come from tools; it invokes the same entry tools and refreshes the active scenario through notifications. The reusable App loads framework runtime, foundation CSS, fonts and selected modules via MCP resources inside its sandbox. Keep project execution and styles out of the outer host; preserve mount/unmount cleanup and lazy dependencies. Use the existing ads_status showcase URL, or ads showcase when no MCP runtime is connected. Projects supply scenarios/fixtures and the compiler, not browser page files or another shell. Tool metadata selects an MCP App; a resources/read of HTML alone is not proof of rendered UI. If the host has no Apps view, give the configured showcase URL and use text/resources.
@@ -43,12 +43,12 @@ export function createAdsMcp(directory, { era = "legacy", skillDirectory, pollIn
43
43
  const project = () => { const context = projectContext(options); context.showcase.url = getShowcaseUrl(); return context; };
44
44
  const subscribed = new Set();
45
45
  const server = new Server({ name: "reqvire-ads", version: pkg.version }, {
46
- instructions: `${serverInstructions} ${mutations ? "Guarded editing is enabled: discover one owner with ads_units, read relevant ranges with ads_read_file and send only a Git unified diff to ads_apply_patch with that unit ID. Include its own scenarios/fixtures and required re-exports together, never unrelated units. The server validates architecture, registered Oxlint and project checks before writing. Fix rejected patches in their owner. Intentional settings, project guards and builders use the configuration unit; mandatory ADS gates still run." : "Source mutation tools are disabled; source edits use the client's editor/filesystem tools."}`,
46
+ instructions: `${serverInstructions} ${mutations ? "Guarded editing is enabled: discover one owner with ads_units, read relevant ranges with ads_read_file and send only a Git unified diff to ads_apply_patch with that unit ID. Include its own scenarios/fixtures and required re-exports together, never unrelated units. Token manifests list companionFiles for canonical foundation import changes required by token files created/deleted in the same patch. The server validates architecture, registered Oxlint and project checks before writing. Fix rejected patches in their owner. Intentional settings, project guards and builders use the configuration unit; mandatory ADS gates still run." : "Source mutation tools are disabled; source edits use the client's editor/filesystem tools."}`,
47
47
  capabilities: { tools: { listChanged: true }, resources: { listChanged: true, subscribe: true }, prompts: {},
48
48
  extensions: { "io.modelcontextprotocol/ui": { mimeTypes: [APP_MIME] }, ...(era === "modern" ? { [SKILLS_EXTENSION]: { directoryRead: true } } : {}) } },
49
49
  });
50
50
  const getRegistry = () => cache.registry ??= inspectScenarios(options);
51
- const scenarioApps = () => getRegistry().scenarios.map(item => ({ id: item.id, title: item.title, kind: item.kind, file: item.artifact }));
51
+ const scenarioApps = () => getRegistry().scenarios.map(item => ({ id: item.id, title: item.title, kind: item.kind, ...(item.samples ? { samples: item.samples } : {}), file: item.artifact }));
52
52
  const tokenScenarios = () => getRegistry().scenarios.filter(item => item.kind === "tokens").map(item => ({ id: item.id, title: item.title, count: item.tokens.length }));
53
53
  const allApps = () => [...options.config.mcp.apps, ...scenarioApps()];
54
54
  const scenarioWidget = () => options.config.mcp.previewManifest ? PREVIEW_APP_URI : SCENARIO_APP_URI;
@@ -214,12 +214,12 @@ export function createAdsMcp(directory, { era = "legacy", skillDirectory, pollIn
214
214
  }
215
215
  return json({ widget: { resourceUri: TOKENS_APP_URI }, category: null, tokenCount: inspectTokens(root).tokens.length, dataUri: `${PROJECT_URI}/tokens`, showcaseUrl: getShowcaseUrl() });
216
216
  } },
217
- { name: "ads_units", description: "Discover editable ADS units and their files: components/patterns with colocated previews; composition; tokens; foundation shared files; documentation; assets/brand, assets/fonts and asset groups; configuration with ds.config.json, package/build/lint configuration and registered project guard/compiler sources. List summaries with query, or pass an exact unit ID for its manifest. Imported peers stay separate. New owners follow the existing colocation convention.", schema: query.extend({ unit: z.string().min(1).max(256).optional() }), call: ({ unit, query: search = "", limit }) => {
217
+ { name: "ads_units", description: "Discover editable ADS units and their files: components/patterns with colocated previews; composition; tokens; foundation shared files; documentation; assets/brand, assets/fonts and asset groups; configuration with ds.config.json, package/build/lint configuration and registered project guard/compiler sources. List summaries with query, or pass an exact unit ID for its manifest. Token manifests include companionFiles with the foundation entry path and allowed import changes. Imported peers stay separate. New owners follow the existing colocation convention.", schema: query.extend({ unit: z.string().min(1).max(256).optional() }), call: ({ unit, query: search = "", limit }) => {
218
218
  const matches = edits.units().filter(item => unit ? item.id === unit : item.id.toLowerCase().includes(search.toLowerCase()));
219
- return json({ total: matches.length, units: matches.slice(0, limit).map(item => ({ id: item.id, kind: item.kind, fileCount: item.files.length, ...(unit ? { files: item.files.slice(0, 200), filesTruncated: item.files.length > 200 } : {}) })) });
219
+ return json({ total: matches.length, units: matches.slice(0, limit).map(item => ({ id: item.id, kind: item.kind, fileCount: item.files.length, ...(unit ? { files: item.files.slice(0, 200), filesTruncated: item.files.length > 200, ...(item.companionFiles ? { companionFiles: item.companionFiles } : {}) } : {}) })) });
220
220
  } },
221
221
  { name: "ads_read_file", description: "Read a bounded range of ADS or registered configuration/extension source. Returns unit, full-file digest, startLine and nextOffset; default/max is 12000 UTF-16 characters. Binary assets return byte count/digest and no encoded content. Paths come from ads_units or discovery. Missing new paths return digest null. Read only relevant context before preparing a Git patch.", schema: z.object({ path: z.string().min(1).max(500), offset: z.number().int().min(0).default(0), limit: z.number().int().min(1).max(mutationLimits.readChars).default(mutationLimits.readChars) }).strict(), call: ({ path: file, ...range }) => json(edits.read(file, range)) },
222
- ...(mutations ? [{ name: "ads_apply_patch", description: "Apply one Git diff to one ADS unit from ads_units. Pass exactly one of patch (inline) or patchFile (a regular .patch/.diff under .ds/patches/, for large/binary diffs without model-context bytes). Text uses --- a/path, +++ b/path and exact @@ hunks; /dev/null creates/deletes. Git binary patches require git diff --binary --full-index and installed Git. A unit owns its implementation, previews and fixtures; imported peers are separate. Foundation, documentation, assets and configuration are editable too. Configuration changes may register/edit project guards and builders; the proposed schema, mandatory core/Oxlint and candidate project checks must pass. Only selected-unit public re-exports may change. Set dryRun true to validate without writing. Fix rejected patches and inspect current previews after acceptance.",
222
+ ...(mutations ? [{ name: "ads_apply_patch", description: "Apply one Git diff to one ADS unit from ads_units. Pass exactly one of patch (inline) or patchFile (a regular .patch/.diff under .ds/patches/, for large/binary diffs without model-context bytes). Text uses --- a/path, +++ b/path and exact @@ hunks; /dev/null creates/deletes. Git binary patches require git diff --binary --full-index and installed Git. A unit owns its implementation, previews and fixtures; imported peers are separate. Foundation, documentation, assets and configuration are editable too. Configuration changes may register/edit project guards and builders; the proposed schema, mandatory core/Oxlint and candidate project checks must pass. Only selected-unit public re-exports may change; preserve unrelated statements including export {};. A tokens patch may include its companionFiles foundation entry only to add/remove canonical imports required by token files created/deleted in this patch, preserving other imports/comments and canonical order. Set dryRun true to validate without writing. Fix rejected patches and inspect current previews after acceptance.",
223
223
  schema: z.object({ unit: z.string().min(1).max(256), dryRun: z.boolean(), patch: z.string().min(1).max(mutationLimits.patchBytes).optional(), patchFile: z.string().min(1).max(500).optional() }).strict().refine(args => (args.patch !== undefined) !== (args.patchFile !== undefined), "Provide exactly one of patch or patchFile."),
224
224
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false }, _meta: { ui: { visibility: ["model"] } },
225
225
  call: async (args, signal) => {
@@ -113,7 +113,7 @@ export function createPreviewResources(output, options, entries) {
113
113
  const scenarios = entries.map((entry, index) => {
114
114
  const chunk = bundle.find(item => item.type === "chunk" && item.facadeModuleId === `\0ads-preview-entry:${index}`);
115
115
  if (!chunk) throw new Error(`Missing compiled scenario entry: ${entry.id}`);
116
- return { id: entry.id, title: entry.title, kind: entry.kind, module: uris.get(chunk.fileName) };
116
+ return { id: entry.id, title: entry.title, kind: entry.kind, ...(entry.samples ? { samples: entry.samples } : {}), module: uris.get(chunk.fileName) };
117
117
  });
118
118
  const graph = { schemaVersion: 1, framework: options.config.framework, assets: records, scenarios };
119
119
  const revision = hash(JSON.stringify(graph));
@@ -6,6 +6,7 @@ import { isDevelopmentFile, isInside, isScenarioFile, sourceFiles } from "./conf
6
6
  import { projectCompilerOptions } from "./boundaries.mjs";
7
7
  import { inspectTokens } from "./tokens.mjs";
8
8
  import { isSharedToken } from "./token-policy.mjs";
9
+ import { validTokenSamples } from "./token-samples.mjs";
9
10
 
10
11
  export const scenarioArtifact = (root, id) => path.join(root, ".ds/scenarios", id, "index.html");
11
12
 
@@ -104,12 +105,16 @@ export function inspectScenarios(options, suppliedProgram) {
104
105
  }
105
106
  const fixtureBase = path.basename(file).startsWith("Scenario.") ? path.join(path.dirname(file), "fixture") : file.replace(/\.scenario\.[^.]+$/, ".fixture");
106
107
  if (![".ts", ".tsx", ".js", ".jsx", ".mts", ".cts", ".mjs", ".cjs", ".json"].some(extension => existsSync(fixtureBase + extension))) report(file, "Keep this scenario's inputs beside it: fixture.ts (directory entry) or <Owner>.fixture.ts (flat entry). An empty fixture is valid for a component without inputs.");
107
- if (!metadata || Object.keys(metadata).some((key) => !["title", "covers", "tokens"].includes(key)) || typeof metadata.title !== "string" || !metadata.title.trim() || !Array.isArray(metadata.covers) || kind !== "tokens" && !metadata.covers.length || metadata.covers.some((name) => typeof name !== "string" || !known.get(name)?.renderable) || Object.hasOwn(metadata, "tokens") && (!Array.isArray(metadata.tokens) || metadata.tokens.some(name => typeof name !== "string" || !name.startsWith("--"))) || kind === "tokens" && !metadata.tokens?.length) {
108
- report(file, "Export static scenario metadata: { title: string, covers: [public UI names], tokens?: [token names] }. Token scenarios need tokens; their covers may be empty. Literal const fixture lists are supported; runtime expressions are not."); continue;
108
+ if (!metadata || Object.keys(metadata).some((key) => !["title", "covers", "tokens", "samples"].includes(key)) || typeof metadata.title !== "string" || !metadata.title.trim() || !Array.isArray(metadata.covers) || !metadata.covers.length || metadata.covers.some((name) => typeof name !== "string" || !known.get(name)?.renderable)
109
+ || Object.hasOwn(metadata, "tokens") && (!Array.isArray(metadata.tokens) || metadata.tokens.some(name => typeof name !== "string" || !name.startsWith("--")))
110
+ || kind === "tokens" && (Object.hasOwn(metadata, "tokens") || !validTokenSamples(metadata.samples) || !metadata.covers.some(name => known.get(name)?.kind === "patterns"))
111
+ || Object.hasOwn(metadata, "samples") && !validTokenSamples(metadata.samples)) {
112
+ report(file, "Export static metadata with nonempty covers. Token scenarios must render a project-owned public review pattern and declare samples: fixture.samples, a literal list of { token, property, probe } CSS samples. A plain value list or covers: [] is incomplete."); continue;
109
113
  }
110
114
  if (!["composition", "tokens"].includes(kind) && metadata.covers.some((name) => known.get(name).kind !== kind)) report(file, "Component scenarios cover primitives; pattern scenarios cover patterns. Use composition/ for larger interfaces.");
111
- for (const name of metadata.tokens ?? []) if (!tokenNames.has(name)) diagnostics.push({ file: path.relative(root, file).replaceAll("\\", "/"), rule: "token-scenario-reference", line: 1, column: 1, message: `Scenario references undeclared token ${name}. Use a real foundation token.` });
112
- scenarios.push({ id, kind, title: metadata.title, covers: [...new Set(metadata.covers)], tokens: [...new Set(metadata.tokens ?? [])], file: path.relative(root, file).replaceAll("\\", "/"), artifact: path.relative(root, scenarioArtifact(root, id)).replaceAll("\\", "/") });
115
+ const tokens = [...new Set([...(metadata.tokens ?? []), ...(metadata.samples ?? []).map(sample => sample.token)])];
116
+ for (const name of tokens) if (!tokenNames.has(name)) diagnostics.push({ file: path.relative(root, file).replaceAll("\\", "/"), rule: "token-scenario-reference", line: 1, column: 1, message: `Scenario references undeclared token ${name}. Use a real foundation token.` });
117
+ scenarios.push({ id, kind, title: metadata.title, covers: [...new Set(metadata.covers)], tokens, ...(metadata.samples ? { samples: metadata.samples } : {}), file: path.relative(root, file).replaceAll("\\", "/"), artifact: path.relative(root, scenarioArtifact(root, id)).replaceAll("\\", "/") });
113
118
  }
114
119
  return { scenarios: scenarios.sort((a, b) => a.id.localeCompare(b.id)), diagnostics };
115
120
  }
@@ -161,6 +166,7 @@ export function inspectScenarioCoverage(options, program, report) {
161
166
  const source = program.getSourceFile(path.join(options.root, scenario.file));
162
167
  const rendered = new Set();
163
168
  const referencedTokens = new Set();
169
+ let samplesPassed = false;
164
170
  const visited = new Set();
165
171
  function visit(node, reached = false) {
166
172
  if (!node || visited.has(node)) return;
@@ -175,6 +181,10 @@ export function inspectScenarioCoverage(options, program, report) {
175
181
  const symbol = canonical(symbolOf(expression));
176
182
  const names = identities.get(symbol?.valueDeclaration ?? symbol?.declarations?.[0]);
177
183
  for (const name of names ?? []) rendered.add(name);
184
+ if (scenario.kind === "tokens" && [...names ?? []].some(name => scenario.covers.includes(name))) {
185
+ const attributes = ts.isCallExpression(node) ? literal(node.arguments[1], checker) : Object.assign({}, ...node.attributes.properties.map(attribute => ts.isJsxSpreadAttribute(attribute) ? literal(attribute.expression, checker) : ts.isJsxAttribute(attribute) && ts.isIdentifier(attribute.name) && attribute.initializer && ts.isJsxExpression(attribute.initializer) ? { [attribute.name.text]: literal(attribute.initializer.expression, checker) } : {}));
186
+ if (JSON.stringify(attributes?.samples) === JSON.stringify(scenario.samples)) samplesPassed = true;
187
+ }
178
188
  }
179
189
  // Follow local adapters reachable from the default entry. Unused example
180
190
  // functions do not satisfy coverage; production implementations are never
@@ -192,9 +202,10 @@ export function inspectScenarioCoverage(options, program, report) {
192
202
  else covered.add(name);
193
203
  }
194
204
  for (const name of referencedTokens) if (!declaredTokens.has(name)) report(source.fileName, "token-scenario-reference", `Reachable scenario input references undeclared token ${name}.`);
205
+ if (scenario.kind === "tokens" && !samplesPassed) report(source.fileName, "token-scenario-render", "Pass the declared fixture.samples into a rendered public review pattern's samples prop. Printing token names or reading unused fixtures is not sample coverage.");
195
206
  for (const name of scenario.tokens) {
196
207
  if (!referencedTokens.has(name)) report(source.fileName, "token-scenario-render", `${name} is declared in tokens but has no reachable reference from the default scenario entry. Consume the real token fixture; metadata alone does not count.`);
197
- else if (declaredTokens.has(name)) coveredTokens.add(name);
208
+ else if (scenario.kind === "tokens" && samplesPassed && declaredTokens.has(name)) coveredTokens.add(name);
198
209
  }
199
210
  }
200
211
  for (const item of ui.filter((item) => item.renderable && !covered.has(item.name))) report(item.file, "scenario-coverage", `Public UI ${item.name} needs a colocated, registered scenario render reference. Related parts may share a scenario.`);
@@ -22,9 +22,15 @@ relevant ranges with `ads_read_file` and send only a Git unified diff through
22
22
  `ads_apply_patch({ unit, patch, dryRun })`. A unit includes its own implementation,
23
23
  scenarios and fixtures; imported peers remain separate. `tokens` is the token
24
24
  foundation unit. Unit discovery and file ownership are distinct from preview IDs.
25
- Include only that owner's required re-exports in the public entry. Use
26
- `dryRun: true` to inspect validation; applying re-runs the gates. Fix rejected
27
- patches in their owner. Shared styles/helpers use `foundation`, root guides use
25
+ Include only that owner's required re-exports in the public entry; preserve
26
+ unrelated statements. Its real re-export may accompany removal of a redundant
27
+ `export {};` placeholder; do not add empty exports. A `tokens` manifest lists the foundation
28
+ entry in `companionFiles`: when creating/deleting a canonical token file such as
29
+ `fonts.css`, include only its required import change there in the same patch.
30
+ Preserve other imports/comments and the canonical order. Use `dryRun: true` to
31
+ inspect validation; applying re-runs the gates. Fix rejected patches in their
32
+ owner. Report unsupported coupled changes instead of bypassing a rejection with
33
+ direct source writes. Shared styles/helpers use `foundation`, root guides use
28
34
  `documentation`, and branding/fonts use their discovered `assets/*` unit.
29
35
  Intentional configuration, rule exceptions, registered guard code and compiler
30
36
  changes use `configuration`; validate the proposed settings and keep mandatory
@@ -209,15 +215,25 @@ entry independently and verify real behavior at narrow/wide sizes in both themes
209
215
  The older `showcase-coverage` policy separately checks application references.
210
216
 
211
217
  Token previews use that same scenario pipeline. Colocate a token scenario and
212
- fixture under the configured token directory. Use static metadata
213
- `{ title: "Typography", covers: ["TokenSamples"], tokens: fixture.tokens }` with
214
- literal const fixture fields; names/sample text are fixture data, values come
215
- from the real foundation CSS. Reuse a public project review pattern for styled
216
- samples. The minimal starter's foundation value scenario can have `covers: []`.
217
- Every declared token needs reachable scenario coverage; adding/removing a token
218
- requires fixture updates. Fix `token-scenario-reference`, `token-scenario-render`
219
- and `token-scenario-coverage` findings. Metadata and unused fixture fields do not
220
- count. Discover exact token IDs with `ads_scenarios({kind:"tokens"})`; mount them
218
+ fixture under the configured token directory. Declare metadata
219
+ `{ title: fixture.title, covers: ["TokenSamples"], samples: fixture.samples }`.
220
+ The renderer name is project-owned; use its real public API. Each literal fixture
221
+ sample declares `{ token, property, probe }`: a real token, the CSS property its
222
+ sample demonstrates, and a valid test value different from that token's current
223
+ value. Pass `samples` into the rendered public review pattern. Names, sample text,
224
+ bindings and probes are fixture data; displayed values come from foundation CSS.
225
+ There is no property catalogue, token-name mapping or probe palette in ADS.
226
+ Every token needs sample coverage; fabricated tokens, missing bindings/probes,
227
+ unused fixtures and value-only lists fail the static gates. `covers: []` is invalid.
228
+ During App rendering, the browser checks sample markers `data-ads-token` and
229
+ `data-ads-property`, validates CSS with its own `CSS.supports`, and temporarily
230
+ probes each token to verify that the declared sample property responds. It restores
231
+ values immediately. Missing, hidden, invalid or hardcoded samples produce a preview
232
+ error instead of a successful review. These browser checks apply to the mounted
233
+ scenario/theme, not every application state or pixel-level design quality.
234
+ Keep styles in the project renderer; do not add inline-style exemptions to scenarios.
235
+ Fix `token-scenario-reference`, `token-scenario-render` and `token-scenario-coverage`
236
+ findings. Metadata and unused fixture fields do not count. Discover exact token IDs with `ads_scenarios({kind:"tokens"})`; mount them
221
237
  with `ads_show_scenario({id})` or `ads_show_tokens({category:id})`. With the shared
222
238
  resource compiler, omitting category opens the first project token scenario.
223
239
  Legacy compilers retain the generic definition inspector. Use `ads_tokens` for
@@ -55,7 +55,7 @@ If no published distribution exists, build and pack the tooling checkout with
55
55
  From inside an existing test project:
56
56
 
57
57
  ```sh
58
- ads_tool_package="/absolute/path/to/reqvire-org-ads-0.3.0.tgz"
58
+ ads_tool_package="/absolute/path/to/reqvire-org-ads-0.3.1.tgz"
59
59
  npx --yes --package "$ads_tool_package" ads --help
60
60
  npx --yes --package "$ads_tool_package" ads init --framework react --tool-package "$ads_tool_package"
61
61
  npm install
@@ -93,7 +93,7 @@ component rules through the project's plugin adapter and `ds.config.json`, then
93
93
  run its actual build. Use `lint-config` directly for custom pipeline integration
94
94
  or debugging, not as a separate routine setup step.
95
95
 
96
- Use ADS 0.3 or later for the shared-resource preview compiler. `mcp` starts stdio
96
+ Use ADS 0.3.1 or later for the shared-resource preview compiler. `mcp` starts stdio
97
97
  plus its local website; `showcase` starts the browser-only MCP runtime. Open the existing runtime URL from `ads_status` when
98
98
  connected; otherwise use `npm run dev:showcase` or the installed `ads showcase`.
99
99
  The tool client discovers entries and renders MCP Apps. Init adds the launch
@@ -25,9 +25,15 @@ relevant ranges with `ads_read_file` and send only a Git unified diff through
25
25
  `ads_apply_patch({ unit, patch, dryRun })`. A unit includes its own implementation,
26
26
  scenarios and fixtures; imported peers remain separate. `tokens` is the token
27
27
  foundation unit. Unit discovery and file ownership are distinct from preview IDs.
28
- Include only that owner's required re-exports in the public entry. Use
29
- `dryRun: true` to inspect validation; applying re-runs the gates. Fix rejected
30
- patches in their owner. Shared styles/helpers use `foundation`, root guides use
28
+ Include only that owner's required re-exports in the public entry; preserve
29
+ unrelated statements. Its real re-export may accompany removal of a redundant
30
+ `export {};` placeholder; do not add empty exports. A `tokens` manifest lists the foundation
31
+ entry in `companionFiles`: when creating/deleting a canonical token file such as
32
+ `fonts.css`, include only its required import change there in the same patch.
33
+ Preserve other imports/comments and the canonical order. Use `dryRun: true` to
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
31
37
  `documentation`, and branding/fonts use their discovered `assets/*` unit.
32
38
  Intentional configuration, rule exceptions, registered guard code and compiler
33
39
  changes use `configuration`; validate the proposed settings and keep mandatory
@@ -161,15 +167,25 @@ entry independently and verify real behavior at narrow/wide sizes in both themes
161
167
  The older `showcase-coverage` policy separately checks application references.
162
168
 
163
169
  Token previews use that same scenario pipeline. Colocate a token scenario and
164
- fixture under the configured token directory. Use static metadata
165
- `{ title: "Typography", covers: ["TokenSamples"], tokens: fixture.tokens }` with
166
- literal const fixture fields; names/sample text are fixture data, values come
167
- from the real foundation CSS. Reuse a public project review pattern for styled
168
- samples. The minimal starter's foundation value scenario can have `covers: []`.
169
- Every declared token needs reachable scenario coverage; adding/removing a token
170
- requires fixture updates. Fix `token-scenario-reference`, `token-scenario-render`
171
- and `token-scenario-coverage` findings. Metadata and unused fixture fields do not
172
- count. Discover exact token IDs with `ads_scenarios({kind:"tokens"})`; mount them
170
+ fixture under the configured token directory. Declare metadata
171
+ `{ title: fixture.title, covers: ["TokenSamples"], samples: fixture.samples }`.
172
+ The renderer name is project-owned; use its real public API. Each literal fixture
173
+ sample declares `{ token, property, probe }`: a real token, the CSS property its
174
+ sample demonstrates, and a valid test value different from that token's current
175
+ value. Pass `samples` into the rendered public review pattern. Names, sample text,
176
+ bindings and probes are fixture data; displayed values come from foundation CSS.
177
+ There is no property catalogue, token-name mapping or probe palette in ADS.
178
+ Every token needs sample coverage; fabricated tokens, missing bindings/probes,
179
+ unused fixtures and value-only lists fail the static gates. `covers: []` is invalid.
180
+ During App rendering, the browser checks sample markers `data-ads-token` and
181
+ `data-ads-property`, validates CSS with its own `CSS.supports`, and temporarily
182
+ probes each token to verify that the declared sample property responds. It restores
183
+ values immediately. Missing, hidden, invalid or hardcoded samples produce a preview
184
+ error instead of a successful review. These browser checks apply to the mounted
185
+ scenario/theme, not every application state or pixel-level design quality.
186
+ Keep styles in the project renderer; do not add inline-style exemptions to scenarios.
187
+ Fix `token-scenario-reference`, `token-scenario-render` and `token-scenario-coverage`
188
+ findings. Metadata and unused fixture fields do not count. Discover exact token IDs with `ads_scenarios({kind:"tokens"})`; mount them
173
189
  with `ads_show_scenario({id})` or `ads_show_tokens({category:id})`. With the shared
174
190
  resource compiler, omitting category opens the first project token scenario.
175
191
  Legacy compilers retain the generic definition inspector. Use `ads_tokens` for
@@ -22,6 +22,10 @@ Select checks that match the item:
22
22
  touch/scroll interaction, and reduced motion for animated controls.
23
23
 
24
24
  Use an isolated browser context. Preserve the user's open pages and settings.
25
+ The showcase keeps a notification stream open. Wait for document loading and the
26
+ selected App's ready/error state; `networkidle` is not a readiness condition.
27
+ For tall previews, scroll the App document to its last example and verify it is
28
+ reachable without the host shell scrolling or clipping the preview.
25
29
  Capture the fixture, viewport/container size, theme, zoom, and actions alongside
26
30
  each failure. CSS viewport emulation is useful; it is not evidence of real
27
31
  mobile keyboard, browser chrome, or assistive-technology behavior.
@@ -57,9 +57,15 @@ relevant ranges with `ads_read_file` and send only a Git unified diff through
57
57
  `ads_apply_patch({ unit, patch, dryRun })`. A unit includes its own implementation,
58
58
  scenarios and fixtures; imported peers remain separate. `tokens` is the token
59
59
  foundation unit. Unit discovery and file ownership are distinct from preview IDs.
60
- Include only that owner's required re-exports in the public entry. Use
61
- `dryRun: true` to inspect validation; applying re-runs the gates. Fix rejected
62
- patches in their owner. Shared styles/helpers use `foundation`, root guides use
60
+ Include only that owner's required re-exports in the public entry; preserve
61
+ unrelated statements. Its real re-export may accompany removal of a redundant
62
+ `export {};` placeholder; do not add empty exports. A `tokens` manifest lists the foundation
63
+ entry in `companionFiles`: when creating/deleting a canonical token file such as
64
+ `fonts.css`, include only its required import change there in the same patch.
65
+ Preserve other imports/comments and the canonical order. Use `dryRun: true` to
66
+ inspect validation; applying re-runs the gates. Fix rejected patches in their
67
+ owner. Report unsupported coupled changes instead of bypassing a rejection with
68
+ direct source writes. Shared styles/helpers use `foundation`, root guides use
63
69
  `documentation`, and branding/fonts use their discovered `assets/*` unit.
64
70
  Intentional configuration, rule exceptions, registered guard code and compiler
65
71
  changes use `configuration`; validate the proposed settings and keep mandatory
@@ -92,8 +98,8 @@ checks; do not fetch the unpublished package.
92
98
 
93
99
  Read [assets/bootstrap-ds/README.md](assets/bootstrap-ds/README.md) and its
94
100
  [contract](assets/bootstrap-ds/ADS.md). The scaffold contains semantic token
95
- defaults, React/TypeScript/Linaria configuration, general lint, an empty public
96
- barrel, empty component/pattern directories, and a project preview compiler adapter. The browser shell is supplied by ADS tooling.
101
+ defaults, React/TypeScript/Linaria configuration, general lint, a public
102
+ barrel, empty product component directories, a project-owned token review pattern, and a project preview compiler adapter. The browser shell is supplied by ADS tooling.
97
103
  It contains no components, shell patterns, vendored fonts, or example screens.
98
104
 
99
105
  Resolve assets relative to this skill's `SKILL.md`, including after installation
@@ -299,15 +305,25 @@ entry independently and verify real behavior at narrow/wide sizes in both themes
299
305
  The older `showcase-coverage` policy separately checks application references.
300
306
 
301
307
  Token previews use that same scenario pipeline. Colocate a token scenario and
302
- fixture under the configured token directory. Use static metadata
303
- `{ title: "Typography", covers: ["TokenSamples"], tokens: fixture.tokens }` with
304
- literal const fixture fields; names/sample text are fixture data, values come
305
- from the real foundation CSS. Reuse a public project review pattern for styled
306
- samples. The minimal starter's foundation value scenario can have `covers: []`.
307
- Every declared token needs reachable scenario coverage; adding/removing a token
308
- requires fixture updates. Fix `token-scenario-reference`, `token-scenario-render`
309
- and `token-scenario-coverage` findings. Metadata and unused fixture fields do not
310
- count. Discover exact token IDs with `ads_scenarios({kind:"tokens"})`; mount them
308
+ fixture under the configured token directory. Declare metadata
309
+ `{ title: fixture.title, covers: ["TokenSamples"], samples: fixture.samples }`.
310
+ The renderer name is project-owned; use its real public API. Each literal fixture
311
+ sample declares `{ token, property, probe }`: a real token, the CSS property its
312
+ sample demonstrates, and a valid test value different from that token's current
313
+ value. Pass `samples` into the rendered public review pattern. Names, sample text,
314
+ bindings and probes are fixture data; displayed values come from foundation CSS.
315
+ There is no property catalogue, token-name mapping or probe palette in ADS.
316
+ Every token needs sample coverage; fabricated tokens, missing bindings/probes,
317
+ unused fixtures and value-only lists fail the static gates. `covers: []` is invalid.
318
+ During App rendering, the browser checks sample markers `data-ads-token` and
319
+ `data-ads-property`, validates CSS with its own `CSS.supports`, and temporarily
320
+ probes each token to verify that the declared sample property responds. It restores
321
+ values immediately. Missing, hidden, invalid or hardcoded samples produce a preview
322
+ error instead of a successful review. These browser checks apply to the mounted
323
+ scenario/theme, not every application state or pixel-level design quality.
324
+ Keep styles in the project renderer; do not add inline-style exemptions to scenarios.
325
+ Fix `token-scenario-reference`, `token-scenario-render` and `token-scenario-coverage`
326
+ findings. Metadata and unused fixture fields do not count. Discover exact token IDs with `ads_scenarios({kind:"tokens"})`; mount them
311
327
  with `ads_show_scenario({id})` or `ads_show_tokens({category:id})`. With the shared
312
328
  resource compiler, omitting category opens the first project token scenario.
313
329
  Legacy compilers retain the generic definition inspector. Use `ads_tokens` for
@@ -114,13 +114,24 @@ design-system/
114
114
  Flat owners may use `Button.scenario.tsx` and `Button.fixture.ts` instead.
115
115
  `Scenario.tsx` exports a default independently mountable view and literal metadata
116
116
  `export const scenario = { title: "Button states", covers: ["Button"] };`.
117
- Token entries under the configured token directory also declare `tokens`:
118
- `export const scenario = { title: "Typography", covers: ["TokenSamples"], tokens: fixture.tokens };`.
119
- Metadata may read literal const fixture fields; it cannot run expressions,
120
- functions, wildcard lists or token discovery. The same fixture list must be
121
- consumed by the default entry. `covers: []` is valid for a minimal token value
122
- inspection entry without public UI; styled samples use a real public review
123
- pattern. Names and sample text belong to the fixture, values to foundation CSS.
117
+ Token entries under the configured token directory declare
118
+ `export const scenario = { title: fixture.title, covers: ["TokenSamples"], samples: fixture.samples };`.
119
+ `TokenSamples` is any project-owned public review pattern, not a required name.
120
+ Its colocated fixture declares literal `samples: [{ token, property, probe }]`.
121
+ Each token must exist and have sample coverage; duplicate bindings, missing probes,
122
+ empty covers and value-only inspectors fail. The default entry passes the declared
123
+ samples to a rendered public pattern's `samples` prop. No token-name/property mapping
124
+ or sample values are embedded in the tool. Display values come from foundation CSS;
125
+ probes are isolated verification inputs owned by the fixture.
126
+
127
+ The renderer marks the actual styled element with `data-ads-token` and
128
+ `data-ads-property`. In a browser, the App validates the declared property/value and
129
+ probe using `CSS.supports`, checks the sample is rendered, then temporarily changes
130
+ that token to its fixture probe. Its computed sample property must respond. Values
131
+ are restored before yielding to painting; hardcoded lookalikes fail. Source gates
132
+ prove the contract structure, not successful rendering. The browser check covers
133
+ the mounted scenario and theme; visual quality and other states still need review.
134
+
124
135
  It imports real public UI through `@ds` and its local fixture. The adapter supplies
125
136
  initial state, callbacks and simulated services. The default entry needs no
126
137
  mandatory caller inputs or prior page initialization. Related parts may share a
@@ -1,8 +1,8 @@
1
1
  # Empty native design system
2
2
 
3
- This private React/TypeScript workspace contains semantic CSS tokens, an empty
4
- public barrel, Linaria extraction and a project preview compiler adapter. It has
5
- no components, patterns, icons, fonts or example screens. The minimal showcase
3
+ This private React/TypeScript workspace contains semantic CSS tokens, a project-owned
4
+ token review pattern, a public barrel, Linaria extraction and a preview compiler
5
+ adapter. It has no product components, icons, fonts or example screens. The minimal showcase
6
6
  shell and MCP client come from ADS tooling.
7
7
 
8
8
  Read the local [contract](ADS.md) and the
@@ -13,7 +13,7 @@ Read the local [contract](ADS.md) and the
13
13
  Copy the contents of `bootstrap-ds/`, including dotfiles, into a new project.
14
14
  The installed `ads-designer` skill contains the same files in
15
15
  `assets/bootstrap-ds/`. Set the project name in `package.json`. Use Node 22.12 or
16
- newer and ADS 0.3 or later for the shared-resource preview compiler.
16
+ newer and ADS 0.3.1 or later for the shared-resource preview compiler.
17
17
 
18
18
  ```sh
19
19
  npx @reqvire-org/ads init --framework react
@@ -25,7 +25,7 @@ npm run dev:showcase
25
25
  For unreleased tooling, run init using the real local package:
26
26
 
27
27
  ```sh
28
- ads_tool_package="/absolute/path/to/reqvire-org-ads-0.3.0.tgz"
28
+ ads_tool_package="/absolute/path/to/reqvire-org-ads-0.3.1.tgz"
29
29
  npx --yes --package "$ads_tool_package" ads init --framework react --tool-package "$ads_tool_package"
30
30
  npm install
31
31
  npm run build
@@ -44,7 +44,7 @@ native Preact app without a saved ADS framework, use `ads init --framework preac
44
44
  Init records that choice and generates native hook imports without migrating the
45
45
  app or rewriting its compiler. Subsequent init runs reuse the saved choice.
46
46
  Init adds missing foundation files and connects architectural checks; it does
47
- not configure Linaria or generate component source. Generated `.ds/` files are
47
+ not configure Linaria or generate product component source. Generated `.ds/` files are
48
48
  ignored; original artwork and provenance belong in `assets/brand/`.
49
49
 
50
50
  ## Inspect and add UI
@@ -56,10 +56,12 @@ Its MCP client discovers entries, invokes preview tools and follows notification
56
56
  There are no required TokensPage/ComponentsPage/PatternsPage/CompositionPage
57
57
  files, and no separate project browser app to implement or rebuild.
58
58
 
59
- The existing `tokens/Foundation.scenario.tsx` and colocated fixture inspect all
60
- starter token values. Add/remove fixture names alongside token definitions;
61
- `ads check` rejects missing coverage and invented names. Styled token samples
62
- belong to your public review patterns and mount through the same scenario pipeline.
59
+ The existing `tokens/Foundation.scenario.tsx` and colocated fixture visually demonstrate all
60
+ starter token values. The fixture owns `{ token, property, probe }` bindings;
61
+ the public `TokenPreview` pattern owns their presentation and applies the real CSS
62
+ variables. Add/remove samples alongside token definitions. `ads check` rejects
63
+ missing coverage and invented names; the App verifies the declared CSS bindings
64
+ in the browser. Extend or replace the project-owned pattern as your tokens evolve.
63
65
 
64
66
  Create a component under `design-system/components/`, export it through `@ds`,
65
67
  and colocate `Scenario.tsx` plus `fixture.ts` (or `<Owner>.scenario.tsx` and
@@ -2,4 +2,4 @@
2
2
  import "./styles.css";
3
3
 
4
4
  // Add public component and pattern exports here as they are implemented.
5
- export {};
5
+ export { TokenPreview } from "./patterns/token-preview/TokenPreview";
@@ -0,0 +1,4 @@
1
+ import { TokenPreview } from "@ds";
2
+ import { fixture } from "./fixture";
3
+ export const scenario = { title: fixture.title, covers: ["TokenPreview"] };
4
+ export default function Scenario() { return <TokenPreview {...fixture} />; }
@@ -0,0 +1,31 @@
1
+ import { useLayoutEffect, useRef } from "react";
2
+ import { css, cx } from "@linaria/atomic";
3
+
4
+ export interface TokenSample { token: `--${string}`; property: string; }
5
+ export interface TokenPreviewProps { title: string; samples: readonly TokenSample[]; text: string; }
6
+ const sampleStyle = css`
7
+ display: inline-block;
8
+ position: relative;
9
+ white-space: pre-line;
10
+ &[data-ads-property="animation-duration"] { animation-duration: var(--ux-token-preview-value); }
11
+ &[data-ads-property="animation-timing-function"] { animation-timing-function: var(--ux-token-preview-value); }
12
+ &[data-ads-property="background-color"] { background-color: var(--ux-token-preview-value); }
13
+ &[data-ads-property="border-radius"] { border-radius: var(--ux-token-preview-value); }
14
+ &[data-ads-property="box-shadow"] { box-shadow: var(--ux-token-preview-value); }
15
+ &[data-ads-property="color"] { color: var(--ux-token-preview-value); }
16
+ &[data-ads-property="font-family"] { font-family: var(--ux-token-preview-value); }
17
+ &[data-ads-property="font-size"] { font-size: var(--ux-token-preview-value); }
18
+ &[data-ads-property="font-weight"] { font-weight: var(--ux-token-preview-value); }
19
+ &[data-ads-property="line-height"] { line-height: var(--ux-token-preview-value); }
20
+ &[data-ads-property="width"] { width: var(--ux-token-preview-value); }
21
+ &[data-ads-property="z-index"] { z-index: var(--ux-token-preview-value); }
22
+ `;
23
+ function Sample({ sample, text }: { sample: TokenSample; text: string }) {
24
+ const ref = useRef<HTMLSpanElement>(null);
25
+ useLayoutEffect(() => { ref.current?.style.setProperty("--ux-token-preview-value", `var(${sample.token})`); }, [sample.token]);
26
+ return <span ref={ref} className={cx("ux-token-preview__sample", sampleStyle)} data-ads-token={sample.token} data-ads-property={sample.property}>{text}</span>;
27
+ }
28
+ /** Project-owned review UI. Values always come from the real foundation CSS. */
29
+ export function TokenPreview({ title, samples, text }: TokenPreviewProps) {
30
+ return <section className="ux-token-preview"><h1>{title}</h1>{samples.map(sample => <div key={`${sample.token}:${sample.property}`} className="ux-token-preview__item"><code className="ux-token-preview__name">{sample.token}</code><Sample sample={sample} text={text}/></div>)}</section>;
31
+ }
@@ -0,0 +1,8 @@
1
+ export const fixture = {
2
+ "title": "Token preview",
3
+ "text": "Aa",
4
+ samples: [
5
+ {"token": "--accent", "property": "background-color", "probe": "rgb(31, 97, 163)"},
6
+ {"token": "--space-4", "property": "width", "probe": "13.375px"},
7
+ ]
8
+ } as const;