@reqvire-org/ads 0.3.13 → 0.3.15

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 (31) hide show
  1. package/dist/bootstrap-ds/ADS.md +4 -1
  2. package/dist/check.mjs +8 -2
  3. package/dist/mcp/sandbox-host.mjs +1 -1
  4. package/dist/mcp/widget.bundle.js +14 -14
  5. package/dist/scenario-slots.mjs +124 -0
  6. package/dist/showcase/sandbox.bundle.js +1 -0
  7. package/dist/skills/ads-break-ui/SKILL.md +2 -2
  8. package/dist/skills/ads-break-ui/references/guarded-editing.md +14 -3
  9. package/dist/skills/ads-break-ui/references/scenarios.md +46 -1
  10. package/dist/skills/ads-designer/SKILL.md +2 -2
  11. package/dist/skills/ads-designer/assets/bootstrap-ds/ADS.md +4 -1
  12. package/dist/skills/ads-designer/references/guarded-editing.md +14 -3
  13. package/dist/skills/ads-designer/references/scenarios.md +46 -1
  14. package/dist/skills/ads-import/SKILL.md +2 -2
  15. package/dist/skills/ads-import/references/guarded-editing.md +14 -3
  16. package/dist/skills/ads-import/references/scenarios.md +46 -1
  17. package/dist/skills/ads-migrate/SKILL.md +2 -2
  18. package/dist/skills/ads-migrate/references/guarded-editing.md +14 -3
  19. package/dist/skills/ads-migrate/references/scenarios.md +46 -1
  20. package/dist/skills/ads-motion/SKILL.md +2 -2
  21. package/dist/skills/ads-motion/references/guarded-editing.md +14 -3
  22. package/dist/skills/ads-motion/references/scenarios.md +46 -1
  23. package/dist/source-values.mjs +2 -1
  24. package/dist/verification.mjs +8 -3
  25. package/docs/ADS.md +5 -2
  26. package/docs/ARCHITECTURE.md +11 -0
  27. package/docs/BEHAVIORS.md +8 -3
  28. package/docs/CHANGELOG.md +17 -0
  29. package/docs/MCP.md +12 -0
  30. package/package.json +1 -1
  31. package/dist/showcase/sandbox.js +0 -30
@@ -1,6 +1,6 @@
1
1
  # Scenario rendering, token previews and public class helpers
2
2
 
3
- Use this reference for `scenario-render`, `scenario-coverage` or `showcase-class`
3
+ Use this reference for `scenario-render`, `scenario-coverage`, `showcase-control` or `showcase-class`
4
4
  diagnostics. MCP serves the same guide as `ads://guides/scenarios`.
5
5
 
6
6
  All preview kinds open at `?scenario=<discovered-id>`. Use the returned showcase
@@ -23,6 +23,17 @@ on-demand element screenshots. It grants no render/behavior coverage and no extr
23
23
  styling permission. Use [captures.md](captures.md) for current/candidate review,
24
24
  target selection, resource reads and storage lifecycle.
25
25
 
26
+ ## Fragment links inside Apps
27
+
28
+ Keep intended `href="#"` and named fragments in the project API and fixtures.
29
+ ADS's inline-document loader supplies an `about:srcdoc` base when the App has no
30
+ explicit base, preserving native scrolling, events and state. If a fragment
31
+ replaces the App with a sandbox URL or browser error, inspect `location.href`,
32
+ `document.baseURI` and the resolved anchor URL. Report a host loading defect;
33
+ do not add scenario `preventDefault` handlers merely to hide it. Explicit App
34
+ bases retain their declared behavior and CSP requirements; external hosts own
35
+ their document-loading policy. Verify link activation as well as rendering.
36
+
26
37
  ## Owner-local token previews load their own definitions
27
38
 
28
39
  Keep the registered token sheet imported by its runtime owner for product use.
@@ -90,6 +101,40 @@ do not export the helper solely for a test. Public/private ownership is a produc
90
101
  decision, not a coverage workaround. Browser checks verify transitions but do not
91
102
  replace the current static `covers` requirement; `exercises` does not waive it.
92
103
 
104
+ ## Native semantics through a public runtime slot
105
+
106
+ A native element can be the direct child of a public runtime component that
107
+ actually clones that child with its own props:
108
+
109
+ ```tsx
110
+ <Action asChild>
111
+ <a href={fixture.destination}>Documentation</a>
112
+ </Action>
113
+ ```
114
+
115
+ `Action` and the fixture are illustrative; use the project's intended public API.
116
+ The runtime owner supplies presentation and interaction; the anchor supplies link
117
+ semantics. Keep this composition when it is the product contract. Do not replace
118
+ the anchor with an unrelated styled link, add a demo-only prop, disable the guard
119
+ or write outside MCP merely to silence a diagnostic.
120
+
121
+ ADS follows the selected runtime render path, the exact child and real imported
122
+ React/Preact `cloneElement` operations, including local slot/prop-merging helpers.
123
+ The prop need not be named `asChild`; the spelling alone grants no permission.
124
+ JSX and recognized framework factories follow the same rule. The native element
125
+ must be the single direct child: another native control nested inside it remains
126
+ subject to the ordinary ownership rule. Passing children through a container,
127
+ a scenario-local wrapper or an opaque component does not establish a slot.
128
+
129
+ This grants no scenario styling permission. Inline styles and arbitrary class
130
+ names remain rejected, including on the slotted child. Public style helpers retain
131
+ their own provenance checks. Unknown branches that could render the native child
132
+ directly, multiple children and untraceable replacement props remain blocked.
133
+ If a valid slot contract cannot be traced, keep the intended API and report a
134
+ small reproduction rather than inventing a product workaround. Static proof
135
+ establishes ownership, not correct prop merging, focus, keyboard or navigation
136
+ behavior; verify those in the built App and registered checks.
137
+
93
138
  ## Scenarios may consume verified public style helpers
94
139
 
95
140
  A deliberately public helper can expose its owner's Linaria treatment:
@@ -131,6 +131,7 @@ export function createSourceValues(program, options, { preserveCall = () => fals
131
131
  function keys(value, context) {
132
132
  value = resolve(value, next(context));
133
133
  if (value.kind === "union") return [...new Set(value.values.flatMap(item => keys(item, next(context))))];
134
+ if (value.kind === "rest") return keys(value.value, next(context)).filter(key => !value.excluded.has(key));
134
135
  if (value.kind === "node" && ts.isObjectLiteralExpression(value.node)) return value.node.properties.flatMap(property => ts.isSpreadAssignment(property) ? keys(reference(property.expression, value.env, value.via), next(context)) : nameOf(property.name) ?? []);
135
136
  if (value.kind === "object") return [...new Set(value.layers.flatMap(layer => layer instanceof Map ? [...layer.keys()] : keys(layer, next(context))))];
136
137
  return [];
@@ -243,5 +244,5 @@ export function createSourceValues(program, options, { preserveCall = () => fals
243
244
  function props(node, env = new Map(), via = []) {
244
245
  return { kind: "object", layers: node.attributes.properties.map(attribute => ts.isJsxSpreadAttribute(attribute) ? reference(attribute.expression, env, via) : new Map([[attribute.name.text, attribute.initializer ? ts.isJsxExpression(attribute.initializer) ? reference(attribute.initializer.expression, env, via) : reference(attribute.initializer, env, via) : literal(true)]])) };
245
246
  }
246
- return { state, next, at, resolve, get, arrays, returns, props, declaration };
247
+ return { state, next, at, resolve, get, keys, arrays, returns, props, declaration };
247
248
  }
@@ -289,15 +289,20 @@ export function createVerificationController(root, { reportRoot = root, onProgre
289
289
  diagnostics.push({ rule: "behavior-runner", message: `${runnerError.message} ${detail}`, reason: runnerError.reason, elapsedMs: runnerError.elapsedMs ?? Date.now() - startedAt, progress, output: runnerError.output?.slice(-4000), profile: profileName });
290
290
  }
291
291
  const suiteErrors = Array.isArray(reportValue?.errors) ? reportValue.errors.slice(0, 5).map(error => String(error).slice(0, 1000)) : [];
292
- const runnerFailure = run && (run.code !== 0 || suiteErrors.length || reportValue?.complete === false) ? `Registered runner ${profileName} ${run.code !== 0 ? `exited with code ${run.code ?? "unknown"}` : reportValue?.complete === false ? "left an incomplete report" : "reported a suite error"}. ${suiteErrors.join("; ") || reportValue?.cases.filter(item => item?.status === "failed").slice(0, 3).map(item => `${item.id}: ${item.message || "failed without a case diagnostic"}`).join("; ") || "Inspect the runner output and verification report."}`.slice(0, 3000) : undefined;
292
+ const incompleteReport = reportValue?.complete === false || progress.pending > 0 || progress.active.length > 0;
293
+ // Exit 1 is the normal test-failure exit. A complete report with actual
294
+ // assertion failures still contains independent, valid passing outcomes.
295
+ // Unexplained exits, suite errors and interrupted reports grant no pass.
296
+ const caseFailureExit = run?.code === 1 && !runnerError && !suiteErrors.length && !incompleteReport && reportValue?.cases.some(item => item?.status === "failed");
297
+ const runnerFailure = run && (run.code !== 0 || suiteErrors.length || incompleteReport) ? `Registered runner ${profileName} ${run.code !== 0 ? `exited with code ${run.code ?? "unknown"}` : incompleteReport ? "left an incomplete report" : "reported a suite error"}. ${suiteErrors.join("; ") || reportValue?.cases.filter(item => item?.status === "failed").slice(0, 3).map(item => `${item.id}: ${item.message || "failed without a case diagnostic"}`).join("; ") || "Inspect the runner output and verification report."}`.slice(0, 3000) : undefined;
293
298
  if (runnerFailure) diagnostics.push({ rule: "behavior-runner", message: runnerFailure, output: run.output?.slice(-4000), profile: profileName });
294
299
  const afterOptions = loadConfig(root), afterContext = inputContext(afterOptions);
295
300
  for (const check of group) {
296
301
  const file = slash(path.relative(root, path.resolve(root, path.dirname(check.contract.file), check.file)));
297
302
  const matches = reportValue?.cases.filter(item => item && item.id === check.case && item.file === file) ?? [];
298
303
  const reportedStatus = matches.length && matches.every(item => item.status === "passed") ? "passed" : matches.length && matches.every(item => item.status === "skipped") ? "skipped" : "failed";
299
- const interrupted = !!runnerError && !!reportValue;
300
- let status = interrupted ? matches.some(item => item.status === "failed") ? "failed" : "incomplete" : !run || runnerError || runnerFailure ? "failed" : reportedStatus;
304
+ const blockedRun = !!runnerError || !!runnerFailure && !caseFailureExit;
305
+ let status = matches.some(item => item.status === "failed") ? "failed" : !matches.length || blockedRun ? reportValue ? "incomplete" : "failed" : reportedStatus;
301
306
  if (runnerError?.code === "ENOENT") status = "unavailable";
302
307
  const current = behaviorInputs(afterOptions, check.contract, check, afterContext).revision === check.inputRevision;
303
308
  if (!current) status = "stale";
package/docs/ADS.md CHANGED
@@ -164,7 +164,10 @@ or applies the right CSS property. Browser review still verifies the samples.
164
164
 
165
165
  Scenarios and fixture helpers are development-only wherever colocated. Runtime
166
166
  source and the public barrel cannot import or re-export them. Showcase/scenario
167
- code defines no CSS, custom classes or native controls; use public ADS APIs.
167
+ code defines no CSS, custom classes or standalone native controls; use public ADS
168
+ APIs. A single direct native child can supply semantics to a public runtime slot
169
+ when source tracing proves that owner clones the exact child with its own props.
170
+ An `asChild` flag alone is not evidence; scenario styling remains forbidden.
168
171
  Scenarios may consume public class helpers whose returned styles trace to owned
169
172
  Linaria. Stable hooks in that owner may accompany real styles; strings forwarded
170
173
  from scenarios, hook-only returns and untraceable outputs remain invalid.
@@ -383,7 +386,7 @@ The checks enforce the architecture's ownership boundaries:
383
386
  | Public API and dependencies | Consumers use public ADS imports; ADS implementations and showcase do not import application source; runtime code does not import showcase fixtures |
384
387
  | Interaction policy | Primitive state variables stay inside components; non-state customization hooks require explicit registration |
385
388
  | Renderer exceptions | Inline styles and imperative writes require used, narrow configured boundaries; stale entries fail; public props omit inherited style and reject open-ended visual strings |
386
- | Preview ownership | Scenarios define no CSS, custom classes, or native controls; all declared tokens and public renderable UI have reachable registered scenarios; token references must exist; legacy catalogue pages are checked when present |
389
+ | Preview ownership | Scenarios define no CSS, custom classes, or standalone native controls; native slot children require public runtime cloning evidence; all declared tokens and public renderable UI have reachable registered scenarios; token references must exist; legacy catalogue pages are checked when present |
387
390
  | Assets | Source/public collisions and configured generated artifacts in source are rejected |
388
391
 
389
392
  Pointer capture without locally visible capture-loss cleanup produces a
@@ -213,6 +213,13 @@ Project JavaScript, styles, fonts and UI run only inside that App; the outer
213
213
  shell forwards resource bytes without applying project styles or executing its
214
214
  runtime. This keeps the host neutral while compositions retain their full skin.
215
215
 
216
+ The ADS host gives inline App documents an `about:srcdoc` base when the App
217
+ provides no explicit base URL. Fragment links then stay inside their own document
218
+ with native scrolling, history and events. The sandbox permits this local base;
219
+ external bases still require declared CSP origins. This belongs to document
220
+ loading and changes no project link or scenario handler. External hosts control
221
+ their own document-loading and CSP behavior.
222
+
216
223
  The project compiler publishes a versioned dependency graph: shared runtime and
217
224
  foundation assets, plus independently addressable scenario modules. Inside the
218
225
  App, a resource loader obtains the selected dependency closure through MCP,
@@ -303,6 +310,10 @@ Opaque conditional branches do not grant speculative coverage. Scenarios may
303
310
  consume deliberate public class helpers when their returned styles retain
304
311
  runtime-owned Linaria provenance; stable owner-authored hooks may accompany real
305
312
  styles. This preserves ownership rather than granting scenario styling freedom.
313
+ A native element may provide semantics as the single direct child of a public
314
+ runtime slot when source tracing proves it is cloned with that owner's props.
315
+ This preserves polymorphic composition without allowing standalone scenario
316
+ controls, scenario styling or name-based exceptions.
306
317
  The skill-local [scenario guide](../skills/ads-designer/references/scenarios.md)
307
318
  explains evidence, limits and diagnostics.
308
319
 
package/docs/BEHAVIORS.md CHANGED
@@ -159,9 +159,14 @@ Prefer readiness/assertion polling over arbitrary sleeps. Timer-only guarantees
159
159
  may use [Playwright's clock](https://playwright.dev/docs/clock); retain real time
160
160
  when browser motion, pointer gestures or layout timing are what the check verifies.
161
161
 
162
- Runner failures remain blocking even when some selected cases reported passed.
163
- Those results retain `reportedStatus` and `runnerExitCode`, and explain the suite
164
- failure rather than returning an empty message. Suite-level browser errors and
162
+ An ordinary test-failure exit (code 1) with a complete report and actual failing
163
+ cases preserves independent case outcomes: passing checks stay `passed`, failed
164
+ checks stay `failed`, and overall verification still blocks the mutation. The
165
+ report must account for every selected check, with no active cases or suite errors.
166
+ The same rule supports older custom reports without an explicit `complete` field
167
+ after normal process completion. An unexplained nonzero exit cannot certify a
168
+ pass, even if all reported cases claim success. Results retain `reportedStatus`
169
+ and `runnerExitCode` and explain the overall failure. Suite-level browser errors and
165
170
  bounded runner output appear in diagnostics. `openBehaviorScenario` includes the
166
171
  scenario ID and failed browser requests in startup failures; App resource errors
167
172
  identify the resource URI. Failed assertions and startup errors are not retried
package/docs/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.15
4
+
5
+ - Keep fragment links inside inline preview documents by supplying a local
6
+ `about:srcdoc` base in the sandbox host and legacy nested widget. Preserve
7
+ explicit App bases, native link events and opaque origins; retain CSP gates
8
+ for external bases. Verify scrolling, state and bridge continuity in browsers.
9
+
10
+ ## 0.3.14
11
+
12
+ - Preserve independent passing behavior outcomes in complete runs with failed
13
+ assertions while keeping overall verification and mutations blocked. Retain
14
+ incomplete status for missing results, suite errors and unexplained exits.
15
+ - Recognize native scenario children through source-verified public runtime slot
16
+ composition, including React/Preact cloning and local prop mergers. Keep
17
+ standalone controls, custom scenario styling and unproven slot paths rejected;
18
+ update diagnostics, architecture and portable skill guidance generically.
19
+
3
20
  ## 0.3.13
4
21
 
5
22
  - Add on-demand scenario/named-element PNG captures and screenshot-enabled guarded
package/docs/MCP.md CHANGED
@@ -270,6 +270,11 @@ retain partial outcomes and reports under `.ds/verification/`, but incomplete
270
270
  runs still block application and cannot establish passing guarantees. See the
271
271
  [runner and evidence details](BEHAVIORS.md#register-execution-profiles).
272
272
 
273
+ A complete ordinary failed run preserves the actual mixed case outcomes; a
274
+ single failed assertion does not turn the other checks into failures. Overall
275
+ verification and guarded application still fail. Suite errors, unexplained exits,
276
+ missing results and interrupted reports cannot certify passing checks.
277
+
273
278
  Browser profiles start an isolated showcase and verify its sandboxed App using
274
279
  the selected candidate's sources. StrictMode profiles need a project React
275
280
  development harness. Profiles describe the execution environment, not a proof
@@ -588,6 +593,13 @@ origin hosts the sandbox proxy; that proxy creates an opaque inner App iframe
588
593
  and forwards the standard Apps bridge messages. The diagnostic explorer follows
589
594
  the same sandbox model. Only the App materializes and executes project resources.
590
595
 
596
+ For inline documents without an explicit App base URL, ADS supplies
597
+ `<base href="about:srcdoc">` and permits the local `about:` base scheme in CSP.
598
+ Bare and named fragment links retain native in-document navigation and state.
599
+ Explicit App bases remain subject to declared CSP origins. The legacy nested
600
+ widget uses the same document preparation. External hosts own this behavior in
601
+ their loaders; adding scenario click handlers would conceal a host defect.
602
+
591
603
  Hosts need Apps support, server resource forwarding, native ESM/import maps and
592
604
  a CSP permitting declared `blob:` script/style/font/worker resources. Project
593
605
  previews declare no network connections. The host may enforce stricter policy;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reqvire-org/ads",
3
- "version": "0.3.13",
3
+ "version": "0.3.15",
4
4
  "type": "module",
5
5
  "description": "A design system your agent can see and shape. Your components. Your look. Your code.",
6
6
  "repository": {
@@ -1,30 +0,0 @@
1
- const parentOrigin = new URL(location.href).searchParams.get("parent");
2
- let view;
3
- function domains(values = []) {
4
- return values.filter(value => /^(?:https?:\/\/[a-z0-9.*:[\]-]+(?::\d+)?|data:|blob:|about:)$/i.test(value)).join(" ");
5
- }
6
- function policy(csp = {}) {
7
- const resources = domains(csp.resourceDomains);
8
- return `default-src 'none';script-src 'unsafe-inline' ${resources};style-src 'unsafe-inline' ${resources};img-src data: ${resources};font-src ${resources || "'none'"};media-src ${resources || "'none'"};worker-src ${resources || "'none'"};connect-src ${domains(csp.connectDomains) || "'none'"};frame-src ${domains(csp.frameDomains) || "'none'"};base-uri ${domains(csp.baseUriDomains) || "'none'"};object-src 'none'`;
9
- }
10
- window.addEventListener("message", event => {
11
- if (event.source === parent && event.origin === parentOrigin) {
12
- const message = event.data;
13
- if (message?.method === "ui/notifications/sandbox-resource-ready") {
14
- const { html, csp } = message.params ?? {};
15
- if (typeof html !== "string") return;
16
- const document = new DOMParser().parseFromString(html, "text/html");
17
- const meta = document.createElement("meta");
18
- meta.httpEquiv = "Content-Security-Policy";
19
- meta.content = policy(csp);
20
- document.head.prepend(meta);
21
- view?.remove();
22
- view = window.document.createElement("iframe");
23
- view.title = "Design-system App";
24
- view.setAttribute("sandbox", "allow-scripts");
25
- window.document.body.append(view);
26
- view.srcdoc = "<!doctype html>" + document.documentElement.outerHTML;
27
- } else if (message?.jsonrpc === "2.0" && !message.method?.startsWith("ui/notifications/sandbox-")) view?.contentWindow.postMessage(message, "*");
28
- } else if (event.source === view?.contentWindow && event.data?.jsonrpc === "2.0" && !event.data.method?.startsWith("ui/notifications/sandbox-")) parent.postMessage(event.data, parentOrigin);
29
- });
30
- parent.postMessage({ jsonrpc: "2.0", method: "ui/notifications/sandbox-proxy-ready", params: {} }, parentOrigin);