@reqvire-org/ads 0.3.18 → 0.3.21

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 (104) hide show
  1. package/README.md +2 -0
  2. package/dist/behaviors.mjs +19 -9
  3. package/dist/bootstrap-ds/ADS.md +2 -2
  4. package/dist/bootstrap-ds/README.md +2 -2
  5. package/dist/bootstrap-ds/design-system/index.ts +0 -1
  6. package/dist/bootstrap-ds/design-system/review/index.ts +1 -0
  7. package/dist/bootstrap-ds/design-system/tokens/{Foundation.scenario.tsx → Foundation.ads.scenario.tsx} +2 -2
  8. package/dist/bootstrap-ds/ds.config.json +1 -1
  9. package/dist/bootstrap-ds/tsconfig.build.json +9 -2
  10. package/dist/bootstrap-ds/tsconfig.json +18 -3
  11. package/dist/bootstrap-ds/vite.config.ts +1 -1
  12. package/dist/boundaries.mjs +6 -2
  13. package/dist/check.mjs +44 -17
  14. package/dist/config.mjs +33 -5
  15. package/dist/contract-migration.mjs +110 -0
  16. package/dist/contracts.d.mts +22 -0
  17. package/dist/evidence.mjs +1 -1
  18. package/dist/index.mjs +1 -0
  19. package/dist/init.mjs +18 -15
  20. package/dist/layers.mjs +288 -0
  21. package/dist/lint-config.mjs +1 -1
  22. package/dist/mcp/catalog.mjs +9 -2
  23. package/dist/mcp/context.mjs +2 -1
  24. package/dist/mcp/edit-preparation.mjs +9 -6
  25. package/dist/mcp/mutations.mjs +1 -1
  26. package/dist/mcp/preview-widget.bundle.js +41 -39
  27. package/dist/mcp/prompts.mjs +1 -1
  28. package/dist/mcp/server.mjs +9 -3
  29. package/dist/mcp/widget.bundle.js +1 -1
  30. package/dist/oxlint.mjs +3 -1
  31. package/dist/paint-contracts.mjs +124 -0
  32. package/dist/preview-resources.mjs +12 -2
  33. package/dist/public-api.mjs +13 -6
  34. package/dist/public-props.mjs +2 -2
  35. package/dist/runtime-contracts.mjs +225 -0
  36. package/dist/scenario-trace.mjs +37 -9
  37. package/dist/scenarios-vite.mjs +3 -2
  38. package/dist/scenarios.mjs +89 -30
  39. package/dist/scoped-tokens.mjs +9 -4
  40. package/dist/showcase/client.bundle.js +1 -1
  41. package/dist/showcase/sandbox.bundle.js +1 -1
  42. package/dist/skills/ads-break-ui/SKILL.md +25 -6
  43. package/dist/skills/ads-break-ui/references/contracts.md +250 -0
  44. package/dist/skills/ads-break-ui/references/guarded-editing.md +1 -1
  45. package/dist/skills/ads-break-ui/references/layers.md +123 -0
  46. package/dist/skills/ads-break-ui/references/review.md +96 -0
  47. package/dist/skills/ads-break-ui/references/scenarios.md +24 -3
  48. package/dist/skills/ads-designer/SKILL.md +26 -7
  49. package/dist/skills/ads-designer/assets/bootstrap-ds/ADS.md +2 -2
  50. package/dist/skills/ads-designer/assets/bootstrap-ds/README.md +2 -2
  51. package/dist/skills/ads-designer/assets/bootstrap-ds/design-system/index.ts +0 -1
  52. package/dist/skills/ads-designer/assets/bootstrap-ds/design-system/review/index.ts +1 -0
  53. package/dist/skills/ads-designer/assets/bootstrap-ds/design-system/tokens/{Foundation.scenario.tsx → Foundation.ads.scenario.tsx} +2 -2
  54. package/dist/skills/ads-designer/assets/bootstrap-ds/ds.config.json +1 -1
  55. package/dist/skills/ads-designer/assets/bootstrap-ds/tsconfig.build.json +9 -2
  56. package/dist/skills/ads-designer/assets/bootstrap-ds/tsconfig.json +18 -3
  57. package/dist/skills/ads-designer/assets/bootstrap-ds/vite.config.ts +1 -1
  58. package/dist/skills/ads-designer/references/contracts.md +250 -0
  59. package/dist/skills/ads-designer/references/guarded-editing.md +1 -1
  60. package/dist/skills/ads-designer/references/layers.md +123 -0
  61. package/dist/skills/ads-designer/references/review.md +96 -0
  62. package/dist/skills/ads-designer/references/scenarios.md +24 -3
  63. package/dist/skills/ads-import/SKILL.md +25 -6
  64. package/dist/skills/ads-import/references/contracts.md +250 -0
  65. package/dist/skills/ads-import/references/guarded-editing.md +1 -1
  66. package/dist/skills/ads-import/references/layers.md +123 -0
  67. package/dist/skills/ads-import/references/review.md +96 -0
  68. package/dist/skills/ads-import/references/scenarios.md +24 -3
  69. package/dist/skills/ads-migrate/SKILL.md +25 -6
  70. package/dist/skills/ads-migrate/references/contracts.md +250 -0
  71. package/dist/skills/ads-migrate/references/guarded-editing.md +1 -1
  72. package/dist/skills/ads-migrate/references/layers.md +123 -0
  73. package/dist/skills/ads-migrate/references/review.md +96 -0
  74. package/dist/skills/ads-migrate/references/scenarios.md +24 -3
  75. package/dist/skills/ads-motion/SKILL.md +26 -7
  76. package/dist/skills/ads-motion/references/audit.md +1 -1
  77. package/dist/skills/ads-motion/references/contracts.md +250 -0
  78. package/dist/skills/ads-motion/references/guarded-editing.md +1 -1
  79. package/dist/skills/ads-motion/references/layers.md +123 -0
  80. package/dist/skills/ads-motion/references/motion-review.md +35 -0
  81. package/dist/skills/ads-motion/references/review.md +96 -35
  82. package/dist/skills/ads-motion/references/scenarios.md +24 -3
  83. package/dist/token-origins.mjs +24 -0
  84. package/dist/tokens.mjs +4 -3
  85. package/dist/unit-contracts.mjs +203 -0
  86. package/dist/units.mjs +26 -17
  87. package/dist/verification/browser.mjs +156 -26
  88. package/dist/verification/playwright-reporter.cjs +13 -3
  89. package/docs/ADS.md +40 -6
  90. package/docs/ARCHITECTURE.md +73 -4
  91. package/docs/BEHAVIORS.md +15 -0
  92. package/docs/CHANGELOG.md +42 -0
  93. package/docs/CONTRACTS.md +250 -0
  94. package/docs/GUIDE.md +29 -3
  95. package/docs/MCP.md +42 -6
  96. package/package.json +4 -1
  97. package/dist/bootstrap-ds/design-system/patterns/token-preview/Scenario.tsx +0 -4
  98. package/dist/bootstrap-ds/design-system/patterns/token-preview/fixture.ts +0 -8
  99. package/dist/skills/ads-designer/assets/bootstrap-ds/design-system/patterns/token-preview/Scenario.tsx +0 -4
  100. package/dist/skills/ads-designer/assets/bootstrap-ds/design-system/patterns/token-preview/fixture.ts +0 -8
  101. /package/dist/bootstrap-ds/design-system/{patterns → review}/token-preview/TokenPreview.tsx +0 -0
  102. /package/dist/bootstrap-ds/design-system/tokens/{Foundation.fixture.ts → Foundation.ads.fixture.ts} +0 -0
  103. /package/dist/skills/ads-designer/assets/bootstrap-ds/design-system/{patterns → review}/token-preview/TokenPreview.tsx +0 -0
  104. /package/dist/skills/ads-designer/assets/bootstrap-ds/design-system/tokens/{Foundation.fixture.ts → Foundation.ads.fixture.ts} +0 -0
@@ -7,19 +7,24 @@ import { isDevelopmentFile } from "./config.mjs";
7
7
  import { foundationSelector } from "./foundation.mjs";
8
8
  import { isSharedToken, tokenContract } from "./token-policy.mjs";
9
9
  import { propertyRegistration } from "./property-registrations.mjs";
10
+ import { inspectUnitContracts } from "./unit-contracts.mjs";
10
11
 
11
12
  /** An explicit owner contract, never a heuristic based on a token's name. */
12
13
  export function scopedTokens(options) {
13
14
  if (options.scopedTokens) return options.scopedTokens;
14
- const inventory = inspectUnits(options), definitions = [], diagnostics = [];
15
+ const inventory = inspectUnits(options), definitions = [], declarations = inspectUnitContracts(options), diagnostics = [...declarations.diagnostics];
15
16
  const report = (file, message, line = 1) => diagnostics.push({rule:"scoped-token", file, message, line, column:1});
16
17
  for (const unit of inventory.units.filter(unit => ["components", "patterns"].includes(unit.kind))) {
17
- for (const manifest of unit.files.filter(file => /(?:^|\/)tokens\.json$|\.tokens\.json$/.test(file))) {
18
+ const inputs = [
19
+ ...unit.files.filter(file => /(?:^|\/)tokens\.json$|\.tokens\.json$/.test(file)).map(manifest => ({manifest})),
20
+ ...declarations.contracts.filter(entry => entry.unit === unit.id && entry.tokens).map(entry => ({manifest:entry.file, contract:entry.tokens})),
21
+ ];
22
+ for (const {manifest, contract} of inputs) {
18
23
  const absolute = path.join(options.root, manifest);
19
24
  try {
20
25
  if (statSync(absolute).size > 65536 || realpathSync(absolute) !== absolute) throw new Error("Token manifests must be ordinary files no larger than 64 KiB.");
21
- const data = JSON.parse(readFileSync(absolute, "utf8"));
22
- const stylesheet = manifest.replace(/\.json$/, ".css");
26
+ const data = contract ? {schemaVersion:1, namespace:contract.namespace} : JSON.parse(readFileSync(absolute, "utf8"));
27
+ const stylesheet = contract ? contract.file : manifest.replace(/\.json$/, ".css");
23
28
  if (!data || data.schemaVersion !== 1 || Object.keys(data).some(key => !["schemaVersion", "namespace"].includes(key))
24
29
  || typeof data.namespace !== "string" || !/^--[a-z][a-z0-9-]*-$/.test(data.namespace)
25
30
  || /^(?:--ds-|--ux-|--showcase-)/.test(data.namespace) || isSharedToken(data.namespace + "value")
@@ -113,7 +113,7 @@ Boolean requesting whether a visible border and background is provided by the ho
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
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.
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 allow-forms"),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 allow-forms"});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:
@@ -1 +1 @@
1
- function c(e,t){let n=new DOMParser().parseFromString(e,"text/html"),r=n.createElement("meta");if(r.httpEquiv="Content-Security-Policy",r.content=t,n.head.prepend(r),!n.querySelector("base[href]")){let i=n.querySelector("base")??n.createElement("base");i.setAttribute("href","about:srcdoc"),r.after(i)}return"<!doctype html>"+n.documentElement.outerHTML}var a=new URL(location.href).searchParams.get("parent"),o;function s(e=[]){return e.filter(t=>/^(?:https?:\/\/[a-z0-9.*:[\]-]+(?::\d+)?|data:|blob:|about:)$/i.test(t)).join(" ")}function d(e={}){let t=s(e.resourceDomains);return`default-src 'none';script-src 'unsafe-inline' ${t};style-src 'unsafe-inline' ${t};img-src data: ${t};font-src ${t||"'none'"};media-src ${t||"'none'"};worker-src ${t||"'none'"};connect-src ${s(e.connectDomains)||"'none'"};frame-src ${s(e.frameDomains)||"'none'"};base-uri about: ${s(e.baseUriDomains)};object-src 'none'`}window.addEventListener("message",e=>{if(e.source===parent&&e.origin===a){let t=e.data;if(t?.method==="ui/notifications/sandbox-resource-ready"){let{html:n,csp:r}=t.params??{};if(typeof n!="string")return;o?.remove(),o=window.document.createElement("iframe"),o.title="Design-system App",o.setAttribute("sandbox","allow-scripts"),window.document.body.append(o),o.srcdoc=c(n,d(r))}else t?.jsonrpc==="2.0"&&!t.method?.startsWith("ui/notifications/sandbox-")&&o?.contentWindow.postMessage(t,"*")}else e.source===o?.contentWindow&&e.data?.jsonrpc==="2.0"&&!e.data.method?.startsWith("ui/notifications/sandbox-")&&parent.postMessage(e.data,a)});parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-ready",params:{}},a);
1
+ function c(e,t){let o=new DOMParser().parseFromString(e,"text/html"),r=o.createElement("meta");if(r.httpEquiv="Content-Security-Policy",r.content=t,o.head.prepend(r),!o.querySelector("base[href]")){let i=o.querySelector("base")??o.createElement("base");i.setAttribute("href","about:srcdoc"),r.after(i)}return"<!doctype html>"+o.documentElement.outerHTML}var a=new URL(location.href).searchParams.get("parent"),n;function s(e=[]){return e.filter(t=>/^(?:https?:\/\/[a-z0-9.*:[\]-]+(?::\d+)?|data:|blob:|about:)$/i.test(t)).join(" ")}function d(e={}){let t=s(e.resourceDomains);return`default-src 'none';script-src 'unsafe-inline' ${t};style-src 'unsafe-inline' ${t};img-src data: ${t};font-src ${t||"'none'"};media-src ${t||"'none'"};worker-src ${t||"'none'"};connect-src ${s(e.connectDomains)||"'none'"};frame-src ${s(e.frameDomains)||"'none'"};base-uri about: ${s(e.baseUriDomains)};object-src 'none';form-action 'none'`}window.addEventListener("message",e=>{if(e.source===parent&&e.origin===a){let t=e.data;if(t?.method==="ui/notifications/sandbox-resource-ready"){let{html:o,csp:r}=t.params??{};if(typeof o!="string")return;n?.remove(),n=window.document.createElement("iframe"),n.title="Design-system App",n.setAttribute("sandbox","allow-scripts allow-forms"),window.document.body.append(n),n.srcdoc=c(o,d(r))}else t?.jsonrpc==="2.0"&&!t.method?.startsWith("ui/notifications/sandbox-")&&n?.contentWindow.postMessage(t,"*")}else e.source===n?.contentWindow&&e.data?.jsonrpc==="2.0"&&!e.data.method?.startsWith("ui/notifications/sandbox-")&&parent.postMessage(e.data,a)});parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-ready",params:{}},a);
@@ -59,6 +59,19 @@ one `ads_investigate({unit,check,runId})` summary before changing APIs or policy
59
59
  load request/error/artifact details only when relevant.
60
60
 
61
61
 
62
+ Review/demo layout belongs in development-only `review/`, exported through the
63
+ separate `${publicAlias}/review` entry (normally `@ds/review`), never the production
64
+ barrel. Product patterns are application UI. For ownership, migration, token
65
+ renderers, build aliases or `review-import` / `review-contract` diagnostics, read
66
+ [development review UI](references/review.md), also served as `ads://guides/review`.
67
+
68
+ A component provides one cohesive UI capability; a pattern arranges capabilities
69
+ into reusable structures or workflows. Components may compose components/controllers;
70
+ layout patterns may arrange slots. Do not classify by component count. For owner
71
+ selection or `component-dependency`, `component-contract` or `component-style`, read
72
+ [layer responsibilities and contracts](references/layers.md), also served as
73
+ `ads://guides/layers`. Preserve public APIs and documented non-state styling hooks.
74
+
62
75
  For parent/child `covers`, conditional-control review, reference comparisons,
63
76
  public style-helper use, native slot/selector composition, fragment navigation,
64
77
  or `scenario-render` / `showcase-control` / `showcase-class` findings, read [scenario contracts](references/scenarios.md). Its verification
@@ -221,7 +234,7 @@ fixture under the configured token directory. Declare metadata
221
234
  The renderer name is project-owned; use its real public API. Each literal fixture
222
235
  sample declares `{ token, property, probe }`: a real token, the CSS property its
223
236
  sample demonstrates, and a valid test value different from that token's current
224
- value. Pass `samples` into the rendered public review pattern. Names, sample text,
237
+ value. Pass `samples` into the rendered development-only review renderer. Names, sample text,
225
238
  bindings and probes are fixture data; displayed values come from foundation CSS.
226
239
  There is no property catalogue, token-name mapping or probe palette in ADS.
227
240
  Every token needs sample coverage; fabricated tokens, missing bindings/probes,
@@ -242,10 +255,12 @@ source definitions and values; inspecting definitions does not prove UI parity.
242
255
  The tool keeps the shared naming contract; sample templates and their token
243
256
  lists belong to the project.
244
257
 
245
- A public review pattern may be covered by real token or composition scenarios;
246
- it needs no redundant standalone entry. Remove an optional scenario only while
247
- retaining reachable render coverage. Keep directory ownership anchored by its
248
- runtime entry (`index` or a file matching the directory name) or colocated fixture.
258
+ Development review helpers need no product catalogue scenario. A token scenario
259
+ may name its review renderer in `covers`; ordinary component/pattern scenarios
260
+ cover their production subjects. Keep review exports in the separate review entry.
261
+ Remove redundant helper catalogue scenarios during migration. Product UI retains
262
+ its real render-coverage requirement. Keep directory ownership anchored by its
263
+ implementation entry (`index` or a file matching the directory name) or colocated fixture.
249
264
  Import colocated image/font fixtures so MCP serves them through asset resources.
250
265
  For a broken-image case, use a local failing fixture, rather than an external
251
266
  network failure or a domain-specific CSP exception. Production asset APIs remain
@@ -269,7 +284,7 @@ branch, property and value must fit its role. Token-backed control skins, layout
269
284
  interactive state rules and owner hooks are rejected even through `:where()`.
270
285
  Do not copy an unchecked upstream preflight or turn a component repair into a reset.
271
286
  Keep behavior and skin in their runtime owner. Put demo-only sizing and framing
272
- in a public review/layout pattern; add component props for a deliberate runtime
287
+ in a development-only review helper; add component props for a deliberate runtime
273
288
  API need. Preserve required capabilities while following ownership rules.
274
289
  Use tokens for absolute/font-relative lengths, including `ch`, `ex`, `cap`, `ic`
275
290
  and `lh`; calculations and fallback values follow the same rule. Structural
@@ -340,3 +355,7 @@ rendered check.
340
355
 
341
356
  Source credits and retained MIT terms: [README.md](README.md) and
342
357
  [LICENSE.upstream](LICENSE.upstream).
358
+
359
+ For static owner contracts, `.ads.` development roles, typed paint/binding APIs,
360
+ declarative evidence, token privacy and guarded migration, read
361
+ [contracts.md](references/contracts.md) when the task touches those contracts.
@@ -0,0 +1,250 @@
1
+ # Static owner contracts and development files
2
+
3
+ Use one optional `<Owner>.ads.contract.ts` for owner-local tokens, constrained
4
+ paint inputs, scoped runtime bindings, declarative parts and behavioral checks.
5
+ The import `import type { UnitContract } from "@reqvire-org/ads/contracts"` supplies
6
+ development typing. ADS reads its AST and resolves real symbols without importing
7
+ or executing project modules. It adds no production runtime.
8
+
9
+ ## Files and ownership
10
+
11
+ ```text
12
+ components/Plot/
13
+ Plot.tsx production API/root
14
+ Series.tsx production declarative API
15
+ renderer.tsx production consumer
16
+ paints.ts production binding producer
17
+ types.ts production prop types
18
+ tokens.css optional production token definitions
19
+ Plot.ads.contract.ts static tool declaration
20
+ Plot.ads.scenario.tsx demonstration
21
+ Plot.ads.fixture.ts demonstration data
22
+ Tokens.ads.scenario.tsx real owner-token samples
23
+ Tokens.ads.fixture.ts sample bindings/probes
24
+ Plot.ads.behavior.spec.ts registered browser checks
25
+ Plot.ads.test.tsx optional unit tests
26
+ Plot.ads.helpers.ts development-only adapters
27
+ README.md API/provenance
28
+ ```
29
+
30
+ This is an example, not a required inventory. Use the same roles for patterns,
31
+ controllers and relevant development owners. `review/` remains a separate
32
+ shared development layer; its implementation filenames need no extra marker.
33
+ Runtime components, types and styles cannot import `.ads.` files, tests, fixtures
34
+ or review code through aliases, barrels or helpers. Unknown `.ads.` roles are
35
+ still development-only and are diagnosed. Fixtures/helpers cannot define styles;
36
+ put demo framing in review. Ordinary README/license files retain their names.
37
+
38
+ Scenarios/fixtures/review enter only the selected preview closure. Contracts are
39
+ never executable preview modules; derived audit descriptors are data. Specs run
40
+ only in the configured test runner. Production declaration emission must also
41
+ start from the public runtime entry, rather than include every file under the
42
+ design-system folder. The starter’s `tsconfig.build.json` does this; its normal
43
+ no-emit typecheck still checks development sources. Core import guards protect
44
+ the runtime dependency closure. Review custom emitter configurations through
45
+ the configuration owner before migrating a consuming project. Guards remain tooling. Production prop types
46
+ belong in runtime files, never inferred from a development contract.
47
+
48
+ ## A contract uses actual implementation references
49
+
50
+ ```ts
51
+ import type { UnitContract } from "@reqvire-org/ads/contracts";
52
+ import { Plot } from "./Plot";
53
+ import { Series } from "./Series";
54
+ import { Renderer } from "./renderer";
55
+ import { paints } from "./paints";
56
+
57
+ export default {
58
+ schemaVersion: 1,
59
+ tokens: { namespace: "--plot-", stylesheet: "./tokens.css" },
60
+ paintInputs: [
61
+ { api: Plot, property: "config.*.paint", kind: "paint-reference" },
62
+ { api: Series, property: "fill", kind: "paint-reference", bindings: ["items"] },
63
+ ],
64
+ bindings: {
65
+ items: {
66
+ input: { api: Plot, property: "config" },
67
+ producer: paints,
68
+ valueFrom: "paint",
69
+ variable: "--plot-runtime-${key}",
70
+ scope: { api: Plot, selector: '[data-plot-root=""]' },
71
+ checks: ["isolation"],
72
+ },
73
+ },
74
+ declarativeParts: [
75
+ { api: Series, parent: Plot, consumer: Renderer, checks: ["isolation"] },
76
+ ],
77
+ behavior: {
78
+ subjects: { plot: { api: Plot }, renderer: { implementation: Renderer } },
79
+ guarantees: [{
80
+ id: "scoped-output",
81
+ description: "Changing either instance updates only its own output.",
82
+ subjects: ["plot", "renderer"], checks: ["isolation"],
83
+ }],
84
+ checks: {
85
+ isolation: {
86
+ file: "./Plot.ads.behavior.spec.ts", case: "isolation",
87
+ scenario: "components/Plot", profile: "widget",
88
+ },
89
+ },
90
+ },
91
+ } satisfies UnitContract;
92
+ ```
93
+
94
+ Keep contracts small; most owners need none or one section. Supported data are
95
+ literal objects/arrays, static constants/spreads, `as const` and `satisfies`.
96
+ Symbol references resolve through normal imports, aliases and re-exports.
97
+ Calls, getters, methods, computed metadata and environment-dependent expressions
98
+ are rejected. Function references are opaque identities, never executed.
99
+ Private implementation subjects may be internal module exports without becoming
100
+ public barrel exports. Producers/consumers must belong to the same owner. Required
101
+ checks must exist, back named guarantees and use a browser profile with the
102
+ corresponding scenario. A relationship is registration, not a passing guarantee.
103
+
104
+ ## Typed paints and runtime bindings
105
+
106
+ Production-facing types can use:
107
+
108
+ ```ts
109
+ export type Paint = `var(--${string})` | `url(#${string})` | "none" | "currentColor";
110
+ export interface SeriesProps { fill?: Paint }
111
+ export type Config = Readonly<Record<string, { paint: Paint }>>;
112
+ ```
113
+
114
+ ADS follows the actual property path/type, including nested maps. A name such as
115
+ `Paint`, an arbitrary `string`, `any` or a forged metadata role grants no waiver.
116
+ Supported references are a single accessible token/binding `var(--name)`, a local
117
+ SVG paint `url(#id)`, `none` or `currentColor`. Fallbacks, raw colours, external
118
+ URLs and arbitrary CSS are outside this role. Raw domain colours need a separately
119
+ reviewed validated input boundary; they are not implicitly supported here.
120
+
121
+ A runtime family is instance data, not an infinite design-token inventory. Its
122
+ source proof requires the declared config map, exact key/value mapping and actual
123
+ root attachment. Supported producers use `Object.entries(config)` with a loop,
124
+ or `Object.fromEntries(...map(...))`; same-owner helper returns are traceable.
125
+ Guard each key before the write/return using
126
+ `if (!/^[A-Za-z0-9_-]+$/.test(key)) throw new Error("Invalid key")` (a loop may
127
+ `continue`). Return only the computed map. For example:
128
+
129
+ ```ts
130
+ export function paints(config: Config) {
131
+ const result: Record<string, string> = {};
132
+ for (const [key, entry] of Object.entries(config)) {
133
+ if (!/^[A-Za-z0-9_-]+$/.test(key)) throw new Error("Invalid key");
134
+ result[`--plot-runtime-${key}`] = entry.paint;
135
+ }
136
+ return result;
137
+ }
138
+ ```
139
+
140
+ The runtime root attaches that returned map using `style={paints(config)}` or a
141
+ traceable local/useMemo result and the declared class/exact data attribute.
142
+ Place spreading props before the explicit scope/style so an unknown spread
143
+ cannot replace them. This grants permission to that binding attachment only;
144
+ it does not authorize arbitrary inline styling, CSSProperties APIs or bulk writes.
145
+ Unsupported dynamic producers/attachment paths fail with an analysis diagnostic.
146
+ Keep the implementation project-owned; there is no required ADS binding helper.
147
+
148
+ Families cannot overlap shared tokens, owner-token namespaces, private runtime
149
+ prefixes or other bindings. A broad family such as `--color-${key}` grants no
150
+ shared-token exception. Use a distinct private family where collision freedom
151
+ cannot be established. Registered paint consumers list their allowed binding IDs;
152
+ borrowing a peer's family or private token remains invalid. Bindings are scoped
153
+ to their actual root; portals outside it need deliberate project integration.
154
+
155
+ The App audit checks current mounted instances, accessible values, local SVG
156
+ resources and computed paint-property responses. Visibility and complete rendered
157
+ output still need assertions in the registered browser checks. It rejects missing/duplicate IDs and reports unobserved written values separately. It is bounded to 100 families, 20 instances per family, 100 keys
158
+ and 1000 elements per instance. Exceeding a budget reports limits and complete: false; it does not invalidate a
159
+ working component. An unobserved paint effect also remains unverified until its
160
+ relevant consumer/state is checked. It does
161
+ not prove every future input, visibility state or interaction. In registered
162
+ Playwright checks, use `auditBehaviorPaints(frame)` from
163
+ `@reqvire-org/ads/verification` after updates. Assert `result.ok` and, when claiming complete current-map
164
+ evidence, `result.complete` and the expected `verified` entries.
165
+ Exercise two instances with identical keys/different paints, key updates/removal,
166
+ themes, local SVG IDs and invalid/inaccessible references. Keep normal runner
167
+ profiles; React StrictMode still needs a project development harness.
168
+
169
+ ## Rendered and declarative evidence
170
+
171
+ `covers` names rendered public UI. A null-returning declarative part belongs in
172
+ `exercises`, alongside behavioral APIs:
173
+
174
+ ```tsx
175
+ export const scenario = {
176
+ title: "Plot", covers: ["Plot"], exercises: ["Series"],
177
+ };
178
+ ```
179
+
180
+ The real part must reach its registered consumer through a rendered parent.
181
+ ADS resolves source identity comparisons (`child.type === Series`, including a
182
+ traceable comparison helper) and reads of that same child’s props traced from the consumer’s children input.
183
+ A comparison against a decoy object gives no evidence. An import, unrelated parent, unused
184
+ child or metadata claim gives no consumption credit. Unknown render branches are
185
+ conservative. Browser checks must verify that adding/removing/changing the part
186
+ changes output. Source relationships and current browser evidence are separate.
187
+ Do not add fake DOM, hide public parts or rewrite a valid API to appease tracing.
188
+
189
+ Scenario metadata is validated by field/entry with positions and reasons.
190
+ A bad entry still blocks acceptance while valid independent render evidence is
191
+ retained. Unreadable prerequisites report coverage as not established. Inspect
192
+ the exact source diagnostic and bounded results; do not assume every subsequent
193
+ missing preview means its runtime API is invalid.
194
+
195
+ ## Local tokens and project consistency
196
+
197
+ `tokens.css` remains optional production design data for component/pattern owners,
198
+ imported by the runtime owner. Controllers stay headless; review uses the shared
199
+ vocabulary and its own development presentation state. Only root/theme definitions belong there. Tokens require real sample
200
+ coverage, including independently built owner token scenarios. One namespace
201
+ cannot be shared by peer components. Private references remain private through
202
+ TS constants, re-exports, helper returns, SVG/paint props and property reads/writes.
203
+ Normal composition of a public component using its own tokens remains valid.
204
+ Source analysis covers supported value flows; arbitrary dynamic code is not
205
+ proof of ownership. CSS variables remain browser-visible; namespacing is an
206
+ architecture boundary, not browser encapsulation.
207
+
208
+ Ownership does not establish a project's visual scale:
209
+
210
+ - A component redefining shared `--space-4` fails.
211
+ - Its private `--plot-gap: 7px` can be valid component geometry.
212
+ - `--plot-gap: var(--space-4)` preserves a shared scale with local meaning.
213
+
214
+ Choose project policy explicitly. A registered project check can use tooling-only
215
+ `inspectTokens(root)` and `inspectTokenOrigins(inventory, name, { stopAt: names })`
216
+ from `@reqvire-org/ads`. `aliasOnly` verifies every theme definition follows only
217
+ aliases to the explicitly selected origins; `literals` and `diagnostics` explain
218
+ violations/limits. Select the exact targets and permitted shared origins. Keep
219
+ intentional geometry exceptions exact with documented reasons. ADS neither
220
+ infers spacing meaning from names nor prescribes a global spacing palette.
221
+
222
+ ## Migration and MCP editing
223
+
224
+ Legacy `tokens.json`/`<Owner>.tokens.json` and `behavior.json` remain readable.
225
+ Use either authoring form per section; competing declarations fail. New contracts
226
+ normalize into the same discovery, ownership, source revisions, affected-check
227
+ selection and candidate gates. `ads_units` exposes `contractFile`; `ads_catalog`
228
+ reports constrained paint paths and declarative parent/consumer/check references.
229
+ Use bounded file reads and paged detailed results, not repeated whole-project dumps.
230
+
231
+ Install supporting ADS tooling before migrating a consuming project and reconnect
232
+ MCP before replacing a package used by a running server. Then:
233
+
234
+ 1. Discover one owner with `ads_units`.
235
+ 2. `ads_prepare_patch({ unit, action: "migrate" })` creates a normal local draft.
236
+ 3. Review the converted contract, renamed files/imports and migration notes.
237
+ 4. `ads_prepare_patch({ unit, action: "diff", draft })` emits its Git patchFile.
238
+ 5. Dry-run/apply with `ads_apply_patch`, or a genuinely coupled transaction.
239
+
240
+ Migration never edits live source or bypasses gates. It retains stable scenario
241
+ IDs and check case IDs. An optional static `scenario.id` preserves a legacy
242
+ identity when a renamed filename would otherwise be ambiguous; it must stay
243
+ inside the owning unit and be unique. Whole-file or unimportable private behavior subjects stay
244
+ in legacy registration with a note until deliberately resolved. Unknown runner
245
+ globs, custom adapters and documentation references require review. Ambiguous
246
+ flat-to-directory renames stay in the legacy form with a migration note so file
247
+ ownership cannot silently change. Preserve
248
+ licenses, pending drafts and unrelated work; keep transactions within their limits.
249
+ There is no alternate JSON contract editor. Do not loosen a guard or make a valid
250
+ API private merely to complete a migration.
@@ -103,7 +103,7 @@ and a definition-only `tokens.css`, imported by its runtime source. Flat owners
103
103
  shared or --ds-/--ux-/--showcase- variables. Only that owner consumes/defines its
104
104
  local tokens; aliases can use shared or own tokens. UI styles stay in Linaria.
105
105
  Add a colocated token scenario with `kind:"tokens"`, real fixture `samples` and a
106
- public review pattern. These inputs belong to the same owner and need real sample
106
+ development-only review renderer. These inputs belong to the same owner and need real sample
107
107
  coverage. Manifests and fixtures stay out of the product bundle.
108
108
 
109
109
  `ads_prepare_token_samples({id, bindings?})` preserves existing project samples,
@@ -0,0 +1,123 @@
1
+ # Components, patterns and application adapters
2
+
3
+ Read this when choosing an owner, moving UI between layers, or resolving
4
+ `component-dependency`, `component-contract`, `component-style`, `no-app-import`
5
+ or `showcase-import`. MCP also serves this guide as `ads://guides/layers`.
6
+
7
+ ## Choose by responsibility
8
+
9
+ A **component** provides one cohesive UI capability: semantics, input handling,
10
+ focus, accessibility, states and appearance. It may compose other components and
11
+ controllers. A notification using an icon, action button and spinner can still
12
+ be one component.
13
+
14
+ A **pattern** arranges capabilities and content slots into a reusable structure
15
+ or workflow, such as navigation with search/account controls or a report screen.
16
+ Layout parts may arrange `children` without importing several components.
17
+ Patterns may compose other patterns. A native wrapper or a large implementation
18
+ does not decide the layer.
19
+
20
+ An **application adapter** supplies domain data, store access, route state and
21
+ navigation/service callbacks. Domain-shaped inputs are valid in project-owned
22
+ patterns; importing a particular application store/router is not.
23
+
24
+ A **scenario** supplies demonstration inputs, local state and simulated services.
25
+ A component scenario may compose several public components and development review helpers
26
+ to demonstrate its subject. That does not turn its fixture or variant selectors
27
+ into production API. Keep `covers` aligned with the scenario's subject layer.
28
+
29
+ Do not classify by import count, JSX element count, names or file length. Review
30
+ the capability and its consumer contract. Preserve valid APIs when tracing is
31
+ inconclusive; do not make an export private or add a product prop just to pass.
32
+
33
+ ## Demonstrate the production implementation
34
+
35
+ Product patterns are reusable application UI, not showcase replicas. A scenario
36
+ imports the same production owner and supplies fixtures through its public API;
37
+ the application supplies live data and callbacks to that API. Keep mock records,
38
+ demo selections and simulated services in development files. Legitimate defaults,
39
+ control labels and the pattern's own interaction logic remain with their owner.
40
+
41
+ Demo framing must not replace a product pattern's styling or behavior. Browser
42
+ checks should exercise both the demonstrated contract and relevant application
43
+ integration; a successful showcase does not establish production parity. A new
44
+ pattern need not already have an application consumer, and import counts cannot
45
+ detect a duplicated implementation. Existing import guards prevent fixture/test
46
+ dependencies; review and browser checks must assess hardcoded mock data and
47
+ unrepresentative rendering that source ownership alone cannot prove.
48
+
49
+ Review framing belongs in development-only `review/`, not production patterns.
50
+ Use the separate review entry and [review.md](review.md) for the migration and
51
+ build contract.
52
+
53
+ ## Dependency contracts (blocking)
54
+
55
+ - Component runtime may depend on components, shared controllers and lower-level
56
+ helpers. It cannot depend on patterns or composition, including indirectly
57
+ through aliases, forwarding exports, helpers or literal dynamic imports.
58
+ Type-only pattern references do not introduce runtime dependencies.
59
+ - Patterns consume component symbols published by the configured public entry,
60
+ including intentional public types. A relative import of a public symbol is
61
+ allowed inside the ADS; requiring a self-import through the barrel can create
62
+ cycles. Private state, class blocks and implementation helpers stay internal.
63
+ A public component may use its own private implementation dependencies.
64
+ - Runtime code cannot import review implementations/types/styles, scenarios, fixtures or checks, including through
65
+ forwarding exports. Scenarios also cannot import application source.
66
+ - ADS implementations receive application data/effects through inputs and
67
+ callbacks. Re-exporting application stores, routers or services from a helper
68
+ does not make them part of the ADS.
69
+
70
+ For example, `components/Control → shared/helper → patterns/Navigation` fails
71
+ `component-dependency`. Extract a lower-level capability/controller, or move the
72
+ arrangement to a pattern. `patterns/Navigation → components/Control` is valid
73
+ when `Control` is public. Replacing that import with its private state helper
74
+ fails `component-contract` even through a forwarding module.
75
+
76
+ ## Styling contracts (blocking when ownership is established)
77
+
78
+ Components own their control states and appearance. Patterns own arrangement,
79
+ spacing and their own presentation. Use typed component props/variants, an
80
+ intentional public class helper/hook, or registered non-state customization
81
+ variables. Registration does not authorize overriding interaction policy.
82
+
83
+ ```tsx
84
+ // Component-owned implementation; the marker is not a consumer styling API.
85
+ function Control() {
86
+ return <button><span className="control-private-label">Label</span></button>;
87
+ }
88
+
89
+ // Pattern: rejected because it styles the component's private rendered part.
90
+ const wrong = css`
91
+ & .control-private-label { color: var(--text-muted); }
92
+ `;
93
+
94
+ // Pattern: its own arrangement remains valid.
95
+ const layout = css`
96
+ display: flex;
97
+ gap: var(--space-4);
98
+ `;
99
+ ```
100
+
101
+ The source guard matches component-emitted class names and unambiguous literal
102
+ data markers to the selector's styled subject, including `:is()`/`:where()`.
103
+ It also rejects reads of known private component variables. It does not treat
104
+ an ancestor predicate or `:has()` query as styling that ancestor/descendant:
105
+ `[data-layout="expanded"] > & { gap: var(--space-4); }` changes the pattern's
106
+ own arrangement. Public hooks and registered non-state variables remain valid.
107
+ Token definitions, namespaces and writes retain their existing guards.
108
+
109
+ Opaque/computed imports, generated selectors, ambiguous shared markers, forwarded
110
+ consumer classes and arbitrary service abstractions are not a complete static
111
+ proof. Passing source checks does not prove that every visual override is safe.
112
+ Review the real rendered states, computed styles, keyboard interactions and
113
+ responsive layouts; use registered browser checks for promised behavior.
114
+
115
+ ## Refactoring an owner
116
+
117
+ Move runtime source, its scenarios/fixtures and guides together; update public
118
+ exports, dependent imports and documented scenario IDs. Keep callers on the
119
+ same intentional public API. Navigation stays in an application callback or a
120
+ normal link supplied by the consumer, rather than a hidden router mutation in
121
+ the pattern. Validate the complete migration with project checks, product and
122
+ preview builds, and behavior tests. An existing guard rejection is not permission
123
+ to write around the guarded editing workflow.