@reqvire-org/ads 0.3.13 → 0.3.14
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/dist/bootstrap-ds/ADS.md +4 -1
- package/dist/check.mjs +8 -2
- package/dist/scenario-slots.mjs +124 -0
- package/dist/skills/ads-break-ui/references/guarded-editing.md +14 -3
- package/dist/skills/ads-break-ui/references/scenarios.md +35 -1
- package/dist/skills/ads-designer/assets/bootstrap-ds/ADS.md +4 -1
- package/dist/skills/ads-designer/references/guarded-editing.md +14 -3
- package/dist/skills/ads-designer/references/scenarios.md +35 -1
- package/dist/skills/ads-import/references/guarded-editing.md +14 -3
- package/dist/skills/ads-import/references/scenarios.md +35 -1
- package/dist/skills/ads-migrate/references/guarded-editing.md +14 -3
- package/dist/skills/ads-migrate/references/scenarios.md +35 -1
- package/dist/skills/ads-motion/references/guarded-editing.md +14 -3
- package/dist/skills/ads-motion/references/scenarios.md +35 -1
- package/dist/source-values.mjs +2 -1
- package/dist/verification.mjs +8 -3
- package/docs/ADS.md +5 -2
- package/docs/ARCHITECTURE.md +4 -0
- package/docs/BEHAVIORS.md +8 -3
- package/docs/CHANGELOG.md +10 -0
- package/docs/MCP.md +5 -0
- package/package.json +1 -1
package/dist/bootstrap-ds/ADS.md
CHANGED
|
@@ -197,7 +197,10 @@ or applies the right CSS property. Browser review still verifies the samples.
|
|
|
197
197
|
|
|
198
198
|
Scenarios and fixture helpers are development-only wherever colocated. Runtime
|
|
199
199
|
source and the public barrel cannot import or re-export them. Showcase/scenario
|
|
200
|
-
code defines no CSS, custom classes or native controls; use public ADS
|
|
200
|
+
code defines no CSS, custom classes or standalone native controls; use public ADS
|
|
201
|
+
APIs. A single direct native child can supply semantics to a public runtime slot
|
|
202
|
+
when source tracing proves that owner clones the exact child with its own props.
|
|
203
|
+
An `asChild` flag alone is not evidence; scenario styling remains forbidden.
|
|
201
204
|
Neither ADS implementations nor scenarios import application code. Live business
|
|
202
205
|
data belongs to application adapters; ADS receives inputs or injected callbacks.
|
|
203
206
|
Direct global `fetch`, XHR, WebSocket and EventSource use is rejected. Renderer
|
package/dist/check.mjs
CHANGED
|
@@ -21,6 +21,7 @@ import { foundationDeclaration, foundationSelector } from "./foundation.mjs";
|
|
|
21
21
|
import { scopedTokens, scopedTokenOwner } from "./scoped-tokens.mjs";
|
|
22
22
|
import { createAtomicCompositionReview } from "./atomic-composition.mjs";
|
|
23
23
|
import { createScenarioTrace } from "./scenario-trace.mjs";
|
|
24
|
+
import { createScenarioSlots } from "./scenario-slots.mjs";
|
|
24
25
|
|
|
25
26
|
const scenarioLayoutGuidance = "Put demo-only sizing and framing in a public review/layout pattern. Keep required behavior in the runtime owner; add component props for deliberate product API needs.";
|
|
26
27
|
const inlineStyleGuidance = `Own visual styles in component/pattern Linaria. ${scenarioLayoutGuidance} Reserve inlineStyleFiles for documented measured geometry, not demo layout.`;
|
|
@@ -54,6 +55,7 @@ export function checkProject(directory, { trackingRoot } = {}) {
|
|
|
54
55
|
const checker = program.getTypeChecker();
|
|
55
56
|
const atomicCompositionReview = createAtomicCompositionReview(program, options);
|
|
56
57
|
const scenarioTrace = createScenarioTrace(program, options);
|
|
58
|
+
let scenarioSlot;
|
|
57
59
|
function report(file, rule, message, line = 1, column = 1) {
|
|
58
60
|
diagnostics.push({ file: path.relative(root, file).replaceAll("\\", "/"), rule, message, line, column });
|
|
59
61
|
}
|
|
@@ -168,6 +170,10 @@ export function checkProject(directory, { trackingRoot } = {}) {
|
|
|
168
170
|
const proof = scenarioTrace.classValue(expression, property);
|
|
169
171
|
if (proof.present && !proof.ok) nodeReport(node, "showcase-class", `Scenarios define no custom class names. A public class helper must return runtime-owned Linaria styles; stable hooks authored in that owner may accompany real styles. Arbitrary strings, private styles and forwarded consumer classes do not qualify.${proof.reasons.length ? ` ${proof.reasons.join(" ")}` : ""} ${scenarioLayoutGuidance} See ads://guides/scenarios.`);
|
|
170
172
|
}
|
|
173
|
+
function scenarioControl(node) {
|
|
174
|
+
const proof = (scenarioSlot ??= createScenarioSlots(program, options))(node);
|
|
175
|
+
if (!proof.ok) nodeReport(node, "showcase-control", `Scenario controls must render public ADS components for actual interaction. A native direct child may provide semantics only when source tracing proves its public runtime parent clones that child with owned props; an asChild prop alone grants no permission.${proof.reasons.length ? ` ${proof.reasons.join(" ")}` : ""} ${scenarioLayoutGuidance} See ads://guides/scenarios.`);
|
|
176
|
+
}
|
|
171
177
|
if (!showcase && (isInside(path.join(dsRoot, "components"), file) || isInside(path.join(dsRoot, "patterns"), file))) inspectPublicProps(source, checker, nodeReport);
|
|
172
178
|
function isLinariaTag(tag) {
|
|
173
179
|
if (ts.isCallExpression(tag)) tag = tag.expression;
|
|
@@ -225,7 +231,7 @@ export function checkProject(directory, { trackingRoot } = {}) {
|
|
|
225
231
|
if (showcase) for (const property of properties) if (ts.isSpreadAssignment(property)) scenarioClass(property, property.expression, true);
|
|
226
232
|
if (showcase && tag && ts.isStringLiteralLike(tag)) {
|
|
227
233
|
const role = properties.find(property => ts.isPropertyAssignment(property) && (ts.isIdentifier(property.name) || ts.isStringLiteralLike(property.name)) && property.name.text === "role")?.initializer;
|
|
228
|
-
if (showcaseControls.has(tag.text) || role && ts.isStringLiteralLike(role) && widgetRoles.has(role.text))
|
|
234
|
+
if (showcaseControls.has(tag.text) || role && ts.isStringLiteralLike(role) && widgetRoles.has(role.text)) scenarioControl(node);
|
|
229
235
|
}
|
|
230
236
|
}
|
|
231
237
|
if (isInside(dsRoot, file) && (ts.isIdentifier(node) && ["fetch", "XMLHttpRequest", "WebSocket", "EventSource"].includes(node.text) && !(ts.isPropertyAccessExpression(node.parent) && node.parent.name === node))) {
|
|
@@ -236,7 +242,7 @@ export function checkProject(directory, { trackingRoot } = {}) {
|
|
|
236
242
|
if (showcase && (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) && ts.isIdentifier(node.tagName) && /^[a-z]/.test(node.tagName.text)) {
|
|
237
243
|
const role = node.attributes.properties.find((attr) => ts.isJsxAttribute(attr) && attr.name.text === "role")?.initializer;
|
|
238
244
|
const roleValue = role && ts.isStringLiteralLike(role) ? role.text : role && ts.isJsxExpression(role) && role.expression && ts.isStringLiteralLike(role.expression) ? role.expression.text : null;
|
|
239
|
-
if (showcaseControls.has(node.tagName.text) || widgetRoles.has(roleValue))
|
|
245
|
+
if (showcaseControls.has(node.tagName.text) || widgetRoles.has(roleValue)) scenarioControl(node);
|
|
240
246
|
}
|
|
241
247
|
if (showcase && ts.isJsxAttribute(node) && ts.isIdentifier(node.name) && node.name.text === "className") scenarioClass(node, node.initializer && ts.isJsxExpression(node.initializer) ? node.initializer.expression : node.initializer);
|
|
242
248
|
if (showcase && ts.isJsxSpreadAttribute(node)) scenarioClass(node, node.expression, true);
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { isDevelopmentFile } from "./config.mjs";
|
|
3
|
+
import { publicApi } from "./public-api.mjs";
|
|
4
|
+
import { createSourceValues, isFunction, reference, unwrap } from "./source-values.mjs";
|
|
5
|
+
import { uiOperation } from "./ui-framework.mjs";
|
|
6
|
+
|
|
7
|
+
/** A native scenario child may supply semantics to a public runtime slot. The
|
|
8
|
+
* proof follows actual child cloning, never a prop/component/helper spelling. */
|
|
9
|
+
export function createScenarioSlots(program, options) {
|
|
10
|
+
const checker = program.getTypeChecker(), cache = new WeakMap();
|
|
11
|
+
const operation = node => uiOperation(node, checker);
|
|
12
|
+
const values = createSourceValues(program, options, { strict: true, preserveCall: node => ["createElement", "cloneElement", "Children.only"].includes(operation(node)) });
|
|
13
|
+
const { state, next, resolve, get, keys, props, returns, declaration } = values;
|
|
14
|
+
const publicNodes = new Set(publicApi(program, options).filter(item => item.renderable).map(item => resolve(reference(item.declaration.name ?? item.declaration), state())).filter(value => value.kind === "node").map(value => value.node));
|
|
15
|
+
|
|
16
|
+
function element(node, env = new Map(), via = []) {
|
|
17
|
+
if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node)) {
|
|
18
|
+
const opening = ts.isJsxElement(node) ? node.openingElement : node;
|
|
19
|
+
const attributes = props(opening, env, via);
|
|
20
|
+
const children = ts.isJsxElement(node) ? node.children.filter(child => !ts.isJsxText(child) || child.text.trim()).filter(child => !ts.isJsxExpression(child) || child.expression) : [];
|
|
21
|
+
if (children.length) attributes.layers.push(new Map([["children", children.length === 1 ? reference(children[0], env, via) : { kind: "array", values: children.map(child => reference(child, env, via)) }]]));
|
|
22
|
+
return { tag: reference(opening.tagName, env, via), attributes, intrinsic: ts.isIdentifier(opening.tagName) && /^[a-z]/.test(opening.tagName.text) };
|
|
23
|
+
}
|
|
24
|
+
if (ts.isCallExpression(node) && operation(node.expression) === "createElement") {
|
|
25
|
+
const attributes = { kind: "object", layers: node.arguments[1] && node.arguments[1].kind !== ts.SyntaxKind.NullKeyword ? [reference(node.arguments[1], env, via)] : [] };
|
|
26
|
+
const children = node.arguments.slice(2).map(child => reference(child, env, via));
|
|
27
|
+
if (children.length) attributes.layers.push(new Map([["children", children.length === 1 ? children[0] : { kind: "array", values: children }]]));
|
|
28
|
+
return { tag: reference(node.arguments[0], env, via), attributes, intrinsic: !!node.arguments[0] && ts.isStringLiteralLike(node.arguments[0]) };
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return function nativeSlot(site) {
|
|
34
|
+
if (cache.has(site)) return cache.get(site);
|
|
35
|
+
const reasons = new Set(); let cloned = false, exposed = false, uncertain = false, steps = 0;
|
|
36
|
+
const fail = reason => { uncertain = true; reasons.add(reason); };
|
|
37
|
+
function childValue(input, context) {
|
|
38
|
+
const value = resolve(input, next(context));
|
|
39
|
+
if (value.kind === "node" && ts.isJsxExpression(value.node)) return childValue(reference(value.node.expression, value.env, value.via), next(context));
|
|
40
|
+
if (value.kind === "node" && ts.isCallExpression(value.node) && operation(value.node.expression) === "Children.only") return childValue(reference(value.node.arguments[0], value.env, value.via), next(context));
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
function ownedProps(input, context, seen = new Set()) {
|
|
44
|
+
if (context.depth > 40) return false;
|
|
45
|
+
const value = resolve(input, next(context));
|
|
46
|
+
const opaque = value.kind === "unknown" || value.kind === "union" && value.values.length <= 32 && value.values.every(branch => resolve(branch, next(context)).kind === "unknown");
|
|
47
|
+
if (value.kind === "union" && !opaque) return value.values.length <= 32 && value.values.every(branch => ownedProps(branch, next(context), new Set(seen)));
|
|
48
|
+
if (keys(value, next(context)).some(key => key !== "children")) return true;
|
|
49
|
+
// Local prop mergers may use loops and a ref assignment, beyond the value
|
|
50
|
+
// evaluator. Retain runtime ownership through their actual input props;
|
|
51
|
+
// a known empty result, external call or arbitrary identifier cannot qualify.
|
|
52
|
+
if (!opaque || input.kind !== "node") return false;
|
|
53
|
+
const node = unwrap(input.node), { env, via } = input;
|
|
54
|
+
if (seen.has(node)) return false;
|
|
55
|
+
seen.add(node);
|
|
56
|
+
if (ts.isIdentifier(node)) {
|
|
57
|
+
if (env.has(checker.getSymbolAtLocation(node))) return ownedProps(env.get(checker.getSymbolAtLocation(node)), next(context), seen);
|
|
58
|
+
const binding = declaration(node);
|
|
59
|
+
return !!(binding && ts.isVariableDeclaration(binding) && binding.parent.flags & ts.NodeFlags.Const && binding.initializer && !isDevelopmentFile(binding.getSourceFile().fileName, options) && ownedProps(reference(binding.initializer, env, via), next(context), seen));
|
|
60
|
+
}
|
|
61
|
+
if (ts.isCallExpression(node)) {
|
|
62
|
+
const target = resolve(reference(node.expression, env, via), next(context));
|
|
63
|
+
return target.kind === "node" && isFunction(target.node) && !isDevelopmentFile(target.node.getSourceFile().fileName, options) && node.arguments.some(argument => ownedProps(reference(argument, env, via), next(context), new Set(seen)));
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
function render(input, context) {
|
|
68
|
+
if (++steps > 500 || context.depth > 48) { fail("Slot tracing reached its source work limit."); return; }
|
|
69
|
+
const value = childValue(input, context);
|
|
70
|
+
context = value.context ?? input.context ?? context;
|
|
71
|
+
if (value.kind === "slot-child") { exposed = true; return; }
|
|
72
|
+
if (["union", "array"].includes(value.kind)) {
|
|
73
|
+
if (value.values.length > 32) { fail("Slot tracing reached its alternative limit."); return; }
|
|
74
|
+
for (const child of value.values) render(child, next(context));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (value.kind === "unknown") { fail(value.reason || "The runtime slot's render path could not be traced."); return; }
|
|
78
|
+
if (value.kind !== "node") return;
|
|
79
|
+
const { node, env, via } = value;
|
|
80
|
+
if (ts.isJsxFragment(node)) { for (const child of node.children) render(reference(child, env, via), next(context)); return; }
|
|
81
|
+
if (ts.isArrayLiteralExpression(node)) { for (const child of node.elements) render(reference(ts.isSpreadElement(child) ? child.expression : child, env, via), next(context)); return; }
|
|
82
|
+
if (ts.isCallExpression(node) && operation(node.expression) === "cloneElement") {
|
|
83
|
+
const child = childValue(reference(node.arguments[0], env, via), next(context));
|
|
84
|
+
if (child.kind === "slot-child") {
|
|
85
|
+
if (!node.arguments[1] || !ownedProps(reference(node.arguments[1], env, via), state())) fail("A slot must clone its child with runtime-owned props.");
|
|
86
|
+
else cloned = true;
|
|
87
|
+
} else render(child, next(context));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const view = element(node, env, via);
|
|
91
|
+
if (!view) return;
|
|
92
|
+
function component(input) {
|
|
93
|
+
const target = resolve(input, next(context));
|
|
94
|
+
if (target.kind === "union") {
|
|
95
|
+
if (target.values.length > 32) { fail("Slot tracing reached its alternative limit."); return; }
|
|
96
|
+
for (const branch of target.values) component(branch);
|
|
97
|
+
} else if (view.intrinsic || ["provider", "context"].includes(target.kind) || target.kind === "literal" && typeof target.value === "string") render(get(view.attributes, "children", next(context)), next(context));
|
|
98
|
+
else if (target.kind === "node" && isFunction(target.node) && !isDevelopmentFile(target.node.getSourceFile().fileName, options)) {
|
|
99
|
+
for (const output of returns(target, [view.attributes], next(context), "component")) render(output, next(context));
|
|
100
|
+
} else fail("The runtime slot's component target could not be traced.");
|
|
101
|
+
}
|
|
102
|
+
component(view.tag);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const native = ts.isJsxOpeningElement(site) ? site.parent : site;
|
|
106
|
+
let child = native;
|
|
107
|
+
while (child.parent && (ts.isJsxExpression(child.parent) || ts.isParenthesizedExpression(child.parent) || ts.isAsExpression(child.parent) || ts.isSatisfiesExpression(child.parent) || ts.isNonNullExpression(child.parent))) child = child.parent;
|
|
108
|
+
let parent = child.parent;
|
|
109
|
+
if (parent && ts.isJsxAttribute(parent) && parent.name.text === "children") parent = parent.parent.parent;
|
|
110
|
+
if (parent && ts.isPropertyAssignment(parent) && parent.name.getText().replace(/^["']|["']$/g, "") === "children" && ts.isObjectLiteralExpression(parent.parent) && ts.isCallExpression(parent.parent.parent) && parent.parent.parent.arguments[1] === parent.parent) parent = parent.parent.parent;
|
|
111
|
+
const view = parent && element(parent), context = state();
|
|
112
|
+
if (view) {
|
|
113
|
+
const target = resolve(view.tag, next(context));
|
|
114
|
+
const original = childValue(get(view.attributes, "children", next(context)), next(context));
|
|
115
|
+
if (target.kind === "node" && publicNodes.has(target.node) && original.kind === "node" && original.node === native) {
|
|
116
|
+
view.attributes.layers.push(new Map([["children", { kind: "slot-child" }]]));
|
|
117
|
+
for (const output of returns(target, [view.attributes], next(context), "component")) render(output, next(context));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const result = { ok: cloned && !exposed && !uncertain, reasons: [...reasons].slice(0, 2) };
|
|
121
|
+
cache.set(site, result);
|
|
122
|
+
return result;
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -107,6 +107,17 @@ Keep contextual public parts public when they are intended consumer API. Demonst
|
|
|
107
107
|
them through their actual public composition/customization path and list their real
|
|
108
108
|
render references in covers. Do not count internal production renders as public API
|
|
109
109
|
coverage. Behavioral APIs use exercises and registered guarantees instead.
|
|
110
|
-
Scenario controls still use public UI
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
Scenario controls still use public UI. A native link/button can supply semantics
|
|
111
|
+
as the single direct child of a public runtime slot when source tracing proves
|
|
112
|
+
that owner clones the child with its props. An `asChild` flag alone is insufficient;
|
|
113
|
+
standalone native controls and custom scenario styling remain rejected. See
|
|
114
|
+
[scenarios.md](scenarios.md) for evidence and limits. Preserve the intended
|
|
115
|
+
composition instead of substituting an unrelated styled control or adding a
|
|
116
|
+
demo-only component prop. Review framing belongs in public layout patterns.
|
|
117
|
+
|
|
118
|
+
A complete runner report containing an ordinary failed assertion preserves each
|
|
119
|
+
passing check as `passed`; the failed check remains `failed`, and overall
|
|
120
|
+
verification still rejects the mutation. Fix the actual failing checks instead of
|
|
121
|
+
assuming every affected owner failed. Suite errors, unexplained runner exits,
|
|
122
|
+
missing results and interrupted runs cannot certify passing guarantees. Use the
|
|
123
|
+
case's `reportedStatus`, message and report to distinguish these conditions.
|
|
@@ -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
|
|
@@ -90,6 +90,40 @@ do not export the helper solely for a test. Public/private ownership is a produc
|
|
|
90
90
|
decision, not a coverage workaround. Browser checks verify transitions but do not
|
|
91
91
|
replace the current static `covers` requirement; `exercises` does not waive it.
|
|
92
92
|
|
|
93
|
+
## Native semantics through a public runtime slot
|
|
94
|
+
|
|
95
|
+
A native element can be the direct child of a public runtime component that
|
|
96
|
+
actually clones that child with its own props:
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
<Action asChild>
|
|
100
|
+
<a href={fixture.destination}>Documentation</a>
|
|
101
|
+
</Action>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`Action` and the fixture are illustrative; use the project's intended public API.
|
|
105
|
+
The runtime owner supplies presentation and interaction; the anchor supplies link
|
|
106
|
+
semantics. Keep this composition when it is the product contract. Do not replace
|
|
107
|
+
the anchor with an unrelated styled link, add a demo-only prop, disable the guard
|
|
108
|
+
or write outside MCP merely to silence a diagnostic.
|
|
109
|
+
|
|
110
|
+
ADS follows the selected runtime render path, the exact child and real imported
|
|
111
|
+
React/Preact `cloneElement` operations, including local slot/prop-merging helpers.
|
|
112
|
+
The prop need not be named `asChild`; the spelling alone grants no permission.
|
|
113
|
+
JSX and recognized framework factories follow the same rule. The native element
|
|
114
|
+
must be the single direct child: another native control nested inside it remains
|
|
115
|
+
subject to the ordinary ownership rule. Passing children through a container,
|
|
116
|
+
a scenario-local wrapper or an opaque component does not establish a slot.
|
|
117
|
+
|
|
118
|
+
This grants no scenario styling permission. Inline styles and arbitrary class
|
|
119
|
+
names remain rejected, including on the slotted child. Public style helpers retain
|
|
120
|
+
their own provenance checks. Unknown branches that could render the native child
|
|
121
|
+
directly, multiple children and untraceable replacement props remain blocked.
|
|
122
|
+
If a valid slot contract cannot be traced, keep the intended API and report a
|
|
123
|
+
small reproduction rather than inventing a product workaround. Static proof
|
|
124
|
+
establishes ownership, not correct prop merging, focus, keyboard or navigation
|
|
125
|
+
behavior; verify those in the built App and registered checks.
|
|
126
|
+
|
|
93
127
|
## Scenarios may consume verified public style helpers
|
|
94
128
|
|
|
95
129
|
A deliberately public helper can expose its owner's Linaria treatment:
|
|
@@ -197,7 +197,10 @@ or applies the right CSS property. Browser review still verifies the samples.
|
|
|
197
197
|
|
|
198
198
|
Scenarios and fixture helpers are development-only wherever colocated. Runtime
|
|
199
199
|
source and the public barrel cannot import or re-export them. Showcase/scenario
|
|
200
|
-
code defines no CSS, custom classes or native controls; use public ADS
|
|
200
|
+
code defines no CSS, custom classes or standalone native controls; use public ADS
|
|
201
|
+
APIs. A single direct native child can supply semantics to a public runtime slot
|
|
202
|
+
when source tracing proves that owner clones the exact child with its own props.
|
|
203
|
+
An `asChild` flag alone is not evidence; scenario styling remains forbidden.
|
|
201
204
|
Neither ADS implementations nor scenarios import application code. Live business
|
|
202
205
|
data belongs to application adapters; ADS receives inputs or injected callbacks.
|
|
203
206
|
Direct global `fetch`, XHR, WebSocket and EventSource use is rejected. Renderer
|
|
@@ -107,6 +107,17 @@ Keep contextual public parts public when they are intended consumer API. Demonst
|
|
|
107
107
|
them through their actual public composition/customization path and list their real
|
|
108
108
|
render references in covers. Do not count internal production renders as public API
|
|
109
109
|
coverage. Behavioral APIs use exercises and registered guarantees instead.
|
|
110
|
-
Scenario controls still use public UI
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
Scenario controls still use public UI. A native link/button can supply semantics
|
|
111
|
+
as the single direct child of a public runtime slot when source tracing proves
|
|
112
|
+
that owner clones the child with its props. An `asChild` flag alone is insufficient;
|
|
113
|
+
standalone native controls and custom scenario styling remain rejected. See
|
|
114
|
+
[scenarios.md](scenarios.md) for evidence and limits. Preserve the intended
|
|
115
|
+
composition instead of substituting an unrelated styled control or adding a
|
|
116
|
+
demo-only component prop. Review framing belongs in public layout patterns.
|
|
117
|
+
|
|
118
|
+
A complete runner report containing an ordinary failed assertion preserves each
|
|
119
|
+
passing check as `passed`; the failed check remains `failed`, and overall
|
|
120
|
+
verification still rejects the mutation. Fix the actual failing checks instead of
|
|
121
|
+
assuming every affected owner failed. Suite errors, unexplained runner exits,
|
|
122
|
+
missing results and interrupted runs cannot certify passing guarantees. Use the
|
|
123
|
+
case's `reportedStatus`, message and report to distinguish these conditions.
|
|
@@ -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
|
|
@@ -90,6 +90,40 @@ do not export the helper solely for a test. Public/private ownership is a produc
|
|
|
90
90
|
decision, not a coverage workaround. Browser checks verify transitions but do not
|
|
91
91
|
replace the current static `covers` requirement; `exercises` does not waive it.
|
|
92
92
|
|
|
93
|
+
## Native semantics through a public runtime slot
|
|
94
|
+
|
|
95
|
+
A native element can be the direct child of a public runtime component that
|
|
96
|
+
actually clones that child with its own props:
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
<Action asChild>
|
|
100
|
+
<a href={fixture.destination}>Documentation</a>
|
|
101
|
+
</Action>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`Action` and the fixture are illustrative; use the project's intended public API.
|
|
105
|
+
The runtime owner supplies presentation and interaction; the anchor supplies link
|
|
106
|
+
semantics. Keep this composition when it is the product contract. Do not replace
|
|
107
|
+
the anchor with an unrelated styled link, add a demo-only prop, disable the guard
|
|
108
|
+
or write outside MCP merely to silence a diagnostic.
|
|
109
|
+
|
|
110
|
+
ADS follows the selected runtime render path, the exact child and real imported
|
|
111
|
+
React/Preact `cloneElement` operations, including local slot/prop-merging helpers.
|
|
112
|
+
The prop need not be named `asChild`; the spelling alone grants no permission.
|
|
113
|
+
JSX and recognized framework factories follow the same rule. The native element
|
|
114
|
+
must be the single direct child: another native control nested inside it remains
|
|
115
|
+
subject to the ordinary ownership rule. Passing children through a container,
|
|
116
|
+
a scenario-local wrapper or an opaque component does not establish a slot.
|
|
117
|
+
|
|
118
|
+
This grants no scenario styling permission. Inline styles and arbitrary class
|
|
119
|
+
names remain rejected, including on the slotted child. Public style helpers retain
|
|
120
|
+
their own provenance checks. Unknown branches that could render the native child
|
|
121
|
+
directly, multiple children and untraceable replacement props remain blocked.
|
|
122
|
+
If a valid slot contract cannot be traced, keep the intended API and report a
|
|
123
|
+
small reproduction rather than inventing a product workaround. Static proof
|
|
124
|
+
establishes ownership, not correct prop merging, focus, keyboard or navigation
|
|
125
|
+
behavior; verify those in the built App and registered checks.
|
|
126
|
+
|
|
93
127
|
## Scenarios may consume verified public style helpers
|
|
94
128
|
|
|
95
129
|
A deliberately public helper can expose its owner's Linaria treatment:
|
|
@@ -107,6 +107,17 @@ Keep contextual public parts public when they are intended consumer API. Demonst
|
|
|
107
107
|
them through their actual public composition/customization path and list their real
|
|
108
108
|
render references in covers. Do not count internal production renders as public API
|
|
109
109
|
coverage. Behavioral APIs use exercises and registered guarantees instead.
|
|
110
|
-
Scenario controls still use public UI
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
Scenario controls still use public UI. A native link/button can supply semantics
|
|
111
|
+
as the single direct child of a public runtime slot when source tracing proves
|
|
112
|
+
that owner clones the child with its props. An `asChild` flag alone is insufficient;
|
|
113
|
+
standalone native controls and custom scenario styling remain rejected. See
|
|
114
|
+
[scenarios.md](scenarios.md) for evidence and limits. Preserve the intended
|
|
115
|
+
composition instead of substituting an unrelated styled control or adding a
|
|
116
|
+
demo-only component prop. Review framing belongs in public layout patterns.
|
|
117
|
+
|
|
118
|
+
A complete runner report containing an ordinary failed assertion preserves each
|
|
119
|
+
passing check as `passed`; the failed check remains `failed`, and overall
|
|
120
|
+
verification still rejects the mutation. Fix the actual failing checks instead of
|
|
121
|
+
assuming every affected owner failed. Suite errors, unexplained runner exits,
|
|
122
|
+
missing results and interrupted runs cannot certify passing guarantees. Use the
|
|
123
|
+
case's `reportedStatus`, message and report to distinguish these conditions.
|
|
@@ -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
|
|
@@ -90,6 +90,40 @@ do not export the helper solely for a test. Public/private ownership is a produc
|
|
|
90
90
|
decision, not a coverage workaround. Browser checks verify transitions but do not
|
|
91
91
|
replace the current static `covers` requirement; `exercises` does not waive it.
|
|
92
92
|
|
|
93
|
+
## Native semantics through a public runtime slot
|
|
94
|
+
|
|
95
|
+
A native element can be the direct child of a public runtime component that
|
|
96
|
+
actually clones that child with its own props:
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
<Action asChild>
|
|
100
|
+
<a href={fixture.destination}>Documentation</a>
|
|
101
|
+
</Action>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`Action` and the fixture are illustrative; use the project's intended public API.
|
|
105
|
+
The runtime owner supplies presentation and interaction; the anchor supplies link
|
|
106
|
+
semantics. Keep this composition when it is the product contract. Do not replace
|
|
107
|
+
the anchor with an unrelated styled link, add a demo-only prop, disable the guard
|
|
108
|
+
or write outside MCP merely to silence a diagnostic.
|
|
109
|
+
|
|
110
|
+
ADS follows the selected runtime render path, the exact child and real imported
|
|
111
|
+
React/Preact `cloneElement` operations, including local slot/prop-merging helpers.
|
|
112
|
+
The prop need not be named `asChild`; the spelling alone grants no permission.
|
|
113
|
+
JSX and recognized framework factories follow the same rule. The native element
|
|
114
|
+
must be the single direct child: another native control nested inside it remains
|
|
115
|
+
subject to the ordinary ownership rule. Passing children through a container,
|
|
116
|
+
a scenario-local wrapper or an opaque component does not establish a slot.
|
|
117
|
+
|
|
118
|
+
This grants no scenario styling permission. Inline styles and arbitrary class
|
|
119
|
+
names remain rejected, including on the slotted child. Public style helpers retain
|
|
120
|
+
their own provenance checks. Unknown branches that could render the native child
|
|
121
|
+
directly, multiple children and untraceable replacement props remain blocked.
|
|
122
|
+
If a valid slot contract cannot be traced, keep the intended API and report a
|
|
123
|
+
small reproduction rather than inventing a product workaround. Static proof
|
|
124
|
+
establishes ownership, not correct prop merging, focus, keyboard or navigation
|
|
125
|
+
behavior; verify those in the built App and registered checks.
|
|
126
|
+
|
|
93
127
|
## Scenarios may consume verified public style helpers
|
|
94
128
|
|
|
95
129
|
A deliberately public helper can expose its owner's Linaria treatment:
|
|
@@ -107,6 +107,17 @@ Keep contextual public parts public when they are intended consumer API. Demonst
|
|
|
107
107
|
them through their actual public composition/customization path and list their real
|
|
108
108
|
render references in covers. Do not count internal production renders as public API
|
|
109
109
|
coverage. Behavioral APIs use exercises and registered guarantees instead.
|
|
110
|
-
Scenario controls still use public UI
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
Scenario controls still use public UI. A native link/button can supply semantics
|
|
111
|
+
as the single direct child of a public runtime slot when source tracing proves
|
|
112
|
+
that owner clones the child with its props. An `asChild` flag alone is insufficient;
|
|
113
|
+
standalone native controls and custom scenario styling remain rejected. See
|
|
114
|
+
[scenarios.md](scenarios.md) for evidence and limits. Preserve the intended
|
|
115
|
+
composition instead of substituting an unrelated styled control or adding a
|
|
116
|
+
demo-only component prop. Review framing belongs in public layout patterns.
|
|
117
|
+
|
|
118
|
+
A complete runner report containing an ordinary failed assertion preserves each
|
|
119
|
+
passing check as `passed`; the failed check remains `failed`, and overall
|
|
120
|
+
verification still rejects the mutation. Fix the actual failing checks instead of
|
|
121
|
+
assuming every affected owner failed. Suite errors, unexplained runner exits,
|
|
122
|
+
missing results and interrupted runs cannot certify passing guarantees. Use the
|
|
123
|
+
case's `reportedStatus`, message and report to distinguish these conditions.
|
|
@@ -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
|
|
@@ -90,6 +90,40 @@ do not export the helper solely for a test. Public/private ownership is a produc
|
|
|
90
90
|
decision, not a coverage workaround. Browser checks verify transitions but do not
|
|
91
91
|
replace the current static `covers` requirement; `exercises` does not waive it.
|
|
92
92
|
|
|
93
|
+
## Native semantics through a public runtime slot
|
|
94
|
+
|
|
95
|
+
A native element can be the direct child of a public runtime component that
|
|
96
|
+
actually clones that child with its own props:
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
<Action asChild>
|
|
100
|
+
<a href={fixture.destination}>Documentation</a>
|
|
101
|
+
</Action>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`Action` and the fixture are illustrative; use the project's intended public API.
|
|
105
|
+
The runtime owner supplies presentation and interaction; the anchor supplies link
|
|
106
|
+
semantics. Keep this composition when it is the product contract. Do not replace
|
|
107
|
+
the anchor with an unrelated styled link, add a demo-only prop, disable the guard
|
|
108
|
+
or write outside MCP merely to silence a diagnostic.
|
|
109
|
+
|
|
110
|
+
ADS follows the selected runtime render path, the exact child and real imported
|
|
111
|
+
React/Preact `cloneElement` operations, including local slot/prop-merging helpers.
|
|
112
|
+
The prop need not be named `asChild`; the spelling alone grants no permission.
|
|
113
|
+
JSX and recognized framework factories follow the same rule. The native element
|
|
114
|
+
must be the single direct child: another native control nested inside it remains
|
|
115
|
+
subject to the ordinary ownership rule. Passing children through a container,
|
|
116
|
+
a scenario-local wrapper or an opaque component does not establish a slot.
|
|
117
|
+
|
|
118
|
+
This grants no scenario styling permission. Inline styles and arbitrary class
|
|
119
|
+
names remain rejected, including on the slotted child. Public style helpers retain
|
|
120
|
+
their own provenance checks. Unknown branches that could render the native child
|
|
121
|
+
directly, multiple children and untraceable replacement props remain blocked.
|
|
122
|
+
If a valid slot contract cannot be traced, keep the intended API and report a
|
|
123
|
+
small reproduction rather than inventing a product workaround. Static proof
|
|
124
|
+
establishes ownership, not correct prop merging, focus, keyboard or navigation
|
|
125
|
+
behavior; verify those in the built App and registered checks.
|
|
126
|
+
|
|
93
127
|
## Scenarios may consume verified public style helpers
|
|
94
128
|
|
|
95
129
|
A deliberately public helper can expose its owner's Linaria treatment:
|
|
@@ -107,6 +107,17 @@ Keep contextual public parts public when they are intended consumer API. Demonst
|
|
|
107
107
|
them through their actual public composition/customization path and list their real
|
|
108
108
|
render references in covers. Do not count internal production renders as public API
|
|
109
109
|
coverage. Behavioral APIs use exercises and registered guarantees instead.
|
|
110
|
-
Scenario controls still use public UI
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
Scenario controls still use public UI. A native link/button can supply semantics
|
|
111
|
+
as the single direct child of a public runtime slot when source tracing proves
|
|
112
|
+
that owner clones the child with its props. An `asChild` flag alone is insufficient;
|
|
113
|
+
standalone native controls and custom scenario styling remain rejected. See
|
|
114
|
+
[scenarios.md](scenarios.md) for evidence and limits. Preserve the intended
|
|
115
|
+
composition instead of substituting an unrelated styled control or adding a
|
|
116
|
+
demo-only component prop. Review framing belongs in public layout patterns.
|
|
117
|
+
|
|
118
|
+
A complete runner report containing an ordinary failed assertion preserves each
|
|
119
|
+
passing check as `passed`; the failed check remains `failed`, and overall
|
|
120
|
+
verification still rejects the mutation. Fix the actual failing checks instead of
|
|
121
|
+
assuming every affected owner failed. Suite errors, unexplained runner exits,
|
|
122
|
+
missing results and interrupted runs cannot certify passing guarantees. Use the
|
|
123
|
+
case's `reportedStatus`, message and report to distinguish these conditions.
|
|
@@ -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
|
|
@@ -90,6 +90,40 @@ do not export the helper solely for a test. Public/private ownership is a produc
|
|
|
90
90
|
decision, not a coverage workaround. Browser checks verify transitions but do not
|
|
91
91
|
replace the current static `covers` requirement; `exercises` does not waive it.
|
|
92
92
|
|
|
93
|
+
## Native semantics through a public runtime slot
|
|
94
|
+
|
|
95
|
+
A native element can be the direct child of a public runtime component that
|
|
96
|
+
actually clones that child with its own props:
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
<Action asChild>
|
|
100
|
+
<a href={fixture.destination}>Documentation</a>
|
|
101
|
+
</Action>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`Action` and the fixture are illustrative; use the project's intended public API.
|
|
105
|
+
The runtime owner supplies presentation and interaction; the anchor supplies link
|
|
106
|
+
semantics. Keep this composition when it is the product contract. Do not replace
|
|
107
|
+
the anchor with an unrelated styled link, add a demo-only prop, disable the guard
|
|
108
|
+
or write outside MCP merely to silence a diagnostic.
|
|
109
|
+
|
|
110
|
+
ADS follows the selected runtime render path, the exact child and real imported
|
|
111
|
+
React/Preact `cloneElement` operations, including local slot/prop-merging helpers.
|
|
112
|
+
The prop need not be named `asChild`; the spelling alone grants no permission.
|
|
113
|
+
JSX and recognized framework factories follow the same rule. The native element
|
|
114
|
+
must be the single direct child: another native control nested inside it remains
|
|
115
|
+
subject to the ordinary ownership rule. Passing children through a container,
|
|
116
|
+
a scenario-local wrapper or an opaque component does not establish a slot.
|
|
117
|
+
|
|
118
|
+
This grants no scenario styling permission. Inline styles and arbitrary class
|
|
119
|
+
names remain rejected, including on the slotted child. Public style helpers retain
|
|
120
|
+
their own provenance checks. Unknown branches that could render the native child
|
|
121
|
+
directly, multiple children and untraceable replacement props remain blocked.
|
|
122
|
+
If a valid slot contract cannot be traced, keep the intended API and report a
|
|
123
|
+
small reproduction rather than inventing a product workaround. Static proof
|
|
124
|
+
establishes ownership, not correct prop merging, focus, keyboard or navigation
|
|
125
|
+
behavior; verify those in the built App and registered checks.
|
|
126
|
+
|
|
93
127
|
## Scenarios may consume verified public style helpers
|
|
94
128
|
|
|
95
129
|
A deliberately public helper can expose its owner's Linaria treatment:
|
package/dist/source-values.mjs
CHANGED
|
@@ -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
|
}
|
package/dist/verification.mjs
CHANGED
|
@@ -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
|
|
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
|
|
300
|
-
let status =
|
|
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
|
|
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
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -303,6 +303,10 @@ Opaque conditional branches do not grant speculative coverage. Scenarios may
|
|
|
303
303
|
consume deliberate public class helpers when their returned styles retain
|
|
304
304
|
runtime-owned Linaria provenance; stable owner-authored hooks may accompany real
|
|
305
305
|
styles. This preserves ownership rather than granting scenario styling freedom.
|
|
306
|
+
A native element may provide semantics as the single direct child of a public
|
|
307
|
+
runtime slot when source tracing proves it is cloned with that owner's props.
|
|
308
|
+
This preserves polymorphic composition without allowing standalone scenario
|
|
309
|
+
controls, scenario styling or name-based exceptions.
|
|
306
310
|
The skill-local [scenario guide](../skills/ads-designer/references/scenarios.md)
|
|
307
311
|
explains evidence, limits and diagnostics.
|
|
308
312
|
|
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
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.14
|
|
4
|
+
|
|
5
|
+
- Preserve independent passing behavior outcomes in complete runs with failed
|
|
6
|
+
assertions while keeping overall verification and mutations blocked. Retain
|
|
7
|
+
incomplete status for missing results, suite errors and unexplained exits.
|
|
8
|
+
- Recognize native scenario children through source-verified public runtime slot
|
|
9
|
+
composition, including React/Preact cloning and local prop mergers. Keep
|
|
10
|
+
standalone controls, custom scenario styling and unproven slot paths rejected;
|
|
11
|
+
update diagnostics, architecture and portable skill guidance generically.
|
|
12
|
+
|
|
3
13
|
## 0.3.13
|
|
4
14
|
|
|
5
15
|
- 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
|