@reqvire-org/ads 0.3.16 → 0.3.18

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 (59) hide show
  1. package/README.md +3 -0
  2. package/dist/atomic-composition.mjs +1 -1
  3. package/dist/check.mjs +43 -11
  4. package/dist/evidence.mjs +18 -0
  5. package/dist/investigations.mjs +101 -0
  6. package/dist/linaria/preserve-values.cjs +21 -2
  7. package/dist/mcp/behavior-pages.mjs +36 -0
  8. package/dist/mcp/context.mjs +3 -1
  9. package/dist/mcp/execution-results.mjs +146 -0
  10. package/dist/mcp/mutations.mjs +34 -17
  11. package/dist/mcp/preview-widget.bundle.js +12 -12
  12. package/dist/mcp/resource-loader.js +3 -3
  13. package/dist/mcp/result-pages.mjs +87 -0
  14. package/dist/mcp/server.mjs +67 -30
  15. package/dist/property-registrations.mjs +39 -0
  16. package/dist/scenario-selectors.mjs +140 -0
  17. package/dist/scenario-trace.mjs +1 -1
  18. package/dist/scoped-tokens.mjs +6 -0
  19. package/dist/selector-match.mjs +94 -0
  20. package/dist/showcase/client.bundle.js +2 -2
  21. package/dist/skills/ads-break-ui/SKILL.md +8 -3
  22. package/dist/skills/ads-break-ui/references/atomic-styles.md +48 -0
  23. package/dist/skills/ads-break-ui/references/captures.md +6 -1
  24. package/dist/skills/ads-break-ui/references/evidence.md +120 -0
  25. package/dist/skills/ads-break-ui/references/guarded-editing.md +8 -5
  26. package/dist/skills/ads-break-ui/references/scenarios.md +32 -0
  27. package/dist/skills/ads-designer/SKILL.md +8 -3
  28. package/dist/skills/ads-designer/references/atomic-styles.md +48 -0
  29. package/dist/skills/ads-designer/references/captures.md +6 -1
  30. package/dist/skills/ads-designer/references/evidence.md +120 -0
  31. package/dist/skills/ads-designer/references/guarded-editing.md +8 -5
  32. package/dist/skills/ads-designer/references/scenarios.md +32 -0
  33. package/dist/skills/ads-import/SKILL.md +8 -3
  34. package/dist/skills/ads-import/references/atomic-styles.md +48 -0
  35. package/dist/skills/ads-import/references/captures.md +6 -1
  36. package/dist/skills/ads-import/references/evidence.md +120 -0
  37. package/dist/skills/ads-import/references/guarded-editing.md +8 -5
  38. package/dist/skills/ads-import/references/scenarios.md +32 -0
  39. package/dist/skills/ads-migrate/SKILL.md +8 -3
  40. package/dist/skills/ads-migrate/references/atomic-styles.md +48 -0
  41. package/dist/skills/ads-migrate/references/captures.md +6 -1
  42. package/dist/skills/ads-migrate/references/evidence.md +120 -0
  43. package/dist/skills/ads-migrate/references/guarded-editing.md +8 -5
  44. package/dist/skills/ads-migrate/references/scenarios.md +32 -0
  45. package/dist/skills/ads-motion/SKILL.md +8 -3
  46. package/dist/skills/ads-motion/references/atomic-styles.md +48 -0
  47. package/dist/skills/ads-motion/references/captures.md +6 -1
  48. package/dist/skills/ads-motion/references/evidence.md +120 -0
  49. package/dist/skills/ads-motion/references/guarded-editing.md +8 -5
  50. package/dist/skills/ads-motion/references/scenarios.md +32 -0
  51. package/dist/verification/browser.mjs +22 -7
  52. package/dist/verification/playwright-reporter.cjs +23 -5
  53. package/dist/verification.mjs +31 -9
  54. package/docs/ADS.md +10 -0
  55. package/docs/ARCHITECTURE.md +47 -3
  56. package/docs/BEHAVIORS.md +30 -0
  57. package/docs/CHANGELOG.md +30 -0
  58. package/docs/MCP.md +66 -7
  59. package/package.json +2 -1
@@ -6,6 +6,7 @@ import { inspectUnits } from "./units.mjs";
6
6
  import { isDevelopmentFile } from "./config.mjs";
7
7
  import { foundationSelector } from "./foundation.mjs";
8
8
  import { isSharedToken, tokenContract } from "./token-policy.mjs";
9
+ import { propertyRegistration } from "./property-registrations.mjs";
9
10
 
10
11
  /** An explicit owner contract, never a heuristic based on a token's name. */
11
12
  export function scopedTokens(options) {
@@ -30,8 +31,13 @@ export function scopedTokens(options) {
30
31
  if (realpathSync(file) !== file) throw new Error("Scoped token stylesheets cannot resolve through symlinks.");
31
32
  const css = postcss.parse(readFileSync(file, "utf8"), {from:file});
32
33
  let count = 0;
34
+ css.walkAtRules(/^property$/i,rule=>{
35
+ try {const {name}=propertyRegistration(rule);let defined=false;css.walkDecls(name,()=>{defined=true;});if(!name.startsWith(data.namespace)||!defined)throw new Error("Register only a real token defined in this owner's namespace and sheet.");}
36
+ catch(error){report(stylesheet,error.message,rule.source?.start.line);}
37
+ });
33
38
  css.walk(node => {
34
39
  if (node.type === "comment" || node.type === "root") return;
40
+ if(node.type==="atrule"&&node.name.toLowerCase()==="property"||node.type==="decl"&&node.parent.type==="atrule"&&node.parent.name.toLowerCase()==="property")return; // Separately validated registration metadata.
35
41
  if (node.type === "atrule" && ["media", "supports", "layer"].includes(node.name) && node.nodes) return;
36
42
  if (node.type === "rule" && node.parent.type !== "rule" && !foundationSelector(node.selector, {definitions:true})) return;
37
43
  if (node.type === "decl" && node.parent.type === "rule" && node.prop.startsWith(data.namespace) && node.prop.length > data.namespace.length && !node.important) { count++; return; }
@@ -0,0 +1,94 @@
1
+ import selectorParser from "postcss-selector-parser";
2
+
3
+ const states = new Set([":hover", ":active", ":focus", ":focus-visible", ":focus-within", ":visited", ":link", ":any-link", ":enabled", ":disabled", ":checked", ":indeterminate", ":required", ":optional", ":valid", ":invalid", ":placeholder-shown", ":open"]);
4
+
5
+ /** Match bounded source DOM witnesses, not a browser cascade. Ownership requires
6
+ * a positive native tag or a :has binding to a real runtime-owned child marker. */
7
+ export function selectorWitness(selector, target, anchor, native) {
8
+ let steps = 0;
9
+ const yes = typed => ({ typed });
10
+ const and = (a, b) => a === false || b === false ? false : !a || !b ? undefined : yes(a.typed || b.typed);
11
+ const any = results => results.find(value => value?.typed) ?? results.find(Boolean) ?? (results.includes(undefined) ? undefined : false);
12
+ const descendants = (element, found = []) => {
13
+ if (found.length > 128) return found;
14
+ for (const child of element.children) { found.push(child); descendants(child, found); }
15
+ return found;
16
+ };
17
+ function simple(node, element, scope, binding) {
18
+ if (++steps > 2000 || !element) return undefined;
19
+ if (node.type === "comment") return yes(false);
20
+ if (node.type === "nesting") return element === anchor ? yes(false) : false;
21
+ if (node.type === "universal") return yes(false);
22
+ if (node.type === "tag") return !node.namespace && node.value.toLowerCase() === element.tag ? yes(element === native) : false;
23
+ if (node.type === "attribute") {
24
+ if (node.namespace || !element.attributes.has(node.attribute)) return false;
25
+ const actual = element.attributes.get(node.attribute);
26
+ if (actual === undefined) return undefined;
27
+ const owned=binding&&element.parent===native&&element.ownedAttributes?.has(node.attribute);
28
+ if (!node.operator) return yes(owned);
29
+ if (node.operator !== "=" || node.insensitive) return undefined;
30
+ return String(actual) === node.value ? yes(owned) : false;
31
+ }
32
+ if (node.type === "class" || node.type === "id") {
33
+ const value = element.attributes.get(node.type === "class" ? "className" : "id");
34
+ if (typeof value !== "string") return undefined;
35
+ return (node.type === "class" ? value.split(/\s+/).includes(node.value) : value === node.value) ? yes(false) : false;
36
+ }
37
+ if (node.type !== "pseudo" || node.value.startsWith("::")) return undefined;
38
+ if (node.value === ":scope") return element === scope ? yes(false) : false;
39
+ if (states.has(node.value) && !node.nodes?.length) return yes(false);
40
+ if ([":is", ":where"].includes(node.value)) return any(node.nodes.map(branch => match(branch.nodes, element, scope, binding)));
41
+ if (node.value === ":not") {
42
+ const matched = any(node.nodes.map(branch => match(branch.nodes, element, scope)));
43
+ return matched === false ? yes(false) : matched ? false : undefined;
44
+ }
45
+ if (node.value === ":has") {
46
+ const children = descendants(element);
47
+ if (children.length > 128) return undefined;
48
+ return any(node.nodes.flatMap(branch => {
49
+ const nodes = [selectorParser.pseudo({ value: ":scope" }), ...(branch.nodes[0]?.type === "combinator" ? [] : [selectorParser.combinator({ value: " " })]), ...branch.nodes];
50
+ return children.map(child => match(nodes, child, element,element===native));
51
+ }));
52
+ }
53
+ return undefined;
54
+ }
55
+ function match(nodes, element, scope, binding=false) {
56
+ if (++steps > 2000 || !element) return undefined;
57
+ const groups = [[]], combinators = [];
58
+ for (const node of nodes) {
59
+ if (node.type === "combinator") { combinators.push(node.value.trim() || " "); groups.push([]); }
60
+ else groups.at(-1).push(node);
61
+ }
62
+ if (groups.some(group => !group.length)) return undefined;
63
+ function part(index, current) {
64
+ if (!current || ++steps > 2000) return current ? undefined : false;
65
+ let result = yes(false);
66
+ for (const node of groups[index]) result = and(result, simple(node, current, scope,binding));
67
+ if (!result || !index) return result;
68
+ const candidates = [];
69
+ if (combinators[index - 1] === ">") candidates.push(current.parent);
70
+ else if (combinators[index - 1] === " ") { for (let parent = current.parent; parent && candidates.length < 32; parent = parent.parent) candidates.push(parent); }
71
+ else if (["+", "~"].includes(combinators[index - 1])) {
72
+ const siblings = current.parent?.children ?? [], position = siblings.indexOf(current);
73
+ candidates.push(...siblings.slice(combinators[index - 1] === "+" ? Math.max(0, position - 1) : 0, position));
74
+ } else return undefined;
75
+ return and(result, any(candidates.map(candidate => part(index - 1, candidate))));
76
+ }
77
+ return part(groups.length - 1, element);
78
+ }
79
+ try { return selectorParser().astSync(selector).nodes.some(branch => match(branch.nodes, target, anchor)?.typed); }
80
+ catch { return false; }
81
+ }
82
+
83
+ /** Resolve CSS nesting with selector ASTs; never substitute project names. */
84
+ export function nestedSelectors(parents, selector) {
85
+ const children = selectorParser().astSync(selector).nodes;
86
+ const result = [];
87
+ for (const parent of parents) for (const child of children) {
88
+ if (result.length >= 32) return [];
89
+ const copy = child.clone(); let nested = false;
90
+ copy.walkNesting(node => { nested = true; node.replaceWith(selectorParser.pseudo({ value: ":is", nodes: selectorParser().astSync(parent).nodes.map(value => value.clone()) })); });
91
+ result.push(nested ? copy.toString() : `${parent} ${copy}`);
92
+ }
93
+ return result;
94
+ }
@@ -112,8 +112,8 @@ Boolean requesting whether a visible border and background is provided by the ho
112
112
  - omitted: host decides border`)}),My=p.object({method:p.literal("ui/request-display-mode"),params:p.object({mode:Fa.describe("The display mode being requested.")})}),Ay=p.object({mode:Fa.describe("The display mode that was actually set. May differ from requested if not supported.")}).passthrough(),DP=p.union([p.literal("model"),p.literal("app")]).describe("Tool visibility scope - who can access the tool."),kk=p.object({resourceUri:p.string().optional(),visibility:p.array(DP).optional().describe(`Who can access this tool. Default: ["model", "app"]
113
113
  - "model": Tool visible to and callable by the agent
114
114
  - "app": Tool callable by the app from this server only`),csp:p.never().optional(),permissions:p.never().optional()}),$k=p.object({mimeTypes:p.array(p.string()).optional().describe('Array of supported MIME types for UI resources.\nMust include `"text/html;profile=mcp-app"` for MCP Apps support.')}),FP=p.object({method:p.literal("ui/download-file"),params:p.object({contents:p.array(p.union([or,rr])).describe("Resource contents to download \u2014 embedded (inline data) or linked (host fetches). Uses standard MCP resource types.")})}),HP=p.object({method:p.literal("ui/message"),params:p.object({role:p.literal("user").describe('Message role, currently only "user" is supported.'),content:p.array(Nt).describe("Message content blocks (text, image, etc.).")})}),Ck=p.object({method:p.literal("ui/notifications/sandbox-resource-ready"),params:p.object({html:p.string().describe("HTML content to load into the inner iframe."),sandbox:p.string().optional().describe("Optional override for the inner iframe's sandbox attribute."),csp:Tg.optional().describe("CSP configuration from resource metadata."),permissions:Eg.optional().describe("Sandbox permissions from resource metadata.")})}),Ik=p.object({method:p.literal("ui/notifications/tool-result"),params:Po.describe("Standard MCP tool execution result.")}),Ly=p.object({toolInfo:p.object({id:pt.optional().describe("JSON-RPC id of the tools/call request."),tool:xo.describe("Tool definition including name, inputSchema, etc.")}).optional().describe("Metadata of the tool call that instantiated this App."),theme:PP.optional().describe("Current color theme preference."),styles:MP.optional().describe("Style configuration for theming the app."),displayMode:Fa.optional().describe("How the UI is currently displayed."),availableDisplayModes:p.array(Fa).optional().describe("Display modes the host supports."),containerDimensions:p.union([p.object({height:p.number().describe("Fixed container height in pixels.")}),p.object({maxHeight:p.union([p.number(),p.undefined()]).optional().describe("Maximum container height in pixels.")})]).and(p.union([p.object({width:p.number().describe("Fixed container width in pixels.")}),p.object({maxWidth:p.union([p.number(),p.undefined()]).optional().describe("Maximum container width in pixels.")})])).optional().describe(`Container dimensions. Represents the dimensions of the iframe or other
115
- container holding the app. Specify either width or maxWidth, and either height or maxHeight.`),locale:p.string().optional().describe("User's language and region preference in BCP 47 format."),timeZone:p.string().optional().describe("User's timezone in IANA format."),userAgent:p.string().optional().describe("Host application identifier."),platform:p.union([p.literal("web"),p.literal("desktop"),p.literal("mobile")]).optional().describe("Platform type for responsive design decisions."),deviceCapabilities:p.object({touch:p.boolean().optional().describe("Whether the device supports touch input."),hover:p.boolean().optional().describe("Whether the device supports hover interactions.")}).optional().describe("Device input capabilities."),safeAreaInsets:p.object({top:p.number().describe("Top safe area inset in pixels."),right:p.number().describe("Right safe area inset in pixels."),bottom:p.number().describe("Bottom safe area inset in pixels."),left:p.number().describe("Left safe area inset in pixels.")}).optional().describe("Mobile safe area boundaries in pixels.")}).passthrough(),Ok=p.object({method:p.literal("ui/notifications/host-context-changed"),params:Ly.describe("Partial context update containing only changed fields.")}),VP=p.object({method:p.literal("ui/update-model-context"),params:p.object({content:p.array(Nt).optional().describe("Context content blocks (text, image, etc.)."),structuredContent:p.record(p.string(),p.unknown().describe("Structured content for machine-readable context data.")).optional().describe("Structured content for machine-readable context data.")})}),JP=p.object({method:p.literal("ui/initialize"),params:p.object({appInfo:It.describe("App identification (name and version)."),appCapabilities:UP.describe("Features and capabilities this app provides."),protocolVersion:p.string().describe("Protocol version this app supports.")})}),BP=p.object({protocolVersion:p.string().describe('Negotiated protocol version string (e.g., "2025-11-21").'),hostInfo:It.describe("Host application identification and version."),hostCapabilities:LP.describe("Features and capabilities provided by the host."),hostContext:Ly.describe("Rich context about the host environment.")}).passthrough();var Ou=class{eventTarget;eventSource;messageListener;constructor(t=window.parent,o){this.eventTarget=t,this.eventSource=o,this.messageListener=r=>{if(o&&r.source!==this.eventSource){console.debug("Ignoring message from unknown source",r);return}let n=ht.safeParse(r.data);n.success?(console.debug("Parsed message",n.data),this.onmessage?.(n.data)):r.data?.jsonrpc!=="2.0"?console.debug("Ignoring non-JSON-RPC message",n.error.message,r):(console.error("Failed to parse message",n.error.message,r),this.onerror?.(Error("Invalid JSON-RPC message received: "+n.error.message)))}}async start(){window.addEventListener("message",this.messageListener)}async send(t,o){t.method!==xP&&console.debug("Sending message",t),this.eventTarget.postMessage(t,"*")}async close(){window.removeEventListener("message",this.messageListener),this.onclose?.()}onclose;onerror;onmessage;sessionId;setProtocolVersion};var WP=[jy],Nu=class extends Tu{_client;_hostInfo;_hostCapabilities;_appCapabilities;_hostContext={};_appInfo;_initializedReceived=!1;_registeredEvents=new Set;_events=new xg;_methods=new Pg;setRequestHandler=(...t)=>{let[o,...r]=Ny("request",t)??t;this._methods.claim(o,"setRequestHandler"),super.setRequestHandler.call(this,o,...r)};setNotificationHandler=(...t)=>{let[o,...r]=Ny("notification",t)??t;this._methods.claim(o,"setNotificationHandler"),super.setNotificationHandler.call(this,o,...r)};removeRequestHandler=t=>{this._methods.release(t),super.removeRequestHandler(t)};removeNotificationHandler=t=>{this._methods.release(t),super.removeNotificationHandler(t)};replaceRequestHandler=(t,...o)=>{this._methods.replace(t),super.setRequestHandler.call(this,t,...o)};_wrapHandler(t,o){let r=super._wrapHandler(t,o);return t==="ui/initialize"||t==="ping"?r:async(n,s)=>(this._initializedReceived||console.warn(`[ext-apps] AppBridge received '${t}' before ui/notifications/initialized. The View is calling host methods before completing the handshake; it should await app.connect() first.`),r(n,s))}eventSchemas={sizechange:NP,sandboxready:OP,initialized:ZP,requestteardown:jP,loggingmessage:nr};setEventHandler(t,o){this._ensureEventSlot(t),this._events.setHandler(t,o)}getEventHandler(t){return this._events.getHandler(t)}addEventListener(t,o){this._ensureEventSlot(t),this._events.addEventListener(t,o)}removeEventListener(t,o){this._events.removeEventListener(t,o)}_ensureEventSlot(t){if(this._registeredEvents.has(t))return;let o=this.eventSchemas[t];if(!o)throw Error(`Unknown event: ${String(t)}`);let r=n=>{t==="initialized"&&(this._initializedReceived=!0),this._events.dispatch(t,n)};t==="loggingmessage"?this.setNotificationHandler("notifications/message",n=>{r(n.params)}):this.setNotificationHandler(o.shape.method.value,{params:o.shape.params},n=>r(n)),this._registeredEvents.add(t)}warnIfRequestHandlerReplaced(t,o,r){o&&r&&console.warn(`[MCP Apps] ${t} handler replaced. Previous handler will no longer be called.`)}constructor(t,o,r,n){super(n),this._client=t,this._hostInfo=o,this._hostCapabilities=r,this._methods.replace("notifications/cancelled"),this._methods.replace("notifications/progress"),this._ensureEventSlot("initialized"),this._hostContext=n?.hostContext||{},this.replaceRequestHandler("ui/initialize",{params:JP.shape.params,result:BP},s=>this._onAppsInitialize(s)),this.replaceRequestHandler("ping",(s,i)=>(this.onping?.(s.params,i),{})),this.replaceRequestHandler("ui/request-display-mode",{params:My.shape.params,result:Ay},()=>({mode:this._hostContext.displayMode??"inline"}))}buildContext(t){return t}assertCapabilityForMethod(t){}assertRequestHandlerCapability(t){}assertNotificationCapability(t){}getAppCapabilities(){return this._appCapabilities}getAppVersion(){return this._appInfo}onping;get onsizechange(){return this.getEventHandler("sizechange")}set onsizechange(t){this.setEventHandler("sizechange",t)}get onsandboxready(){return this.getEventHandler("sandboxready")}set onsandboxready(t){this.setEventHandler("sandboxready",t)}get oninitialized(){return this.getEventHandler("initialized")}set oninitialized(t){this.setEventHandler("initialized",t)}_onmessage;get onmessage(){return this._onmessage}set onmessage(t){this.warnIfRequestHandlerReplaced("onmessage",this._onmessage,t),this._onmessage=t,this.replaceRequestHandler("ui/message",{params:HP.shape.params,result:IP},async(o,r)=>{if(!this._onmessage)throw Error("No onmessage handler set");return this._onmessage(o,r)})}_onopenlink;get onopenlink(){return this._onopenlink}set onopenlink(t){this.warnIfRequestHandlerReplaced("onopenlink",this._onopenlink,t),this._onopenlink=t,this.replaceRequestHandler("ui/open-link",{params:kP.shape.params,result:$P},async(o,r)=>{if(!this._onopenlink)throw Error("No onopenlink handler set");return this._onopenlink(o,r)})}_ondownloadfile;get ondownloadfile(){return this._ondownloadfile}set ondownloadfile(t){this.warnIfRequestHandlerReplaced("ondownloadfile",this._ondownloadfile,t),this._ondownloadfile=t,this.replaceRequestHandler("ui/download-file",{params:FP.shape.params,result:CP},async(o,r)=>{if(!this._ondownloadfile)throw Error("No ondownloadfile handler set");return this._ondownloadfile(o,r)})}get onrequestteardown(){return this.getEventHandler("requestteardown")}set onrequestteardown(t){this.setEventHandler("requestteardown",t)}_onrequestdisplaymode;get onrequestdisplaymode(){return this._onrequestdisplaymode}set onrequestdisplaymode(t){this.warnIfRequestHandlerReplaced("onrequestdisplaymode",this._onrequestdisplaymode,t),this._onrequestdisplaymode=t,this.replaceRequestHandler("ui/request-display-mode",{params:My.shape.params,result:Ay},async(o,r)=>{if(!this._onrequestdisplaymode)throw Error("No onrequestdisplaymode handler set");return this._onrequestdisplaymode(o,r)})}get onloggingmessage(){return this.getEventHandler("loggingmessage")}set onloggingmessage(t){this.setEventHandler("loggingmessage",t)}_onupdatemodelcontext;get onupdatemodelcontext(){return this._onupdatemodelcontext}set onupdatemodelcontext(t){this.warnIfRequestHandlerReplaced("onupdatemodelcontext",this._onupdatemodelcontext,t),this._onupdatemodelcontext=t,this.replaceRequestHandler("ui/update-model-context",{params:VP.shape.params,result:Ro},async(o,r)=>{if(!this._onupdatemodelcontext)throw Error("No onupdatemodelcontext handler set");return this._onupdatemodelcontext(o,r)})}_oncalltool;get oncalltool(){return this._oncalltool}set oncalltool(t){this.warnIfRequestHandlerReplaced("oncalltool",this._oncalltool,t),this._oncalltool=t,this.replaceRequestHandler("tools/call",async(o,r)=>{if(!this._oncalltool)throw Error("No oncalltool handler set");return this._oncalltool(o.params,r)})}_oncreatesamplingmessage;get oncreatesamplingmessage(){return this._oncreatesamplingmessage}set oncreatesamplingmessage(t){this.warnIfRequestHandlerReplaced("oncreatesamplingmessage",this._oncreatesamplingmessage,t),this._oncreatesamplingmessage=t,this.replaceRequestHandler("sampling/createMessage",async(o,r)=>{if(!this._oncreatesamplingmessage)throw Error("No oncreatesamplingmessage handler set");return this._oncreatesamplingmessage(o.params,r)})}sendToolListChanged(t={}){return this.notification({method:"notifications/tools/list_changed",params:t})}_onlistresources;get onlistresources(){return this._onlistresources}set onlistresources(t){this.warnIfRequestHandlerReplaced("onlistresources",this._onlistresources,t),this._onlistresources=t,this.replaceRequestHandler("resources/list",async(o,r)=>{if(!this._onlistresources)throw Error("No onlistresources handler set");return this._onlistresources(o.params,r)})}_onlistresourcetemplates;get onlistresourcetemplates(){return this._onlistresourcetemplates}set onlistresourcetemplates(t){this.warnIfRequestHandlerReplaced("onlistresourcetemplates",this._onlistresourcetemplates,t),this._onlistresourcetemplates=t,this.replaceRequestHandler("resources/templates/list",async(o,r)=>{if(!this._onlistresourcetemplates)throw Error("No onlistresourcetemplates handler set");return this._onlistresourcetemplates(o.params,r)})}_onreadresource;get onreadresource(){return this._onreadresource}set onreadresource(t){this.warnIfRequestHandlerReplaced("onreadresource",this._onreadresource,t),this._onreadresource=t,this.replaceRequestHandler("resources/read",async(o,r)=>{if(!this._onreadresource)throw Error("No onreadresource handler set");return this._onreadresource(o.params,r)})}sendResourceListChanged(t={}){return this.notification({method:"notifications/resources/list_changed",params:t})}_onlistprompts;get onlistprompts(){return this._onlistprompts}set onlistprompts(t){this.warnIfRequestHandlerReplaced("onlistprompts",this._onlistprompts,t),this._onlistprompts=t,this.replaceRequestHandler("prompts/list",async(o,r)=>{if(!this._onlistprompts)throw Error("No onlistprompts handler set");return this._onlistprompts(o.params,r)})}sendPromptListChanged(t={}){return this.notification({method:"notifications/prompts/list_changed",params:t})}getCapabilities(){return this._hostCapabilities}async _onAppsInitialize(t){let o=t.protocolVersion;return this._appInfo!==void 0&&console.warn("[ext-apps] AppBridge received a second ui/initialize. The View may be double-mounting (e.g. React StrictMode in dev) without closing the previous App instance. Responding normally; the latest appInfo/appCapabilities replace the previous values."),this._appCapabilities=t.appCapabilities,this._appInfo=t.appInfo,{protocolVersion:WP.includes(o)?o:jy,hostCapabilities:this.getCapabilities(),hostInfo:this._hostInfo,hostContext:this._hostContext}}setHostContext(t){let o={},r=!1;for(let n of Object.keys(t)){let s=this._hostContext[n],i=t[n];KP(s,i)||(o[n]=i,r=!0)}r&&(this._hostContext=t,this.sendHostContextChange(o))}sendHostContextChange(t){return this.notification({method:"ui/notifications/host-context-changed",params:t})}sendToolInput(t){return this.notification({method:"ui/notifications/tool-input",params:t})}sendToolInputPartial(t){return this.notification({method:"ui/notifications/tool-input-partial",params:t})}sendToolResult(t){return this.notification({method:"ui/notifications/tool-result",params:t})}sendToolCancelled(t){return this.notification({method:"ui/notifications/tool-cancelled",params:t})}sendSandboxResourceReady(t){return this.notification({method:"ui/notifications/sandbox-resource-ready",params:t})}teardownResource(t,o){return this.request({method:"ui/resource-teardown",params:t},AP,o)}sendResourceTeardown=this.teardownResource;callTool(t,o){return this.request({method:"tools/call",params:t},o)}listTools(t,o){return this.request({method:"tools/list",params:t},o)}async connect(t){if(this.transport)throw Error("AppBridge is already connected. Call close() before connecting again.");if(this._initializedReceived=!1,this._client){let o=this._client.getServerCapabilities();if(!o)throw Error("Client server capabilities not available");o.tools&&(this.oncalltool=async(r,n)=>this._client.request({method:"tools/call",params:r},{signal:n.mcpReq.signal}),o.tools.listChanged&&this._client.setNotificationHandler("notifications/tools/list_changed",r=>this.sendToolListChanged(r.params))),o.resources&&(this.onlistresources=async(r,n)=>this._client.request({method:"resources/list",params:r},{signal:n.mcpReq.signal}),this.onlistresourcetemplates=async(r,n)=>this._client.request({method:"resources/templates/list",params:r},{signal:n.mcpReq.signal}),this.onreadresource=async(r,n)=>this._client.request({method:"resources/read",params:r},{signal:n.mcpReq.signal}),o.resources.listChanged&&this._client.setNotificationHandler("notifications/resources/list_changed",r=>this.sendResourceListChanged(r.params))),o.prompts&&(this.onlistprompts=async(r,n)=>this._client.request({method:"prompts/list",params:r},{signal:n.mcpReq.signal}),o.prompts.listChanged&&this._client.setNotificationHandler("notifications/prompts/list_changed",r=>this.sendPromptListChanged(r.params)))}return super.connect(t)}};function KP(e,t){return JSON.stringify(e)===JSON.stringify(t)}var Uy=[200,800],GP=e=>e?.name==="TypeError"&&/^(Failed to fetch|fetch failed|Load failed|NetworkError when attempting to fetch resource\.)$/.test(e.message);function YP(e,t){return new Promise((o,r)=>{t?.throwIfAborted();let s=setTimeout(()=>{t?.removeEventListener("abort",i),o()},e),i=()=>{clearTimeout(s),t.removeEventListener("abort",i),r(t.reason)};t?.addEventListener("abort",i,{once:!0})})}function Zy(e,{signal:t,onDiagnostic:o,wait:r=YP}={}){return async n=>{for(let s=1;;s++){t?.throwIfAborted();try{let i=await e(n);return t?.throwIfAborted(),s>1&&o?.({state:"recovered",uri:n.uri,attempt:s}),i}catch(i){if(t?.throwIfAborted(),!GP(i))throw i;if(s>Uy.length)throw o?.({state:"exhausted",uri:n.uri,attempt:s,message:i.message}),Object.assign(new Error(`MCP resource transport failed for ${n.uri} after ${s} attempts: ${i.message}. Inspect the host connection and browser network diagnostics; this resource-loading error does not establish a component assertion failure.`,{cause:i}),{code:"ADS_RESOURCE_TRANSPORT"});let a=Uy[s-1];o?.({state:"retry",uri:n.uri,attempt:s,nextAttempt:s+1,delayMs:a,message:i.message}),await r(a,t)}}}}var re=e=>document.getElementById(e),Ha=new URLSearchParams(location.search),XP=Ha.get("scenario")??Ha.get("category"),QP=["tokens","components","patterns","composition"],we="tokens",oo={},kg=!0,Ng=[],qg=[],jg=[],$g,gt,ft,Zn,Dn=0,fr=Ha.get("capture")==="1"&&Ha.get("theme")==="dark"?"dark":"light";Ha.get("capture")==="1"&&(document.documentElement.dataset.capture="true");document.documentElement.dataset.theme=fr;var Cg,Ig=!1,Au,Mg,Lu=!1,Va=!1,Fn,mr,Og=0,qu,ju=new AbortController,St=new Cy({name:"ads-showcase",version:"1.0.0"},{versionNegotiation:{mode:"auto"},capabilities:{extensions:{"io.modelcontextprotocol/ui":{mimeTypes:["text/html;profile=mcp-app"]}}}}),Mu=(e,t={})=>St.callTool({name:e,arguments:t}),Ag=Zy(e=>St.readResource(e,{signal:ju.signal}),{signal:ju.signal,onDiagnostic:e=>{e.state==="retry"&&(document.documentElement.dataset.adsResourceRetries=String(Number(document.documentElement.dataset.adsResourceRetries??0)+1)),console.warn("ADS resource transport",e)}}),eT=async e=>JSON.parse((await Ag({uri:e})).contents.find(t=>typeof t.text=="string").text);function ro(e,t=!1){re("loader").hidden=!0,re("preview").removeAttribute("aria-busy"),re("status").hidden=!e,re("status").textContent=e,re("status").dataset.error=String(t),re("status").setAttribute("role",t?"alert":"status")}function Fy(){re("status").hidden=!0,re("loader").hidden=!1,re("preview").setAttribute("aria-busy","true")}function Dy(e){let t=new URL(location.href);return t.searchParams.delete("scenario"),t.searchParams.delete("category"),e&&t.searchParams.set("scenario",e),t.pathname+t.search+t.hash}function tT(){return we==="tokens"?Ng:qg.filter(e=>e.kind===we)}function Lg(){return{theme:fr,displayMode:"inline",containerDimensions:{width:re("frame-slot").clientWidth,height:re("frame-slot").clientHeight}}}function Ug(){let e=tT();e.some(o=>o.id===oo[we])||(oo[we]=e[0]?.id??null),history.replaceState(null,"",Dy(oo[we]));let t=re("sidebar").scrollTop;re("entries").setAttribute("aria-label",we[0].toUpperCase()+we.slice(1)),re("entries").replaceChildren(...e.map(o=>{let r=document.createElement("a");return r.textContent=o.title,r.href=Dy(o.id),o.id===oo[we]&&r.setAttribute("aria-current","page"),r.addEventListener("click",n=>{n.button||n.metaKey||n.ctrlKey||n.shiftKey||n.altKey||(n.preventDefault(),oo[we]=o.id,Ug(),Vn().catch(gr))}),r})),re("sidebar").scrollTop=t;for(let o of re("sections").querySelectorAll("button"))o.dataset.section===we?o.setAttribute("aria-current","page"):o.removeAttribute("aria-current")}function gr(e){!Lu&&!Va&&ro(`${e.message}${gt?`
116
- The retained preview is not verified against the current source.`:""}`,!0)}async function Hn(e){if(e){try{await e.teardownResource({},{timeout:500})}catch{}await e.close()}}function Hy(){if(Lu||Va)return;Va=!0,ju.abort(),++Dn,clearTimeout(Au),ro("MCP disconnected. The retained preview is stale. Refresh the page after restarting ADS.",!0);let e=ft;ft=void 0,Hn(e)}async function Vy(){$g=(await Mu("ads_status")).structuredContent,re("title").textContent=$g.project.showcase.title,re("title").title=$g.projectRoot;let e=[],t;do{let i=await St.listTools(t?{cursor:t}:{});e.push(...i.tools),t=i.nextCursor}while(t);jg=e,Ng=(await Mu("ads_tokens",{limit:1})).structuredContent.categories;let o=[],r=0;do{let i=(await Mu("ads_scenarios",{limit:50,offset:r})).structuredContent;o.push(...i.scenarios),r=i.nextOffset}while(r!==void 0);qg=o;let n=kg?XP:oo[we],s=qg.find(i=>i.id===n);s&&QP.includes(s.kind)?(we=s.kind,oo[we]=s.id):kg&&Ng.some(i=>i.id===n)&&(oo.tokens=n),kg=!1,Ug()}function Vn({force:e=!1}={}){if(Va)return Promise.resolve();let t=oo[we],o=`${we}:${t}`;if(Zn?.key===o)return Zn.promise;if(!e&&!Zn&&gt?.key===o)return Promise.resolve();let r={key:o};return Zn=r,r.promise=oT(t,o,we==="tokens").finally(()=>{Zn===r&&(Zn=void 0)}),r.promise}async function oT(e,t,o){let r=++Dn;try{if(!e&&!o){if(await Hn(ft),r!==Dn)return;ft=void 0,gt=void 0,Fn=void 0,mr=void 0,re("frame-slot").replaceChildren(),ro("No scenarios discovered. Colocate an independently mountable scenario and fixture with the public implementation.");return}let n=jg.find(l=>l.name===(o?"ads_show_tokens":"ads_show_scenario"));if(!n?._meta?.ui?.resourceUri)throw new Error("The server did not expose a preview App tool.");let s=o?e?{category:e}:{}:{id:e};Fy();let i=await Mu(n.name,s);if(r!==Dn)return;if(i.isError)throw new Error(i.content?.find(l=>l.type==="text")?.text??"Preview build failed.");gt={key:t,id:e,token:o&&!i.structuredContent?.scenario,build:i.structuredContent?.build,manifestRevision:i.structuredContent?.manifestRevision};let a=await rT(n._meta.ui.resourceUri);if(r!==Dn)return;if(Fn==="ui://ads/preview"&&!gt.manifestRevision)throw new Error("The resource preview has no manifest revision.");await a.sendToolInput({arguments:s}),await a.sendToolResult(i),i.structuredContent?.manifestUri||ro("")}catch(n){r===Dn&&gr(n)}}async function rT(e){if(Fn===e&&mr)return mr;let t=++Og,o=ft;ft=void 0,Fn=e,mr=(async()=>{await Hn(o);let r=await Ag({uri:e});if(t!==Og||Lu||Va)throw new Error("Preview opening was superseded.");let n=r.contents.find(u=>u.mimeType==="text/html;profile=mcp-app"&&typeof u.text=="string");if(!n)throw new Error("The preview resource contains no MCP App.");let s=document.createElement("iframe");s.title="Design-system preview",s.setAttribute("sandbox","allow-scripts allow-same-origin"),s.src=qu,re("frame-slot").replaceChildren(s);let i=new Nu(null,{name:"ADS showcase",version:"1.0.0"},{serverResources:{},serverTools:{},logging:{}},{hostContext:Lg()});ft=i,i.onreadresource=u=>Ag(u),i.oncalltool=u=>{let d=jg.find(h=>h.name===u.name);if(!d||!(d._meta?.ui?.visibility??["model","app"]).includes("app"))throw new Error("This tool is not callable from an App.");return St.callTool(u)},i.onloggingmessage=u=>{u.logger!=="ads-preview"||u.data?.id!==gt?.id||(u.data.state==="ready"&&u.data.revision===gt.manifestRevision?ro(""):u.data.code==="ADS_PREVIEW_REOPEN"?(ft=void 0,Fn=void 0,mr=void 0,Fy(),Hn(i).then(()=>Vn({force:!0})).catch(gr)):u.data.state==="error"&&ro(u.data.message,!0))},i.onerror=gr;let a,l=new Promise((u,d)=>{a=setTimeout(()=>d(new Error("MCP App initialization timed out.")),15e3),i.oninitialized=()=>{clearTimeout(a),u()}});i.onsandboxready=()=>i.sendSandboxResourceReady({html:n.text,csp:n._meta?.ui?.csp,sandbox:"allow-scripts"});try{await Promise.all([i.connect(new Ou(s.contentWindow,s.contentWindow)),l])}catch(u){throw await Hn(i),u}finally{clearTimeout(a)}return i})();try{return await mr}catch(r){throw t===Og&&(Fn=void 0,mr=void 0),r}}async function nT(){let e=gt;if(!e||e.token)return;let t=await eT("ads://project/build");if(gt!==e)return;let o=t.previews.find(r=>r.id===e.id);if(!o){ro("The selected scenario is no longer registered.",!0);return}if(!(["idle","building"].includes(o.state)&&e.build?.current&&o.session===e.build.session&&o.revision<=e.build.revision)){if(o.state==="error"){ro(`Preview build failed. Showing an earlier view.
115
+ container holding the app. Specify either width or maxWidth, and either height or maxHeight.`),locale:p.string().optional().describe("User's language and region preference in BCP 47 format."),timeZone:p.string().optional().describe("User's timezone in IANA format."),userAgent:p.string().optional().describe("Host application identifier."),platform:p.union([p.literal("web"),p.literal("desktop"),p.literal("mobile")]).optional().describe("Platform type for responsive design decisions."),deviceCapabilities:p.object({touch:p.boolean().optional().describe("Whether the device supports touch input."),hover:p.boolean().optional().describe("Whether the device supports hover interactions.")}).optional().describe("Device input capabilities."),safeAreaInsets:p.object({top:p.number().describe("Top safe area inset in pixels."),right:p.number().describe("Right safe area inset in pixels."),bottom:p.number().describe("Bottom safe area inset in pixels."),left:p.number().describe("Left safe area inset in pixels.")}).optional().describe("Mobile safe area boundaries in pixels.")}).passthrough(),Ok=p.object({method:p.literal("ui/notifications/host-context-changed"),params:Ly.describe("Partial context update containing only changed fields.")}),VP=p.object({method:p.literal("ui/update-model-context"),params:p.object({content:p.array(Nt).optional().describe("Context content blocks (text, image, etc.)."),structuredContent:p.record(p.string(),p.unknown().describe("Structured content for machine-readable context data.")).optional().describe("Structured content for machine-readable context data.")})}),JP=p.object({method:p.literal("ui/initialize"),params:p.object({appInfo:It.describe("App identification (name and version)."),appCapabilities:UP.describe("Features and capabilities this app provides."),protocolVersion:p.string().describe("Protocol version this app supports.")})}),BP=p.object({protocolVersion:p.string().describe('Negotiated protocol version string (e.g., "2025-11-21").'),hostInfo:It.describe("Host application identification and version."),hostCapabilities:LP.describe("Features and capabilities provided by the host."),hostContext:Ly.describe("Rich context about the host environment.")}).passthrough();var Ou=class{eventTarget;eventSource;messageListener;constructor(t=window.parent,o){this.eventTarget=t,this.eventSource=o,this.messageListener=r=>{if(o&&r.source!==this.eventSource){console.debug("Ignoring message from unknown source",r);return}let n=ht.safeParse(r.data);n.success?(console.debug("Parsed message",n.data),this.onmessage?.(n.data)):r.data?.jsonrpc!=="2.0"?console.debug("Ignoring non-JSON-RPC message",n.error.message,r):(console.error("Failed to parse message",n.error.message,r),this.onerror?.(Error("Invalid JSON-RPC message received: "+n.error.message)))}}async start(){window.addEventListener("message",this.messageListener)}async send(t,o){t.method!==xP&&console.debug("Sending message",t),this.eventTarget.postMessage(t,"*")}async close(){window.removeEventListener("message",this.messageListener),this.onclose?.()}onclose;onerror;onmessage;sessionId;setProtocolVersion};var WP=[jy],Nu=class extends Tu{_client;_hostInfo;_hostCapabilities;_appCapabilities;_hostContext={};_appInfo;_initializedReceived=!1;_registeredEvents=new Set;_events=new xg;_methods=new Pg;setRequestHandler=(...t)=>{let[o,...r]=Ny("request",t)??t;this._methods.claim(o,"setRequestHandler"),super.setRequestHandler.call(this,o,...r)};setNotificationHandler=(...t)=>{let[o,...r]=Ny("notification",t)??t;this._methods.claim(o,"setNotificationHandler"),super.setNotificationHandler.call(this,o,...r)};removeRequestHandler=t=>{this._methods.release(t),super.removeRequestHandler(t)};removeNotificationHandler=t=>{this._methods.release(t),super.removeNotificationHandler(t)};replaceRequestHandler=(t,...o)=>{this._methods.replace(t),super.setRequestHandler.call(this,t,...o)};_wrapHandler(t,o){let r=super._wrapHandler(t,o);return t==="ui/initialize"||t==="ping"?r:async(n,s)=>(this._initializedReceived||console.warn(`[ext-apps] AppBridge received '${t}' before ui/notifications/initialized. The View is calling host methods before completing the handshake; it should await app.connect() first.`),r(n,s))}eventSchemas={sizechange:NP,sandboxready:OP,initialized:ZP,requestteardown:jP,loggingmessage:nr};setEventHandler(t,o){this._ensureEventSlot(t),this._events.setHandler(t,o)}getEventHandler(t){return this._events.getHandler(t)}addEventListener(t,o){this._ensureEventSlot(t),this._events.addEventListener(t,o)}removeEventListener(t,o){this._events.removeEventListener(t,o)}_ensureEventSlot(t){if(this._registeredEvents.has(t))return;let o=this.eventSchemas[t];if(!o)throw Error(`Unknown event: ${String(t)}`);let r=n=>{t==="initialized"&&(this._initializedReceived=!0),this._events.dispatch(t,n)};t==="loggingmessage"?this.setNotificationHandler("notifications/message",n=>{r(n.params)}):this.setNotificationHandler(o.shape.method.value,{params:o.shape.params},n=>r(n)),this._registeredEvents.add(t)}warnIfRequestHandlerReplaced(t,o,r){o&&r&&console.warn(`[MCP Apps] ${t} handler replaced. Previous handler will no longer be called.`)}constructor(t,o,r,n){super(n),this._client=t,this._hostInfo=o,this._hostCapabilities=r,this._methods.replace("notifications/cancelled"),this._methods.replace("notifications/progress"),this._ensureEventSlot("initialized"),this._hostContext=n?.hostContext||{},this.replaceRequestHandler("ui/initialize",{params:JP.shape.params,result:BP},s=>this._onAppsInitialize(s)),this.replaceRequestHandler("ping",(s,i)=>(this.onping?.(s.params,i),{})),this.replaceRequestHandler("ui/request-display-mode",{params:My.shape.params,result:Ay},()=>({mode:this._hostContext.displayMode??"inline"}))}buildContext(t){return t}assertCapabilityForMethod(t){}assertRequestHandlerCapability(t){}assertNotificationCapability(t){}getAppCapabilities(){return this._appCapabilities}getAppVersion(){return this._appInfo}onping;get onsizechange(){return this.getEventHandler("sizechange")}set onsizechange(t){this.setEventHandler("sizechange",t)}get onsandboxready(){return this.getEventHandler("sandboxready")}set onsandboxready(t){this.setEventHandler("sandboxready",t)}get oninitialized(){return this.getEventHandler("initialized")}set oninitialized(t){this.setEventHandler("initialized",t)}_onmessage;get onmessage(){return this._onmessage}set onmessage(t){this.warnIfRequestHandlerReplaced("onmessage",this._onmessage,t),this._onmessage=t,this.replaceRequestHandler("ui/message",{params:HP.shape.params,result:IP},async(o,r)=>{if(!this._onmessage)throw Error("No onmessage handler set");return this._onmessage(o,r)})}_onopenlink;get onopenlink(){return this._onopenlink}set onopenlink(t){this.warnIfRequestHandlerReplaced("onopenlink",this._onopenlink,t),this._onopenlink=t,this.replaceRequestHandler("ui/open-link",{params:kP.shape.params,result:$P},async(o,r)=>{if(!this._onopenlink)throw Error("No onopenlink handler set");return this._onopenlink(o,r)})}_ondownloadfile;get ondownloadfile(){return this._ondownloadfile}set ondownloadfile(t){this.warnIfRequestHandlerReplaced("ondownloadfile",this._ondownloadfile,t),this._ondownloadfile=t,this.replaceRequestHandler("ui/download-file",{params:FP.shape.params,result:CP},async(o,r)=>{if(!this._ondownloadfile)throw Error("No ondownloadfile handler set");return this._ondownloadfile(o,r)})}get onrequestteardown(){return this.getEventHandler("requestteardown")}set onrequestteardown(t){this.setEventHandler("requestteardown",t)}_onrequestdisplaymode;get onrequestdisplaymode(){return this._onrequestdisplaymode}set onrequestdisplaymode(t){this.warnIfRequestHandlerReplaced("onrequestdisplaymode",this._onrequestdisplaymode,t),this._onrequestdisplaymode=t,this.replaceRequestHandler("ui/request-display-mode",{params:My.shape.params,result:Ay},async(o,r)=>{if(!this._onrequestdisplaymode)throw Error("No onrequestdisplaymode handler set");return this._onrequestdisplaymode(o,r)})}get onloggingmessage(){return this.getEventHandler("loggingmessage")}set onloggingmessage(t){this.setEventHandler("loggingmessage",t)}_onupdatemodelcontext;get onupdatemodelcontext(){return this._onupdatemodelcontext}set onupdatemodelcontext(t){this.warnIfRequestHandlerReplaced("onupdatemodelcontext",this._onupdatemodelcontext,t),this._onupdatemodelcontext=t,this.replaceRequestHandler("ui/update-model-context",{params:VP.shape.params,result:Ro},async(o,r)=>{if(!this._onupdatemodelcontext)throw Error("No onupdatemodelcontext handler set");return this._onupdatemodelcontext(o,r)})}_oncalltool;get oncalltool(){return this._oncalltool}set oncalltool(t){this.warnIfRequestHandlerReplaced("oncalltool",this._oncalltool,t),this._oncalltool=t,this.replaceRequestHandler("tools/call",async(o,r)=>{if(!this._oncalltool)throw Error("No oncalltool handler set");return this._oncalltool(o.params,r)})}_oncreatesamplingmessage;get oncreatesamplingmessage(){return this._oncreatesamplingmessage}set oncreatesamplingmessage(t){this.warnIfRequestHandlerReplaced("oncreatesamplingmessage",this._oncreatesamplingmessage,t),this._oncreatesamplingmessage=t,this.replaceRequestHandler("sampling/createMessage",async(o,r)=>{if(!this._oncreatesamplingmessage)throw Error("No oncreatesamplingmessage handler set");return this._oncreatesamplingmessage(o.params,r)})}sendToolListChanged(t={}){return this.notification({method:"notifications/tools/list_changed",params:t})}_onlistresources;get onlistresources(){return this._onlistresources}set onlistresources(t){this.warnIfRequestHandlerReplaced("onlistresources",this._onlistresources,t),this._onlistresources=t,this.replaceRequestHandler("resources/list",async(o,r)=>{if(!this._onlistresources)throw Error("No onlistresources handler set");return this._onlistresources(o.params,r)})}_onlistresourcetemplates;get onlistresourcetemplates(){return this._onlistresourcetemplates}set onlistresourcetemplates(t){this.warnIfRequestHandlerReplaced("onlistresourcetemplates",this._onlistresourcetemplates,t),this._onlistresourcetemplates=t,this.replaceRequestHandler("resources/templates/list",async(o,r)=>{if(!this._onlistresourcetemplates)throw Error("No onlistresourcetemplates handler set");return this._onlistresourcetemplates(o.params,r)})}_onreadresource;get onreadresource(){return this._onreadresource}set onreadresource(t){this.warnIfRequestHandlerReplaced("onreadresource",this._onreadresource,t),this._onreadresource=t,this.replaceRequestHandler("resources/read",async(o,r)=>{if(!this._onreadresource)throw Error("No onreadresource handler set");return this._onreadresource(o.params,r)})}sendResourceListChanged(t={}){return this.notification({method:"notifications/resources/list_changed",params:t})}_onlistprompts;get onlistprompts(){return this._onlistprompts}set onlistprompts(t){this.warnIfRequestHandlerReplaced("onlistprompts",this._onlistprompts,t),this._onlistprompts=t,this.replaceRequestHandler("prompts/list",async(o,r)=>{if(!this._onlistprompts)throw Error("No onlistprompts handler set");return this._onlistprompts(o.params,r)})}sendPromptListChanged(t={}){return this.notification({method:"notifications/prompts/list_changed",params:t})}getCapabilities(){return this._hostCapabilities}async _onAppsInitialize(t){let o=t.protocolVersion;return this._appInfo!==void 0&&console.warn("[ext-apps] AppBridge received a second ui/initialize. The View may be double-mounting (e.g. React StrictMode in dev) without closing the previous App instance. Responding normally; the latest appInfo/appCapabilities replace the previous values."),this._appCapabilities=t.appCapabilities,this._appInfo=t.appInfo,{protocolVersion:WP.includes(o)?o:jy,hostCapabilities:this.getCapabilities(),hostInfo:this._hostInfo,hostContext:this._hostContext}}setHostContext(t){let o={},r=!1;for(let n of Object.keys(t)){let s=this._hostContext[n],i=t[n];KP(s,i)||(o[n]=i,r=!0)}r&&(this._hostContext=t,this.sendHostContextChange(o))}sendHostContextChange(t){return this.notification({method:"ui/notifications/host-context-changed",params:t})}sendToolInput(t){return this.notification({method:"ui/notifications/tool-input",params:t})}sendToolInputPartial(t){return this.notification({method:"ui/notifications/tool-input-partial",params:t})}sendToolResult(t){return this.notification({method:"ui/notifications/tool-result",params:t})}sendToolCancelled(t){return this.notification({method:"ui/notifications/tool-cancelled",params:t})}sendSandboxResourceReady(t){return this.notification({method:"ui/notifications/sandbox-resource-ready",params:t})}teardownResource(t,o){return this.request({method:"ui/resource-teardown",params:t},AP,o)}sendResourceTeardown=this.teardownResource;callTool(t,o){return this.request({method:"tools/call",params:t},o)}listTools(t,o){return this.request({method:"tools/list",params:t},o)}async connect(t){if(this.transport)throw Error("AppBridge is already connected. Call close() before connecting again.");if(this._initializedReceived=!1,this._client){let o=this._client.getServerCapabilities();if(!o)throw Error("Client server capabilities not available");o.tools&&(this.oncalltool=async(r,n)=>this._client.request({method:"tools/call",params:r},{signal:n.mcpReq.signal}),o.tools.listChanged&&this._client.setNotificationHandler("notifications/tools/list_changed",r=>this.sendToolListChanged(r.params))),o.resources&&(this.onlistresources=async(r,n)=>this._client.request({method:"resources/list",params:r},{signal:n.mcpReq.signal}),this.onlistresourcetemplates=async(r,n)=>this._client.request({method:"resources/templates/list",params:r},{signal:n.mcpReq.signal}),this.onreadresource=async(r,n)=>this._client.request({method:"resources/read",params:r},{signal:n.mcpReq.signal}),o.resources.listChanged&&this._client.setNotificationHandler("notifications/resources/list_changed",r=>this.sendResourceListChanged(r.params))),o.prompts&&(this.onlistprompts=async(r,n)=>this._client.request({method:"prompts/list",params:r},{signal:n.mcpReq.signal}),o.prompts.listChanged&&this._client.setNotificationHandler("notifications/prompts/list_changed",r=>this.sendPromptListChanged(r.params)))}return super.connect(t)}};function KP(e,t){return JSON.stringify(e)===JSON.stringify(t)}var Uy=[200,800],GP=e=>e?.name==="TypeError"&&/^(Failed to fetch|fetch failed|Load failed|NetworkError when attempting to fetch resource\.)$/.test(e.message);function YP(e,t){return new Promise((o,r)=>{t?.throwIfAborted();let s=setTimeout(()=>{t?.removeEventListener("abort",i),o()},e),i=()=>{clearTimeout(s),t.removeEventListener("abort",i),r(t.reason)};t?.addEventListener("abort",i,{once:!0})})}function Zy(e,{signal:t,onDiagnostic:o,wait:r=YP}={}){return async n=>{for(let s=1;;s++){t?.throwIfAborted();try{let i=await e(n);return t?.throwIfAborted(),s>1&&o?.({state:"recovered",uri:n.uri,attempt:s}),i}catch(i){if(t?.throwIfAborted(),!GP(i))throw i;if(s>Uy.length)throw o?.({state:"exhausted",uri:n.uri,attempt:s,message:i.message}),Object.assign(new Error(`MCP resource transport failed for ${n.uri} after ${s} attempts: ${i.message}. Inspect the host connection and browser network diagnostics; this resource-loading error does not establish a component assertion failure.`,{cause:i}),{code:"ADS_RESOURCE_TRANSPORT"});let a=Uy[s-1];o?.({state:"retry",uri:n.uri,attempt:s,nextAttempt:s+1,delayMs:a,message:i.message}),await r(a,t)}}}}var re=e=>document.getElementById(e),Ha=new URLSearchParams(location.search),XP=Ha.get("scenario")??Ha.get("category"),QP=["tokens","components","patterns","composition"],we="tokens",oo={},kg=!0,Ng=[],qg=[],jg=[],$g,gt,ft,Zn,Dn=0,fr=Ha.get("capture")==="1"&&Ha.get("theme")==="dark"?"dark":"light";Ha.get("capture")==="1"&&(document.documentElement.dataset.capture="true");document.documentElement.dataset.theme=fr;var Cg,Ig=!1,Au,Mg,Lu=!1,Va=!1,Fn,mr,Og=0,qu,ju=new AbortController,St=new Cy({name:"ads-showcase",version:"1.0.0"},{versionNegotiation:{mode:"auto"},capabilities:{extensions:{"io.modelcontextprotocol/ui":{mimeTypes:["text/html;profile=mcp-app"]}}}}),Mu=(e,t={})=>St.callTool({name:e,arguments:t}),Ag=Zy(e=>St.readResource(e,{signal:ju.signal}),{signal:ju.signal,onDiagnostic:e=>{e.state==="retry"&&(document.documentElement.dataset.adsResourceRetries=String(Number(document.documentElement.dataset.adsResourceRetries??0)+1)),console.warn("ADS resource transport",e)}}),eT=async e=>JSON.parse((await Ag({uri:e})).contents.find(t=>typeof t.text=="string").text);function ro(e,t=!1){re("loader").hidden=!0,re("preview").removeAttribute("aria-busy"),re("status").hidden=!e,re("status").textContent=e,re("status").dataset.error=String(t),re("status").setAttribute("role",t?"alert":"status")}function Fy(){re("status").hidden=!0,re("loader").hidden=!1,re("preview").setAttribute("aria-busy","true")}function Dy(e){let t=new URL(location.href);return t.searchParams.delete("scenario"),t.searchParams.delete("category"),e&&t.searchParams.set("scenario",e),t.pathname+t.search+t.hash}function tT(){return we==="tokens"?Ng:qg.filter(e=>e.kind===we)}function Lg(){return{theme:fr,displayMode:"inline",containerDimensions:{width:re("frame-slot").clientWidth,height:re("frame-slot").clientHeight}}}function Ug(){let e=tT();e.some(o=>o.id===oo[we])||(oo[we]=e[0]?.id??null),history.replaceState(null,"",Dy(oo[we]));let t=re("sidebar").scrollTop;re("entries").setAttribute("aria-label",we[0].toUpperCase()+we.slice(1)),re("entries").replaceChildren(...e.map(o=>{let r=document.createElement("a");return r.textContent=o.title,r.href=Dy(o.id),o.id===oo[we]&&r.setAttribute("aria-current","page"),r.addEventListener("click",n=>{n.button||n.metaKey||n.ctrlKey||n.shiftKey||n.altKey||(n.preventDefault(),oo[we]=o.id,Ug(),Vn().catch(gr))}),r})),re("sidebar").scrollTop=t;for(let o of re("sections").querySelectorAll("button"))o.dataset.section===we?o.setAttribute("aria-current","page"):o.removeAttribute("aria-current")}function gr(e){!Lu&&!Va&&(re("status").dataset.errorCategory=e.category??(e.code==="ADS_RESOURCE_TRANSPORT"?"transport":"runner"),re("status").dataset.errorCode=e.code??"ADS_HOST_STARTUP",ro(`${e.message}${gt?`
116
+ The retained preview is not verified against the current source.`:""}`,!0))}async function Hn(e){if(e){try{await e.teardownResource({},{timeout:500})}catch{}await e.close()}}function Hy(){if(Lu||Va)return;Va=!0,ju.abort(),++Dn,clearTimeout(Au),ro("MCP disconnected. The retained preview is stale. Refresh the page after restarting ADS.",!0);let e=ft;ft=void 0,Hn(e)}async function Vy(){$g=(await Mu("ads_status")).structuredContent,re("title").textContent=$g.project.showcase.title,re("title").title=$g.projectRoot;let e=[],t;do{let i=await St.listTools(t?{cursor:t}:{});e.push(...i.tools),t=i.nextCursor}while(t);jg=e,Ng=(await Mu("ads_tokens",{limit:1})).structuredContent.categories;let o=[],r=0;do{let i=(await Mu("ads_scenarios",{limit:50,offset:r})).structuredContent;o.push(...i.scenarios),r=i.nextOffset}while(r!==void 0);qg=o;let n=kg?XP:oo[we],s=qg.find(i=>i.id===n);s&&QP.includes(s.kind)?(we=s.kind,oo[we]=s.id):kg&&Ng.some(i=>i.id===n)&&(oo.tokens=n),kg=!1,Ug()}function Vn({force:e=!1}={}){if(Va)return Promise.resolve();let t=oo[we],o=`${we}:${t}`;if(Zn?.key===o)return Zn.promise;if(!e&&!Zn&&gt?.key===o)return Promise.resolve();let r={key:o};return Zn=r,r.promise=oT(t,o,we==="tokens").finally(()=>{Zn===r&&(Zn=void 0)}),r.promise}async function oT(e,t,o){let r=++Dn;try{if(!e&&!o){if(await Hn(ft),r!==Dn)return;ft=void 0,gt=void 0,Fn=void 0,mr=void 0,re("frame-slot").replaceChildren(),ro("No scenarios discovered. Colocate an independently mountable scenario and fixture with the public implementation.");return}let n=jg.find(l=>l.name===(o?"ads_show_tokens":"ads_show_scenario"));if(!n?._meta?.ui?.resourceUri)throw new Error("The server did not expose a preview App tool.");let s=o?e?{category:e}:{}:{id:e};Fy();let i=await Mu(n.name,s);if(r!==Dn)return;if(i.isError)throw Object.assign(new Error(i.content?.find(l=>l.type==="text")?.text??"Preview build failed."),i.structuredContent?.failure);gt={key:t,id:e,token:o&&!i.structuredContent?.scenario,build:i.structuredContent?.build,manifestRevision:i.structuredContent?.manifestRevision};let a=await rT(n._meta.ui.resourceUri);if(r!==Dn)return;if(Fn==="ui://ads/preview"&&!gt.manifestRevision)throw new Error("The resource preview has no manifest revision.");await a.sendToolInput({arguments:s}),await a.sendToolResult(i),i.structuredContent?.manifestUri||ro("")}catch(n){r===Dn&&gr(n)}}async function rT(e){if(Fn===e&&mr)return mr;let t=++Og,o=ft;ft=void 0,Fn=e,mr=(async()=>{await Hn(o);let r=await Ag({uri:e});if(t!==Og||Lu||Va)throw new Error("Preview opening was superseded.");let n=r.contents.find(u=>u.mimeType==="text/html;profile=mcp-app"&&typeof u.text=="string");if(!n)throw new Error("The preview resource contains no MCP App.");let s=document.createElement("iframe");s.title="Design-system preview",s.setAttribute("sandbox","allow-scripts allow-same-origin"),s.src=qu,re("frame-slot").replaceChildren(s);let i=new Nu(null,{name:"ADS showcase",version:"1.0.0"},{serverResources:{},serverTools:{},logging:{}},{hostContext:Lg()});ft=i,i.onreadresource=u=>Ag(u),i.oncalltool=u=>{let d=jg.find(h=>h.name===u.name);if(!d||!(d._meta?.ui?.visibility??["model","app"]).includes("app"))throw new Error("This tool is not callable from an App.");return St.callTool(u)},i.onloggingmessage=u=>{u.logger!=="ads-preview"||u.data?.id!==gt?.id||(u.data.state==="ready"&&u.data.revision===gt.manifestRevision?ro(""):u.data.code==="ADS_PREVIEW_REOPEN"?(ft=void 0,Fn=void 0,mr=void 0,Fy(),Hn(i).then(()=>Vn({force:!0})).catch(gr)):u.data.state==="error"&&ro(u.data.message,!0))},i.onerror=gr;let a,l=new Promise((u,d)=>{a=setTimeout(()=>d(new Error("MCP App initialization timed out.")),15e3),i.oninitialized=()=>{clearTimeout(a),u()}});i.onsandboxready=()=>i.sendSandboxResourceReady({html:n.text,csp:n._meta?.ui?.csp,sandbox:"allow-scripts"});try{await Promise.all([i.connect(new Ou(s.contentWindow,s.contentWindow)),l])}catch(u){throw await Hn(i),u}finally{clearTimeout(a)}return i})();try{return await mr}catch(r){throw t===Og&&(Fn=void 0,mr=void 0),r}}async function nT(){let e=gt;if(!e||e.token)return;let t=await eT("ads://project/build");if(gt!==e)return;let o=t.previews.find(r=>r.id===e.id);if(!o){ro("The selected scenario is no longer registered.",!0);return}if(!(["idle","building"].includes(o.state)&&e.build?.current&&o.session===e.build.session&&o.revision<=e.build.revision)){if(o.state==="error"){ro(`Preview build failed. Showing an earlier view.
117
117
  ${o.diagnostics.map(r=>`${r.file??""}${r.line?`:${r.line}`:""}: ${r.message}`).join(`
118
118
  `)}`,!0);return}if(["idle","building"].includes(o.state)){ro("Rebuilding\u2026 The current view is the previous preview.");return}o.current&&(o.revision!==e.build?.revision||o.session!==e.build?.session)&&await Vn({force:!0})}}function Uu(e=!1,t=!1){Ig||=e,t&&gt?.token&&(gt.key="invalidated"),clearTimeout(Au),Au=setTimeout(()=>{if(Cg){Uu(e,t);return}Cg=(async()=>{Ig&&(Ig=!1,await Vy()),await Vn(),await nT()})().catch(gr).finally(()=>{Cg=void 0})},150)}for(let e of re("sections").querySelectorAll("button"))e.addEventListener("click",()=>{we=e.dataset.section,Ug(),Vn().catch(gr)});re("pane-toggle").addEventListener("click",()=>{let e=document.body.classList.toggle("collapsed");re("pane-toggle").setAttribute("aria-expanded",String(!e)),re("pane-toggle").setAttribute("aria-label",e?"Show navigation pane":"Hide navigation pane"),re("pane-toggle").title=e?"Show navigation pane":"Hide navigation pane",re("pane-toggle").querySelector("path").setAttribute("d",e?"M6 4l4 4-4 4":"M10 4l-4 4 4 4")});re("theme").addEventListener("click",()=>{fr=fr==="light"?"dark":"light",document.documentElement.dataset.theme=fr,re("theme").setAttribute("aria-label",fr==="dark"?"Use light theme":"Use dark theme"),re("theme").title=re("theme").getAttribute("aria-label"),re("theme").querySelector("svg").innerHTML=fr==="dark"?'<circle cx="8" cy="8" r="3"/><path d="M8 1v1M8 14v1M1 8h1M14 8h1M3 3l1 1M12 12l1 1M3 13l1-1M12 4l1-1"/>':'<path d="M13.5 9a5.5 5.5 0 0 1-6.5-6.5A5.5 5.5 0 1 0 13.5 9Z"/>',ft?.setHostContext(Lg())});re("minimize").addEventListener("click",()=>{re("header").hidden=!0,document.body.classList.add("collapsed")});var Jy=new ResizeObserver(()=>ft?.setHostContext(Lg()));Jy.observe(re("frame-slot"));window.addEventListener("pagehide",()=>{Lu=!0,ju.abort(),clearTimeout(Au),Jy.disconnect(),Hn(ft),Mg?.close(),St.close()},{once:!0});St.onclose=Hy;St.setNotificationHandler("notifications/resources/list_changed",()=>Uu(!0));St.setNotificationHandler("notifications/tools/list_changed",()=>Uu(!0));St.setNotificationHandler("notifications/resources/updated",({params:e})=>Uu(!1,e.uri==="ads://project/tokens"));try{if(qu=(await fetch("/host.json").then(e=>e.json())).sandboxUrl,!qu||new URL(qu).origin===location.origin)throw new Error("The separate-origin MCP App sandbox is unavailable.");await St.connect(new Iy(new URL("/mcp",location.href))),Mg=await St.listen({resourcesListChanged:!0,toolsListChanged:!0,resourceSubscriptions:["ads://project/build","ads://project/tokens","ads://project/scenarios","ads://project/status"]}),Mg.closed.then(e=>{e!=="local"&&Hy()}),await Vy(),await Vn()}catch(e){gr(e)}
119
119
  /*! Bundled license information:
@@ -52,17 +52,22 @@ For owner drafts, token-sample reconciliation, coupled transactions and local
52
52
  constant namespaces, read [guarded-editing.md](references/guarded-editing.md).
53
53
  For on-demand current/candidate screenshots and visual dry-run review, read
54
54
  [captures.md](references/captures.md).
55
+ For a rejected patch, failed check or loading error, read
56
+ [evidence and diagnosis](references/evidence.md); inspect the failing boundary
57
+ then page needed `ads_results` sections using the returned result ID. Inspect
58
+ one `ads_investigate({unit,check,runId})` summary before changing APIs or policy;
59
+ load request/error/artifact details only when relevant.
55
60
 
56
61
 
57
62
  For parent/child `covers`, conditional-control review, reference comparisons,
58
- public style-helper use, fragment-link navigation, or `scenario-render` /
59
- `showcase-class` findings, read [scenario contracts](references/scenarios.md). Its verification
63
+ public style-helper use, native slot/selector composition, fragment navigation,
64
+ or `scenario-render` / `showcase-control` / `showcase-class` findings, read [scenario contracts](references/scenarios.md). Its verification
60
65
  examples explain API visibility, reference baselines and state-transition checks.
61
66
  Traced parents can cover declared public children; verified public Linaria
62
67
  helpers do not grant custom scenario CSS.
63
68
 
64
69
  For atomic base/variant styling, helper/wrapper class composition, extracted
65
- custom-property arithmetic, or an
70
+ custom-property arithmetic, typed `@property` registrations, or an
66
71
  `atomic-composition-review` / `atomic-composition-limit` advisory, read
67
72
  [atomic style composition](references/atomic-styles.md). The same guide is served
68
73
  as `ads://guides/atomic-styles`. It explains shorthand/logical conflicts and
@@ -81,6 +81,54 @@ const treatment = css`
81
81
  This is a distinct state, not two competing base atoms. Whether changing padding
82
82
  on hover is desirable remains a product decision.
83
83
 
84
+ ## Register typed custom properties without atomizing their descriptors
85
+
86
+ Use a literal `@property` in its component/pattern's Linaria template when the
87
+ browser needs a typed, interpolated runtime variable:
88
+
89
+ ```tsx
90
+ const treatment = css`
91
+ @property --ds-example-progress {
92
+ syntax: "<number>";
93
+ inherits: false;
94
+ initial-value: 0;
95
+ }
96
+ opacity: var(--ds-example-progress);
97
+ `;
98
+ ```
99
+
100
+ The identifier is illustrative; use a unique name owned by the real unit.
101
+ Registrations are document-global, even when authored beside one component.
102
+ Put them at template level or inside conditional at-rules, never element selectors.
103
+ Use the same ADS tag resolver described below in both product and preview builds;
104
+ it preserves each complete registration and its conditional context instead of
105
+ splitting syntax/inherits/initial-value into invalid atomic rules. This works for
106
+ atomic `css` and `styled`; the browser receives CSS without a registration runtime.
107
+ MCP delivers it inside the App document, not the outer showcase host.
108
+
109
+ `property-registration` validates descriptor shape, CSS syntax/initial-value and
110
+ ownership, and rejects conflicting schemas or registrations shared by unrelated
111
+ owners. Identical registrations from the same owner are permitted. Runtime
112
+ `--ds-*` / `--ux-*` variables stay with their runtime owner. A shared token's
113
+ registration belongs beside its actual definition in a foundation value sheet;
114
+ a namespaced owner token's registration belongs in its registered `tokens.css`.
115
+ Neither `base.css` nor `fonts.css` becomes a registration escape hatch. Registration
116
+ alone does not define a token or satisfy its sample/fixture coverage.
117
+
118
+ Typed initial values must match their syntax and be computationally independent:
119
+ `0px` is valid metadata; `var(--space-4)`, `1rem` and `currentColor` are not.
120
+ The literal exemption applies only to registration descriptors. Keep design
121
+ assignments outside them token-backed; a technical fallback is not a place to
122
+ hide a component skin. Use literal static descriptors so guards can inspect them.
123
+ See the [W3C registration contract](https://www.w3.org/TR/css-properties-values-api-1/#at-property-rule).
124
+
125
+ Verify extracted CSS and actual interpolation, including after minification and
126
+ inside the MCP App. A declaration can parse while a browser ignores an invalid
127
+ registration. Keep required browser fallbacks and reduced-motion behavior; do not
128
+ replace a valid CSS implementation with script merely to evade extraction or a
129
+ guard limitation. These checks cover declarative registrations; arbitrary dynamic
130
+ `CSS.registerProperty()` calls are not an ownership proof.
131
+
84
132
  ## Preserve custom-property arithmetic during extraction
85
133
 
86
134
  Custom properties can hold an unevaluated token stream that becomes arithmetic
@@ -101,7 +101,12 @@ failed/cancelled capture does not replace existing image slots or apply source.
101
101
  If a scenario/target is newly introduced, `current` is null with an explicit
102
102
  `currentAbsent` explanation; only the real candidate image exists.
103
103
 
104
- Review the returned images and diagnostics, fix the draft if necessary, then apply
104
+ The edit reply is a summary. Read `ads_results({resultId,section:"captures"})` for
105
+ the requested image resource URIs and `section:"diagnostics"` for relevant
106
+ messages; follow `nextCursor` when needed. Read image resources explicitly.
107
+ Standalone `ads_capture` continues returning its selected capture directly.
108
+
109
+ Review those images and diagnostics, fix the draft if necessary, then apply
105
110
  with `dryRun:false`. Apply reruns all gates. An image is promoted only when the
106
111
  accepted source revision matches the captured candidate; other proposals remain
107
112
  stale. Images do not approve a patch or certify interaction/accessibility/parity.
@@ -0,0 +1,120 @@
1
+ # Investigate the failing boundary
2
+
3
+ Use this reference after a rejected patch, failed check or preview loading error.
4
+ MCP serves it as `ads://guides/evidence`. Inspect the compact result first; fetch
5
+ only the failing check's bundle or relevant artifact, rather than repeating a suite.
6
+
7
+ ## Read the result correctly
8
+
9
+ `failure.category` identifies a boundary, not an inferred root cause:
10
+
11
+ | Category | Established observation | Next inspection |
12
+ |----------|-------------------------|-----------------|
13
+ | `architecture` | A source/ownership/configuration gate rejected the candidate. | Exact rule, source location and `certainty`. An inconclusive ownership proof does not establish that a valid API is wrong. |
14
+ | `build` | A registered project pipeline step or compiled-resource integrity check failed. | Named command/phase, exit code, bounded output and build revision. A custom project script may contain several kinds of checks. |
15
+ | `transport` | An MCP/browser resource delivery boundary failed. | Resource URI, relevant failed startup requests and observed browser errors. Do not infer a network-interface or component cause without evidence. |
16
+ | `runner` | The harness, process, readiness or reporting boundary failed, or a test threw without assertion evidence. | Active/pending checks, timeout, failing step and artifacts. `certainty: "unclassified"` means ADS cannot establish the cause. |
17
+ | `assertion` | A reported assertion step or token binding check failed. | Expected behavior, source location, state and trace. This can still be a test defect; compare the intended contract before editing the component. |
18
+
19
+ A complete failed run can contain independent passing cases. Suite interruption,
20
+ missing/skipped cases, stale inputs and an unexplained process exit cannot certify
21
+ a pass. Keep the reported case outcome separate from whole-run acceptance.
22
+
23
+ ## Retrieve only the evidence needed
24
+
25
+ `ads_verify`, `ads_apply_patch` and `ads_apply_transaction` reply with counts,
26
+ bounded acceptance, at most three failure previews and `resultId`, within 4096
27
+ UTF-8 JSON bytes. Full validation still decides acceptance. Do not repeat a suite
28
+ merely to recover its output, or restate the complete report in conversation.
29
+
30
+ Read a relevant saved section:
31
+
32
+ ```js
33
+ ads_results({resultId: "<returned ID>", section: "outcomes", status: "not_passed"})
34
+ // Continue only when needed, with the same section and status:
35
+ ads_results({resultId: "<returned ID>", section: "outcomes", status: "not_passed", cursor: "<nextCursor>"})
36
+ ```
37
+
38
+ Pages default to 10 rows, allow at most 20 and stay within 8192 JSON bytes. The
39
+ byte cap can return fewer rows; `nextCursor` is the continuation. Other sections
40
+ are `checks` (pipeline steps), `diagnostics`, `advisories`, `warnings`, `changes`,
41
+ `captures`, `acceptance`, `investigation` (mutation boundary) and `selection`.
42
+ A row marked `detailTruncated` is a preview. To read that exact record, pass its
43
+ `recordIndex`, the same result/section, and `offset`/`chars` (maximum 2000 UTF-16
44
+ characters); follow `nextOffset`. Do not combine ranges with a cursor/status filter.
45
+ Read only the relevant log range. Reading pages or ranges never executes checks.
46
+
47
+ `ads://project/results` lists the latest IDs. `ads://results/<resultId>` reads a
48
+ summary; matching query parameters read pages/ranges. Only the latest verification
49
+ and latest mutation snapshots are retained, independently, up to 8 MiB each.
50
+ A superseded ID fails explicitly. An oversized report retains only its summary
51
+ with a warning; disk failure retains details in server memory until shutdown.
52
+ Neither changes actual acceptance or applied state. These are logical JSON byte
53
+ budgets, not token estimates; compatibility text can duplicate structured JSON.
54
+ Saved outcomes describe completion-time evidence, not current source freshness;
55
+ compare their revisions with `ads_behaviors` before claiming a current pass.
56
+
57
+ `ads_behaviors` pages owner summaries, then an exact unit's `checks`, `guarantees`,
58
+ `subjects`, `demonstrations` or `profiles`. Use `nextCursor` and identical filters;
59
+ changes invalidate a discovery cursor. Read a truncated contract via its
60
+ `sourceFile` and bounded `ads_read_file`, rather than requesting everything.
61
+
62
+ ## Get a compact investigation
63
+
64
+ Take an outcome's exact owner/check/run IDs from `ads_results`, then:
65
+
66
+ ```json
67
+ { "unit": "components/Example", "check": "keyboard-navigation", "runId": "<outcome runId>" }
68
+ ```
69
+
70
+ Send this to `ads_investigate`. It reads a summary within 4096 JSON bytes, without
71
+ running code. The run ID rejects evidence superseded by another execution.
72
+ `section: "details"`, `"requests"`, `"browserErrors"` or `"artifacts"` reads separate pages
73
+ (default 10/max 20 rows, at most 8192 JSON bytes); artifacts themselves require
74
+ explicit resource reads. Truncated investigation records also support
75
+ `recordIndex`/`offset`/`chars`; keep the exact `runId` to avoid mixing evidence.
76
+ Without a check, the tool pages available investigations.
77
+ `ads://project/investigations` lists available entries; the returned `resourceUri`
78
+ reads the same bundle. It records:
79
+
80
+ - Project versus isolated candidate, configured framework/profile and scenario.
81
+ - Exact input revision and the preview revision actually observed by the helper.
82
+ - `details.failingStep`, `details.source`, startup viewport and failure boundary.
83
+ - Counts for separately paged relevant requests, browser errors and artifacts.
84
+ - Available registered trace, screenshot or context artifact resource URIs.
85
+ - Explicit `acceptance.established` and `acceptance.unverified` scope.
86
+
87
+ Absent observations are `null` or empty; ADS does not invent a viewport, trace or
88
+ source line. Viewport evidence is labeled `preview-startup`; a test that resizes
89
+ later must collect its own failure-state evidence. Failed-request collection
90
+ covers managed startup, not a complete network trace. Bodies and headers are not
91
+ collected. Project-configured Playwright trace/screenshot policy controls which
92
+ artifacts exist; ADS does not turn on expensive recording or rerun a failed case.
93
+ Protocol runners may provide structured failure/step/location/artifact fields;
94
+ older reports remain valid and their unclassified errors stay explicit.
95
+
96
+ Only the latest bundle per registered check is exposed, including dry runs.
97
+ Another verification replaces it and removes old artifacts from resource access;
98
+ old runner output directories follow project cleanup policy. Artifact resources
99
+ are restricted to registered ordinary files under that run's output directory,
100
+ bounded to 20 MiB each and checked against their recorded digest on read.
101
+ Metadata is bounded to 2 MiB. Removed registrations are pruned on verification.
102
+
103
+ Candidate evidence never becomes passing live-source evidence. Compare the
104
+ recorded input revision with current `ads_behaviors` evidence; a bundle describes
105
+ its run, and does not claim current source merely because it remains available.
106
+ For a build/architecture rejection before behavior runs, inspect the mutation
107
+ saved `investigation` section via `ads_results`. Status retains the compact
108
+ execution summary and result ID, rather than embedding complete reports.
109
+
110
+ ## Preserve the design while repairing the failure
111
+
112
+ Fix the owner of the demonstrated defect. Do not privatize a valid API, introduce
113
+ demo-only props, substitute a styled link, change the reference baseline or weaken
114
+ verification policy to pass a tooling limitation. Preserve the draft and report
115
+ the smallest source/browser reproduction when ownership remains unprovable.
116
+
117
+ `acceptance` states what that result established. Core checks establish source
118
+ guard acceptance; registered tests establish only their actual cases/profiles.
119
+ Neither establishes untested states, visual parity, an accessibility audit or
120
+ another MCP host's compatibility. Captures show an appearance, not a transition.
@@ -15,8 +15,10 @@ ads_apply_patch({"unit":"components/Example","patchFile":"<returned patchFile>",
15
15
 
16
16
  The dry run validates the full candidate, including affected registered behavior
17
17
  checks in an isolated project copy. Configured browser checks use that candidate's
18
- managed preview, not the live showcase. Inspect the returned `behavior` failures
19
- or the registered behavior-check results in `checks`, fix the draft and diff again.
18
+ managed preview, not the live showcase. Read the summary, then page failing
19
+ `outcomes`, `diagnostics` or pipeline `checks` through `ads_results` with its
20
+ `resultId`. Inspect one exact outcome with `ads_investigate({unit,check,runId})`;
21
+ fix the draft and diff again. See [evidence.md](evidence.md) for page/range limits.
20
22
  Keep behavior.json/specs with their owner and use registered checks; a scratchpad
21
23
  run against live source cannot verify unapplied changes. After validation, apply
22
24
  with `dryRun:false`; ADS revalidates before writing. Dry-run evidence is not saved
@@ -120,9 +122,10 @@ them through their actual public composition/customization path and list their r
120
122
  render references in covers. Do not count internal production renders as public API
121
123
  coverage. Behavioral APIs use exercises and registered guarantees instead.
122
124
  Scenario controls still use public UI. A native link/button can supply semantics
123
- as the single direct child of a public runtime slot when source tracing proves
124
- that owner clones the child with its props. An `asChild` flag alone is insufficient;
125
- standalone native controls and custom scenario styling remain rejected. See
125
+ through a public runtime slot that demonstrably clones its child with owned
126
+ props, or through matching runtime-owned selectors attached to the actual rendered
127
+ DOM. Source ownership must be proven; an `asChild` flag or a lookalike marker is
128
+ insufficient. Standalone controls and custom scenario styling remain rejected. See
126
129
  [scenarios.md](scenarios.md) for evidence and limits. Preserve the intended
127
130
  composition instead of substituting an unrelated styled control or adding a
128
131
  demo-only component prop. Review framing belongs in public layout patterns.
@@ -101,6 +101,38 @@ do not export the helper solely for a test. Public/private ownership is a produc
101
101
  decision, not a coverage workaround. Browser checks verify transitions but do not
102
102
  replace the current static `covers` requirement; `exercises` does not waive it.
103
103
 
104
+ ## Native semantics through runtime-owned selectors
105
+
106
+ An anchor or other native semantic element can also occupy a position styled by
107
+ public runtime UI, without cloning or an `asChild` API:
108
+
109
+ ```tsx
110
+ // Runtime owner: the class is attached to Line's actual rendered span.
111
+ const line = css`:is(a, button) > & { color: var(--text-body); }`;
112
+ export function Line(props) { return <span className={line} {...props} />; }
113
+
114
+ // Scenario: semantics belong to the consumer, appearance to the runtime owner.
115
+ <a href={fixture.destination}><Line>{fixture.label}</Line></a>
116
+ ```
117
+
118
+ ADS traces the actual returned DOM, the attached Linaria template and matching
119
+ selector relationships. A public parent's `& > a` is evidence too. A tag-agnostic
120
+ parent selector such as `& > :has(> [data-part="line"])` must match a marker
121
+ declared by the actual runtime-owned child; a scenario-authored lookalike does not
122
+ qualify. No component names or marker spellings are reserved.
123
+
124
+ Unused styles, unrelated CSS, broad `& > *` selectors, negative selectors alone,
125
+ fabricated markers, unknown render alternatives and opaque class replacement
126
+ cannot grant permission. A selector cannot establish a scenario-built custom
127
+ widget's keyboard behavior. Inline styles and arbitrary classes remain rejected;
128
+ existing source-verified public class helpers keep their own contract.
129
+
130
+ Do not add a public link part or change the DOM to satisfy a cloning-only model.
131
+ Preserve semantic attributes and the intended public API. Unsupported source
132
+ grammar produces an inconclusive ownership diagnostic, not proof of a bad API.
133
+ Browser verification must establish the real focus treatment, hover/state
134
+ precedence, navigation and other promised behavior in the compiled App iframe.
135
+
104
136
  ## Native semantics through a public runtime slot
105
137
 
106
138
  A native element can be the direct child of a public runtime component that
@@ -84,16 +84,21 @@ For owner drafts, token-sample reconciliation, coupled transactions and local
84
84
  constant namespaces, read [guarded-editing.md](references/guarded-editing.md).
85
85
  For on-demand current/candidate screenshots and visual dry-run review, read
86
86
  [captures.md](references/captures.md).
87
+ For a rejected patch, failed check or loading error, read
88
+ [evidence and diagnosis](references/evidence.md); inspect the failing boundary
89
+ then page needed `ads_results` sections using the returned result ID. Inspect
90
+ one `ads_investigate({unit,check,runId})` summary before changing APIs or policy;
91
+ load request/error/artifact details only when relevant.
87
92
 
88
93
  For parent/child `covers`, conditional-control review, reference comparisons,
89
- public style-helper use, fragment-link navigation, or `scenario-render` /
90
- `showcase-class` findings, read [scenario contracts](references/scenarios.md). Its verification
94
+ public style-helper use, native slot/selector composition, fragment navigation,
95
+ or `scenario-render` / `showcase-control` / `showcase-class` findings, read [scenario contracts](references/scenarios.md). Its verification
91
96
  examples explain API visibility, reference baselines and state-transition checks.
92
97
  Traced parents can cover declared public children; verified public Linaria
93
98
  helpers do not grant custom scenario CSS.
94
99
 
95
100
  For atomic base/variant styling, helper/wrapper class composition, extracted
96
- custom-property arithmetic, or an
101
+ custom-property arithmetic, typed `@property` registrations, or an
97
102
  `atomic-composition-review` / `atomic-composition-limit` advisory, read
98
103
  [atomic style composition](references/atomic-styles.md). The same guide is served
99
104
  as `ads://guides/atomic-styles`. It explains shorthand/logical conflicts and
@@ -81,6 +81,54 @@ const treatment = css`
81
81
  This is a distinct state, not two competing base atoms. Whether changing padding
82
82
  on hover is desirable remains a product decision.
83
83
 
84
+ ## Register typed custom properties without atomizing their descriptors
85
+
86
+ Use a literal `@property` in its component/pattern's Linaria template when the
87
+ browser needs a typed, interpolated runtime variable:
88
+
89
+ ```tsx
90
+ const treatment = css`
91
+ @property --ds-example-progress {
92
+ syntax: "<number>";
93
+ inherits: false;
94
+ initial-value: 0;
95
+ }
96
+ opacity: var(--ds-example-progress);
97
+ `;
98
+ ```
99
+
100
+ The identifier is illustrative; use a unique name owned by the real unit.
101
+ Registrations are document-global, even when authored beside one component.
102
+ Put them at template level or inside conditional at-rules, never element selectors.
103
+ Use the same ADS tag resolver described below in both product and preview builds;
104
+ it preserves each complete registration and its conditional context instead of
105
+ splitting syntax/inherits/initial-value into invalid atomic rules. This works for
106
+ atomic `css` and `styled`; the browser receives CSS without a registration runtime.
107
+ MCP delivers it inside the App document, not the outer showcase host.
108
+
109
+ `property-registration` validates descriptor shape, CSS syntax/initial-value and
110
+ ownership, and rejects conflicting schemas or registrations shared by unrelated
111
+ owners. Identical registrations from the same owner are permitted. Runtime
112
+ `--ds-*` / `--ux-*` variables stay with their runtime owner. A shared token's
113
+ registration belongs beside its actual definition in a foundation value sheet;
114
+ a namespaced owner token's registration belongs in its registered `tokens.css`.
115
+ Neither `base.css` nor `fonts.css` becomes a registration escape hatch. Registration
116
+ alone does not define a token or satisfy its sample/fixture coverage.
117
+
118
+ Typed initial values must match their syntax and be computationally independent:
119
+ `0px` is valid metadata; `var(--space-4)`, `1rem` and `currentColor` are not.
120
+ The literal exemption applies only to registration descriptors. Keep design
121
+ assignments outside them token-backed; a technical fallback is not a place to
122
+ hide a component skin. Use literal static descriptors so guards can inspect them.
123
+ See the [W3C registration contract](https://www.w3.org/TR/css-properties-values-api-1/#at-property-rule).
124
+
125
+ Verify extracted CSS and actual interpolation, including after minification and
126
+ inside the MCP App. A declaration can parse while a browser ignores an invalid
127
+ registration. Keep required browser fallbacks and reduced-motion behavior; do not
128
+ replace a valid CSS implementation with script merely to evade extraction or a
129
+ guard limitation. These checks cover declarative registrations; arbitrary dynamic
130
+ `CSS.registerProperty()` calls are not an ownership proof.
131
+
84
132
  ## Preserve custom-property arithmetic during extraction
85
133
 
86
134
  Custom properties can hold an unevaluated token stream that becomes arithmetic