@reqvire-org/ads 0.3.12 → 0.3.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +20 -1
  2. package/dist/bootstrap-ds/ADS.md +4 -1
  3. package/dist/capture/worker.mjs +38 -0
  4. package/dist/capture-store.mjs +123 -0
  5. package/dist/capture.mjs +117 -0
  6. package/dist/check.mjs +8 -2
  7. package/dist/config.mjs +6 -1
  8. package/dist/mcp/context.mjs +1 -1
  9. package/dist/mcp/mutations.mjs +25 -5
  10. package/dist/mcp/server.mjs +62 -15
  11. package/dist/scenario-slots.mjs +124 -0
  12. package/dist/scenarios.mjs +9 -3
  13. package/dist/showcase/client.bundle.js +18 -18
  14. package/dist/showcase/style.css +2 -0
  15. package/dist/skills/ads-break-ui/SKILL.md +6 -1
  16. package/dist/skills/ads-break-ui/references/captures.md +147 -0
  17. package/dist/skills/ads-break-ui/references/guarded-editing.md +72 -3
  18. package/dist/skills/ads-break-ui/references/scenarios.md +40 -1
  19. package/dist/skills/ads-designer/SKILL.md +6 -1
  20. package/dist/skills/ads-designer/assets/bootstrap-ds/ADS.md +4 -1
  21. package/dist/skills/ads-designer/references/captures.md +147 -0
  22. package/dist/skills/ads-designer/references/guarded-editing.md +72 -3
  23. package/dist/skills/ads-designer/references/scenarios.md +40 -1
  24. package/dist/skills/ads-import/SKILL.md +6 -1
  25. package/dist/skills/ads-import/references/captures.md +147 -0
  26. package/dist/skills/ads-import/references/guarded-editing.md +72 -3
  27. package/dist/skills/ads-import/references/scenarios.md +40 -1
  28. package/dist/skills/ads-migrate/SKILL.md +6 -1
  29. package/dist/skills/ads-migrate/references/captures.md +147 -0
  30. package/dist/skills/ads-migrate/references/guarded-editing.md +72 -3
  31. package/dist/skills/ads-migrate/references/scenarios.md +40 -1
  32. package/dist/skills/ads-motion/SKILL.md +6 -1
  33. package/dist/skills/ads-motion/references/captures.md +147 -0
  34. package/dist/skills/ads-motion/references/guarded-editing.md +72 -3
  35. package/dist/skills/ads-motion/references/scenarios.md +40 -1
  36. package/dist/source-values.mjs +2 -1
  37. package/dist/verification/browser.mjs +8 -3
  38. package/dist/verification/playwright-reporter.cjs +23 -8
  39. package/dist/verification.mjs +142 -33
  40. package/docs/ADS.md +10 -2
  41. package/docs/ARCHITECTURE.md +43 -2
  42. package/docs/BEHAVIORS.md +66 -7
  43. package/docs/CHANGELOG.md +27 -0
  44. package/docs/GUIDE.md +48 -1
  45. package/docs/MCP.md +54 -1
  46. package/package.json +1 -1
@@ -5,3 +5,5 @@ header{height:52px;display:flex;flex:none;border-bottom:1px solid var(--frame-li
5
5
  #sections{display:flex;gap:4px;align-items:center;min-width:0;overflow-x:auto;padding:8px}#sections button{display:flex;align-items:center;gap:8px;flex:none;border-color:transparent;padding:6px 10px}#sections button[aria-current="page"]{border-color:var(--frame-line)}#sections svg{opacity:.7}.actions{display:flex;align-items:center;gap:4px;margin-inline-start:auto;padding:8px;flex:none}.actions button{width:28px;height:28px;display:grid;place-items:center}
6
6
  main{flex:1;min-height:0;display:flex}#sidebar{width:var(--pane-width);flex:none;border-right:1px solid var(--frame-line);overflow-y:auto;background:var(--frame-bg)}#entries{display:flex;flex-direction:column;gap:4px;padding:8px}#entries a{flex:none;padding:8px;border:1px solid transparent;border-radius:4px;text-decoration:none;overflow-wrap:anywhere}#entries a:hover,#entries a[aria-current="page"]{border-color:var(--frame-line)}#entries a[aria-current="page"]{font-weight:600}
7
7
  #preview{position:relative;flex:1;min-width:0;min-height:0;display:flex;flex-direction:column;overflow:hidden}#frame-slot{flex:1;min-height:0;min-width:0}iframe{display:block;width:100%;height:100%;border:0}#status{position:absolute;z-index:1;inset:12px 12px auto;margin:0;padding:12px 16px;border:1px solid var(--frame-line);border-radius:4px;background:var(--frame-bg);white-space:pre-wrap;overflow-wrap:anywhere;max-height:30vh;overflow:auto}#status[data-error="true"]{color:CanvasText;background:Canvas;border-color:GrayText}#loader{position:absolute;inset:0;z-index:2;display:grid;place-items:center;background:var(--frame-bg)}.spinner{width:24px;height:24px;border:2px solid var(--frame-line);border-top-color:var(--frame-ink);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.spinner{animation:none}}[hidden],.collapsed #sidebar{display:none!important}@media(max-width:760px){#title{display:none}}
8
+ /* The capture host keeps the same MCP client/bridge and gives the App the exact requested viewport. */
9
+ :root[data-capture="true"] #header,:root[data-capture="true"] #sidebar{display:none!important}
@@ -35,7 +35,10 @@ also list a `ds.config.json` companion: change only that owner's `inlineStyleFil
35
35
  and `boundaries.styleWrites` entries together with its source. Unused permissions
36
36
  fail the core checks; do not pre-register a boundary without its implementation.
37
37
  Use `dryRun: true` to
38
- inspect validation; applying re-runs the gates. Fix rejected patches in their
38
+ inspect validation, including affected registered behavior checks on the isolated
39
+ candidate. For a prepared draft, request its diff and dry-run the returned
40
+ `patchFile`; `ads_verify` checks current source and accepts no draft argument.
41
+ Applying re-runs the gates. Fix rejected patches in their
39
42
  owner. Use `ads_apply_transaction` for genuinely coupled owner patches;
40
43
  never bypass a rejection with direct source writes. Shared styles/helpers use `foundation`, root guides use
41
44
  `documentation`, and branding/fonts use their discovered `assets/*` unit.
@@ -47,6 +50,8 @@ stay out of model context. Binary changes require Git. When editing is disabled,
47
50
  use the client's normal source-editing tools.
48
51
  For owner drafts, token-sample reconciliation, coupled transactions and local
49
52
  constant namespaces, read [guarded-editing.md](references/guarded-editing.md).
53
+ For on-demand current/candidate screenshots and visual dry-run review, read
54
+ [captures.md](references/captures.md).
50
55
 
51
56
 
52
57
  For parent/child `covers`, conditional-control review, reference comparisons,
@@ -0,0 +1,147 @@
1
+ # On-demand visual captures
2
+
3
+ Use captures when reviewing appearance or a proposed visual change. Discover exact
4
+ scenario IDs with `ads_scenarios`; these are project-owned independently mountable
5
+ token, component, pattern and composition views. A component export alone is not
6
+ a renderable fixture. Controllers/hooks need a scenario demonstrating their visible
7
+ effects. ADS never supplies library-specific renderers, fixtures or capture targets.
8
+
9
+ ## Capture current source
10
+
11
+ ```js
12
+ ads_capture({
13
+ scenario: "components/Example",
14
+ theme: "dark",
15
+ viewport: { width: 1280, height: 800 }
16
+ })
17
+ ```
18
+
19
+ The ID above is illustrative; use one returned by discovery. The default is light
20
+ theme at 1280 × 800 CSS pixels. Capture uses the real sandboxed MCP App with the
21
+ requested viewport and excludes showcase navigation. The default image records
22
+ the complete widget viewport, including its current scroll/state; it does not
23
+ automatically stitch every scroll position into one image.
24
+
25
+ ADS starts an isolated showcase/compiler and an owned browser process, using the
26
+ connected project's installed `@playwright/test` or `playwright`. It installs no
27
+ dependency/browser. Chromium is the default; optional project configuration is:
28
+
29
+ ```json
30
+ {
31
+ "mcp": {
32
+ "capture": {
33
+ "browser": "chromium",
34
+ "channel": "chrome",
35
+ "timeoutMs": 60000
36
+ }
37
+ }
38
+ }
39
+ ```
40
+
41
+ Omit `channel` to use Playwright's installed Chromium. Firefox and WebKit are also
42
+ accepted browser choices; channel is Chromium-only. The registered browser must
43
+ be installed locally. This budget bounds the capture runner, including preview
44
+ readiness. Capture does not run the project's entire Playwright suite or require a
45
+ behavior manifest. Readiness uses the App bridge, fonts and settled image loads,
46
+ not `networkidle` (MCP maintains live connections).
47
+
48
+ ## Capture a named element
49
+
50
+ Declare selectors in the scenario's static metadata:
51
+
52
+ ```tsx
53
+ export const scenario = {
54
+ title: "Example",
55
+ covers: ["Example"],
56
+ captureTargets: {
57
+ control: '[data-testid="example-control"]'
58
+ }
59
+ };
60
+ ```
61
+
62
+ Use attributes/selectors already owned by the real UI or its public review pattern.
63
+ Scenarios keep their existing styling/control rules. A target is not permission
64
+ to add demo-only product props, custom CSS or synthetic native controls. Each
65
+ selector is scoped to the App iframe and must match exactly one visible element.
66
+ Select it with `ads_capture({scenario:"<discovered ID>",target:"control"})`.
67
+ `captureTargets` allows up to 20 named CSS selectors. Unknown, hidden, ambiguous
68
+ or unrenderable targets fail explicitly; ADS does not guess a substitute element.
69
+ Arrange a specific interaction state through the scenario's real fixture/adapter;
70
+ capture itself accepts no arbitrary browser script or click sequence.
71
+
72
+ Screenshots hide the text caret and disable animations by default. Set
73
+ `animations:"allow"` when motion must remain active, and account for nondeterminism.
74
+ The same capture settings are used for both sides of a candidate comparison.
75
+
76
+ ## Review a patch without applying it
77
+
78
+ Prepare/diff an owner draft normally, then request screenshots during its guarded
79
+ dry run:
80
+
81
+ ```js
82
+ ads_apply_patch({
83
+ unit: "components/Example",
84
+ patchFile: ".ds/patches/<returned-name>.patch",
85
+ dryRun: true,
86
+ screenshot: true,
87
+ captures: [{ scenario: "components/Example", target: "control", theme: "light" }]
88
+ })
89
+ ```
90
+
91
+ The placeholder owner/ID/path must come from discovery/preparation. Transactions
92
+ accept the same `screenshot`/`captures` fields. Select 1–8 distinct scenario/target
93
+ pairs explicitly; do not capture every affected scenario by default. Ordinary dry
94
+ runs and patches create no screenshots. `screenshot:true` requires `dryRun:true`.
95
+
96
+ Core, registered lint/project and affected behavior gates pass first. ADS then
97
+ captures current source and the isolated candidate under matching theme, viewport,
98
+ animation and browser settings. Candidate screenshots survive candidate-directory
99
+ cleanup. Final source/conflict checks still run before the pair is published. A
100
+ failed/cancelled capture does not replace existing image slots or apply source.
101
+ If a scenario/target is newly introduced, `current` is null with an explicit
102
+ `currentAbsent` explanation; only the real candidate image exists.
103
+
104
+ Review the returned images and diagnostics, fix the draft if necessary, then apply
105
+ with `dryRun:false`. Apply reruns all gates. An image is promoted only when the
106
+ accepted source revision matches the captured candidate; other proposals remain
107
+ stale. Images do not approve a patch or certify interaction/accessibility/parity.
108
+
109
+ ## Two slots, no automatic history
110
+
111
+ The stable capture key identifies the scenario and optional named target. Theme,
112
+ viewport and revision are metadata, not additional resource identities.
113
+
114
+ | Successful operation | Result for that target |
115
+ | --- | --- |
116
+ | Standalone `ads_capture` | Fresh current; any earlier candidate removed |
117
+ | Screenshot-enabled dry run | Fresh current + candidate; replaces the old pair |
118
+ | Apply matching proposed source | Candidate becomes current; candidate slot cleared |
119
+ | `ads_capture_clear({scenario,target?,candidateOnly:true})` | Discard candidate, retain current when present |
120
+ | `ads_capture_clear({scenario,target?})` | Remove both slots |
121
+ | Remove scenario/target | Orphan images collected on resource refresh/discovery |
122
+
123
+ No previous slot, revision history or TTL is kept. Later source changes mark
124
+ images stale rather than silently recapturing them. New proposals replace the
125
+ candidate. Three limits keep storage bounded: at most 4 MiB per PNG, 8 megapixels
126
+ per image and 256 MiB total capture storage. Clear unneeded targets if the store
127
+ reaches its limit. Viewport dimensions range from 100 to 4096 pixels per axis,
128
+ subject to the pixel limit.
129
+
130
+ ## Read resources and freshness
131
+
132
+ `ads://project/captures` lists existing slots and metadata. Each returned image URI
133
+ uses the template `ads://captures/{key}/{slot}`, with `slot` current or candidate.
134
+ Read the exact returned URI through MCP `resources/read` (or the host's resource
135
+ tool). The result is `image/png` with binary `blob`, not a filesystem or browser
136
+ URL. Use the client's image/resource viewer; do not paste base64 into model text.
137
+
138
+ Capture metadata records source/build revision, fixture owner file, settings,
139
+ capture time, PNG dimensions/digest and candidate proposal identity. Current is
140
+ stale when its source revision differs; a candidate becomes stale when its base
141
+ source changes. Static freshness does not prove external/live fixture data stayed
142
+ identical. Subscribe to the index and image URIs for `resources/updated`, including
143
+ candidate removal; explicitly reread when the host does not deliver notifications.
144
+ Absent/cleared slots return a resource-not-found diagnostic, not an old image.
145
+ The resource catalogue exposes one index and an image URI template, avoiding a
146
+ resource-list entry for every new revision. Images stay as generated development
147
+ data under `.ds/captures/`; none belongs in product bundles or source patches.
@@ -6,6 +6,64 @@ tools, request `action:"diff"`, then apply the returned patchFile. Discard the
6
6
  draft afterward. Remote clients can keep sending inline Git diffs. Neither draft
7
7
  preparation nor diff generation validates or changes live source.
8
8
 
9
+ To verify a draft before applying it, diff it and dry-run the returned patch:
10
+
11
+ ```js
12
+ ads_prepare_patch({"unit":"components/Example","action":"diff","draft":"<returned draft ID>"})
13
+ ads_apply_patch({"unit":"components/Example","patchFile":"<returned patchFile>","dryRun":true})
14
+ ```
15
+
16
+ The dry run validates the full candidate, including affected registered behavior
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.
20
+ Keep behavior.json/specs with their owner and use registered checks; a scratchpad
21
+ run against live source cannot verify unapplied changes. After validation, apply
22
+ with `dryRun:false`; ADS revalidates before writing. Dry-run evidence is not saved
23
+ as passing evidence for the live project.
24
+
25
+ `ads_verify({unit,checks?})` selects checks for current project source; it has no
26
+ draft argument. There is no dedicated selected-check runner for drafts yet.
27
+ Use a dry-run transaction for genuinely coupled draft patches.
28
+
29
+ Behavior-run performance belongs to both orchestration and project checks. ADS
30
+ selects affected owners, filters registered Playwright case tags and shares one
31
+ candidate host per run. The project owns assertions, browser setup, worker count,
32
+ per-test timeouts and its profile's whole-run `timeoutMs`. The latter covers the
33
+ entire runner process, not each test. Do not rewrite APIs, drop guarantees or
34
+ disable checks to fit the budget. Inspect measured durations before tuning it.
35
+
36
+ During verification, inspect `ads_status.behaviors.progress`; during candidate
37
+ validation, inspect `ads_status.mutations.progress`. These show completed/active/
38
+ pending checks and timings. MCP clients requesting progress also receive updates.
39
+ A timeout retains reported case outcomes and identifies active/unstarted checks;
40
+ it still blocks the mutation. `reportedStatus:"passed"` within an incomplete run
41
+ is diagnostic information, not a passing guarantee. Reports/artifacts under the
42
+ live project's `.ds/verification/` survive candidate cleanup. Uninstrumented custom
43
+ runners may report an unknown execution state instead of claiming a case started.
44
+
45
+ For independent browser specs, consider a small parallel worker count and measure
46
+ on the target machine. Use separate contexts/pages, independent state and the
47
+ candidate URLs provided by `openBehaviorScenario`; avoid fixed live-server URLs,
48
+ shared mutable fixtures and test-order assumptions. Files can run in parallel;
49
+ tests in one file remain sequential by default. Prefer readiness assertions over
50
+ blanket sleeps. Fake clocks suit timer-only guarantees; keep real time for actual
51
+ gesture/motion/layout guarantees. Project settings belong in guarded configuration
52
+ changes; ADS does not silently raise budgets or override the worker count.
53
+
54
+ Named barrel imports can narrow affected checks when the project accurately
55
+ declares package `sideEffects`; retain CSS/global effects in that policy. Missing
56
+ or unsupported policies and dynamic/wildcard dependencies stay conservative.
57
+ Do not change the policy merely to suppress verification of real side effects.
58
+
59
+ For registered measured-geometry style writes, use direct `element.style.transform =
60
+ value` or `element.style.setProperty("--ds-measured-value", value)` with the exact
61
+ file/property boundary. The current guard rejects a style-object alias such as
62
+ `const style = element.style` as an escape; it does not trace the alias to later
63
+ writes. Keep writes explicit when adapting local controller code. This syntax
64
+ constraint is separate from deciding which measured properties belong to the
65
+ controller; it does not authorize unregistered visual writes.
66
+
9
67
  Shared provenance uses the existing `documentation` unit. Read its `filePolicy`
10
68
  from `ads_units`: one patch can create `CREDITS.md`, shared `licenses/*.txt` and
11
69
  README links together. Conventional root credits/license/notice names support
@@ -49,6 +107,17 @@ Keep contextual public parts public when they are intended consumer API. Demonst
49
107
  them through their actual public composition/customization path and list their real
50
108
  render references in covers. Do not count internal production renders as public API
51
109
  coverage. Behavioral APIs use exercises and registered guarantees instead.
52
- Scenario controls still use public UI; native link/button markup belongs in an
53
- owned public primitive/pattern or an owner-local test, including polymorphic/slot
54
- adaptation. Do not add demo-only component props or global styles for review glue.
110
+ Scenario controls still use public UI. A native link/button can supply semantics
111
+ as the single direct child of a public runtime slot when source tracing proves
112
+ that owner clones the child with its props. An `asChild` flag alone is insufficient;
113
+ standalone native controls and custom scenario styling remain rejected. See
114
+ [scenarios.md](scenarios.md) for evidence and limits. Preserve the intended
115
+ composition instead of substituting an unrelated styled control or adding a
116
+ demo-only component prop. Review framing belongs in public layout patterns.
117
+
118
+ A complete runner report containing an ordinary failed assertion preserves each
119
+ passing check as `passed`; the failed check remains `failed`, and overall
120
+ verification still rejects the mutation. Fix the actual failing checks instead of
121
+ assuming every affected owner failed. Suite errors, unexplained runner exits,
122
+ missing results and interrupted runs cannot certify passing guarantees. Use the
123
+ case's `reportedStatus`, message and report to distinguish these conditions.
@@ -1,6 +1,6 @@
1
1
  # Scenario rendering, token previews and public class helpers
2
2
 
3
- Use this reference for `scenario-render`, `scenario-coverage` or `showcase-class`
3
+ Use this reference for `scenario-render`, `scenario-coverage`, `showcase-control` or `showcase-class`
4
4
  diagnostics. MCP serves the same guide as `ads://guides/scenarios`.
5
5
 
6
6
  All preview kinds open at `?scenario=<discovered-id>`. Use the returned showcase
@@ -18,6 +18,11 @@ recovers missed atomic replacements. Do not touch source to force freshness;
18
18
  inspect build diagnostics. Upgrade the project's compiler dependency together
19
19
  with MCP when an older reporter lacks source evidence.
20
20
 
21
+ Optional static `captureTargets` names CSS selectors within the scenario's App for
22
+ on-demand element screenshots. It grants no render/behavior coverage and no extra
23
+ styling permission. Use [captures.md](captures.md) for current/candidate review,
24
+ target selection, resource reads and storage lifecycle.
25
+
21
26
  ## Owner-local token previews load their own definitions
22
27
 
23
28
  Keep the registered token sheet imported by its runtime owner for product use.
@@ -85,6 +90,40 @@ do not export the helper solely for a test. Public/private ownership is a produc
85
90
  decision, not a coverage workaround. Browser checks verify transitions but do not
86
91
  replace the current static `covers` requirement; `exercises` does not waive it.
87
92
 
93
+ ## Native semantics through a public runtime slot
94
+
95
+ A native element can be the direct child of a public runtime component that
96
+ actually clones that child with its own props:
97
+
98
+ ```tsx
99
+ <Action asChild>
100
+ <a href={fixture.destination}>Documentation</a>
101
+ </Action>
102
+ ```
103
+
104
+ `Action` and the fixture are illustrative; use the project's intended public API.
105
+ The runtime owner supplies presentation and interaction; the anchor supplies link
106
+ semantics. Keep this composition when it is the product contract. Do not replace
107
+ the anchor with an unrelated styled link, add a demo-only prop, disable the guard
108
+ or write outside MCP merely to silence a diagnostic.
109
+
110
+ ADS follows the selected runtime render path, the exact child and real imported
111
+ React/Preact `cloneElement` operations, including local slot/prop-merging helpers.
112
+ The prop need not be named `asChild`; the spelling alone grants no permission.
113
+ JSX and recognized framework factories follow the same rule. The native element
114
+ must be the single direct child: another native control nested inside it remains
115
+ subject to the ordinary ownership rule. Passing children through a container,
116
+ a scenario-local wrapper or an opaque component does not establish a slot.
117
+
118
+ This grants no scenario styling permission. Inline styles and arbitrary class
119
+ names remain rejected, including on the slotted child. Public style helpers retain
120
+ their own provenance checks. Unknown branches that could render the native child
121
+ directly, multiple children and untraceable replacement props remain blocked.
122
+ If a valid slot contract cannot be traced, keep the intended API and report a
123
+ small reproduction rather than inventing a product workaround. Static proof
124
+ establishes ownership, not correct prop merging, focus, keyboard or navigation
125
+ behavior; verify those in the built App and registered checks.
126
+
88
127
  ## Scenarios may consume verified public style helpers
89
128
 
90
129
  A deliberately public helper can expose its owner's Linaria treatment:
@@ -67,7 +67,10 @@ also list a `ds.config.json` companion: change only that owner's `inlineStyleFil
67
67
  and `boundaries.styleWrites` entries together with its source. Unused permissions
68
68
  fail the core checks; do not pre-register a boundary without its implementation.
69
69
  Use `dryRun: true` to
70
- inspect validation; applying re-runs the gates. Fix rejected patches in their
70
+ inspect validation, including affected registered behavior checks on the isolated
71
+ candidate. For a prepared draft, request its diff and dry-run the returned
72
+ `patchFile`; `ads_verify` checks current source and accepts no draft argument.
73
+ Applying re-runs the gates. Fix rejected patches in their
71
74
  owner. Use `ads_apply_transaction` for genuinely coupled owner patches;
72
75
  never bypass a rejection with direct source writes. Shared styles/helpers use `foundation`, root guides use
73
76
  `documentation`, and branding/fonts use their discovered `assets/*` unit.
@@ -79,6 +82,8 @@ stay out of model context. Binary changes require Git. When editing is disabled,
79
82
  use the client's normal source-editing tools.
80
83
  For owner drafts, token-sample reconciliation, coupled transactions and local
81
84
  constant namespaces, read [guarded-editing.md](references/guarded-editing.md).
85
+ For on-demand current/candidate screenshots and visual dry-run review, read
86
+ [captures.md](references/captures.md).
82
87
 
83
88
  For parent/child `covers`, conditional-control review, reference comparisons,
84
89
  public style-helper use in scenarios, or `scenario-render` / `showcase-class`
@@ -197,7 +197,10 @@ or applies the right CSS property. Browser review still verifies the samples.
197
197
 
198
198
  Scenarios and fixture helpers are development-only wherever colocated. Runtime
199
199
  source and the public barrel cannot import or re-export them. Showcase/scenario
200
- code defines no CSS, custom classes or native controls; use public ADS APIs.
200
+ code defines no CSS, custom classes or standalone native controls; use public ADS
201
+ APIs. A single direct native child can supply semantics to a public runtime slot
202
+ when source tracing proves that owner clones the exact child with its own props.
203
+ An `asChild` flag alone is not evidence; scenario styling remains forbidden.
201
204
  Neither ADS implementations nor scenarios import application code. Live business
202
205
  data belongs to application adapters; ADS receives inputs or injected callbacks.
203
206
  Direct global `fetch`, XHR, WebSocket and EventSource use is rejected. Renderer
@@ -0,0 +1,147 @@
1
+ # On-demand visual captures
2
+
3
+ Use captures when reviewing appearance or a proposed visual change. Discover exact
4
+ scenario IDs with `ads_scenarios`; these are project-owned independently mountable
5
+ token, component, pattern and composition views. A component export alone is not
6
+ a renderable fixture. Controllers/hooks need a scenario demonstrating their visible
7
+ effects. ADS never supplies library-specific renderers, fixtures or capture targets.
8
+
9
+ ## Capture current source
10
+
11
+ ```js
12
+ ads_capture({
13
+ scenario: "components/Example",
14
+ theme: "dark",
15
+ viewport: { width: 1280, height: 800 }
16
+ })
17
+ ```
18
+
19
+ The ID above is illustrative; use one returned by discovery. The default is light
20
+ theme at 1280 × 800 CSS pixels. Capture uses the real sandboxed MCP App with the
21
+ requested viewport and excludes showcase navigation. The default image records
22
+ the complete widget viewport, including its current scroll/state; it does not
23
+ automatically stitch every scroll position into one image.
24
+
25
+ ADS starts an isolated showcase/compiler and an owned browser process, using the
26
+ connected project's installed `@playwright/test` or `playwright`. It installs no
27
+ dependency/browser. Chromium is the default; optional project configuration is:
28
+
29
+ ```json
30
+ {
31
+ "mcp": {
32
+ "capture": {
33
+ "browser": "chromium",
34
+ "channel": "chrome",
35
+ "timeoutMs": 60000
36
+ }
37
+ }
38
+ }
39
+ ```
40
+
41
+ Omit `channel` to use Playwright's installed Chromium. Firefox and WebKit are also
42
+ accepted browser choices; channel is Chromium-only. The registered browser must
43
+ be installed locally. This budget bounds the capture runner, including preview
44
+ readiness. Capture does not run the project's entire Playwright suite or require a
45
+ behavior manifest. Readiness uses the App bridge, fonts and settled image loads,
46
+ not `networkidle` (MCP maintains live connections).
47
+
48
+ ## Capture a named element
49
+
50
+ Declare selectors in the scenario's static metadata:
51
+
52
+ ```tsx
53
+ export const scenario = {
54
+ title: "Example",
55
+ covers: ["Example"],
56
+ captureTargets: {
57
+ control: '[data-testid="example-control"]'
58
+ }
59
+ };
60
+ ```
61
+
62
+ Use attributes/selectors already owned by the real UI or its public review pattern.
63
+ Scenarios keep their existing styling/control rules. A target is not permission
64
+ to add demo-only product props, custom CSS or synthetic native controls. Each
65
+ selector is scoped to the App iframe and must match exactly one visible element.
66
+ Select it with `ads_capture({scenario:"<discovered ID>",target:"control"})`.
67
+ `captureTargets` allows up to 20 named CSS selectors. Unknown, hidden, ambiguous
68
+ or unrenderable targets fail explicitly; ADS does not guess a substitute element.
69
+ Arrange a specific interaction state through the scenario's real fixture/adapter;
70
+ capture itself accepts no arbitrary browser script or click sequence.
71
+
72
+ Screenshots hide the text caret and disable animations by default. Set
73
+ `animations:"allow"` when motion must remain active, and account for nondeterminism.
74
+ The same capture settings are used for both sides of a candidate comparison.
75
+
76
+ ## Review a patch without applying it
77
+
78
+ Prepare/diff an owner draft normally, then request screenshots during its guarded
79
+ dry run:
80
+
81
+ ```js
82
+ ads_apply_patch({
83
+ unit: "components/Example",
84
+ patchFile: ".ds/patches/<returned-name>.patch",
85
+ dryRun: true,
86
+ screenshot: true,
87
+ captures: [{ scenario: "components/Example", target: "control", theme: "light" }]
88
+ })
89
+ ```
90
+
91
+ The placeholder owner/ID/path must come from discovery/preparation. Transactions
92
+ accept the same `screenshot`/`captures` fields. Select 1–8 distinct scenario/target
93
+ pairs explicitly; do not capture every affected scenario by default. Ordinary dry
94
+ runs and patches create no screenshots. `screenshot:true` requires `dryRun:true`.
95
+
96
+ Core, registered lint/project and affected behavior gates pass first. ADS then
97
+ captures current source and the isolated candidate under matching theme, viewport,
98
+ animation and browser settings. Candidate screenshots survive candidate-directory
99
+ cleanup. Final source/conflict checks still run before the pair is published. A
100
+ failed/cancelled capture does not replace existing image slots or apply source.
101
+ If a scenario/target is newly introduced, `current` is null with an explicit
102
+ `currentAbsent` explanation; only the real candidate image exists.
103
+
104
+ Review the returned images and diagnostics, fix the draft if necessary, then apply
105
+ with `dryRun:false`. Apply reruns all gates. An image is promoted only when the
106
+ accepted source revision matches the captured candidate; other proposals remain
107
+ stale. Images do not approve a patch or certify interaction/accessibility/parity.
108
+
109
+ ## Two slots, no automatic history
110
+
111
+ The stable capture key identifies the scenario and optional named target. Theme,
112
+ viewport and revision are metadata, not additional resource identities.
113
+
114
+ | Successful operation | Result for that target |
115
+ | --- | --- |
116
+ | Standalone `ads_capture` | Fresh current; any earlier candidate removed |
117
+ | Screenshot-enabled dry run | Fresh current + candidate; replaces the old pair |
118
+ | Apply matching proposed source | Candidate becomes current; candidate slot cleared |
119
+ | `ads_capture_clear({scenario,target?,candidateOnly:true})` | Discard candidate, retain current when present |
120
+ | `ads_capture_clear({scenario,target?})` | Remove both slots |
121
+ | Remove scenario/target | Orphan images collected on resource refresh/discovery |
122
+
123
+ No previous slot, revision history or TTL is kept. Later source changes mark
124
+ images stale rather than silently recapturing them. New proposals replace the
125
+ candidate. Three limits keep storage bounded: at most 4 MiB per PNG, 8 megapixels
126
+ per image and 256 MiB total capture storage. Clear unneeded targets if the store
127
+ reaches its limit. Viewport dimensions range from 100 to 4096 pixels per axis,
128
+ subject to the pixel limit.
129
+
130
+ ## Read resources and freshness
131
+
132
+ `ads://project/captures` lists existing slots and metadata. Each returned image URI
133
+ uses the template `ads://captures/{key}/{slot}`, with `slot` current or candidate.
134
+ Read the exact returned URI through MCP `resources/read` (or the host's resource
135
+ tool). The result is `image/png` with binary `blob`, not a filesystem or browser
136
+ URL. Use the client's image/resource viewer; do not paste base64 into model text.
137
+
138
+ Capture metadata records source/build revision, fixture owner file, settings,
139
+ capture time, PNG dimensions/digest and candidate proposal identity. Current is
140
+ stale when its source revision differs; a candidate becomes stale when its base
141
+ source changes. Static freshness does not prove external/live fixture data stayed
142
+ identical. Subscribe to the index and image URIs for `resources/updated`, including
143
+ candidate removal; explicitly reread when the host does not deliver notifications.
144
+ Absent/cleared slots return a resource-not-found diagnostic, not an old image.
145
+ The resource catalogue exposes one index and an image URI template, avoiding a
146
+ resource-list entry for every new revision. Images stay as generated development
147
+ data under `.ds/captures/`; none belongs in product bundles or source patches.
@@ -6,6 +6,64 @@ tools, request `action:"diff"`, then apply the returned patchFile. Discard the
6
6
  draft afterward. Remote clients can keep sending inline Git diffs. Neither draft
7
7
  preparation nor diff generation validates or changes live source.
8
8
 
9
+ To verify a draft before applying it, diff it and dry-run the returned patch:
10
+
11
+ ```js
12
+ ads_prepare_patch({"unit":"components/Example","action":"diff","draft":"<returned draft ID>"})
13
+ ads_apply_patch({"unit":"components/Example","patchFile":"<returned patchFile>","dryRun":true})
14
+ ```
15
+
16
+ The dry run validates the full candidate, including affected registered behavior
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.
20
+ Keep behavior.json/specs with their owner and use registered checks; a scratchpad
21
+ run against live source cannot verify unapplied changes. After validation, apply
22
+ with `dryRun:false`; ADS revalidates before writing. Dry-run evidence is not saved
23
+ as passing evidence for the live project.
24
+
25
+ `ads_verify({unit,checks?})` selects checks for current project source; it has no
26
+ draft argument. There is no dedicated selected-check runner for drafts yet.
27
+ Use a dry-run transaction for genuinely coupled draft patches.
28
+
29
+ Behavior-run performance belongs to both orchestration and project checks. ADS
30
+ selects affected owners, filters registered Playwright case tags and shares one
31
+ candidate host per run. The project owns assertions, browser setup, worker count,
32
+ per-test timeouts and its profile's whole-run `timeoutMs`. The latter covers the
33
+ entire runner process, not each test. Do not rewrite APIs, drop guarantees or
34
+ disable checks to fit the budget. Inspect measured durations before tuning it.
35
+
36
+ During verification, inspect `ads_status.behaviors.progress`; during candidate
37
+ validation, inspect `ads_status.mutations.progress`. These show completed/active/
38
+ pending checks and timings. MCP clients requesting progress also receive updates.
39
+ A timeout retains reported case outcomes and identifies active/unstarted checks;
40
+ it still blocks the mutation. `reportedStatus:"passed"` within an incomplete run
41
+ is diagnostic information, not a passing guarantee. Reports/artifacts under the
42
+ live project's `.ds/verification/` survive candidate cleanup. Uninstrumented custom
43
+ runners may report an unknown execution state instead of claiming a case started.
44
+
45
+ For independent browser specs, consider a small parallel worker count and measure
46
+ on the target machine. Use separate contexts/pages, independent state and the
47
+ candidate URLs provided by `openBehaviorScenario`; avoid fixed live-server URLs,
48
+ shared mutable fixtures and test-order assumptions. Files can run in parallel;
49
+ tests in one file remain sequential by default. Prefer readiness assertions over
50
+ blanket sleeps. Fake clocks suit timer-only guarantees; keep real time for actual
51
+ gesture/motion/layout guarantees. Project settings belong in guarded configuration
52
+ changes; ADS does not silently raise budgets or override the worker count.
53
+
54
+ Named barrel imports can narrow affected checks when the project accurately
55
+ declares package `sideEffects`; retain CSS/global effects in that policy. Missing
56
+ or unsupported policies and dynamic/wildcard dependencies stay conservative.
57
+ Do not change the policy merely to suppress verification of real side effects.
58
+
59
+ For registered measured-geometry style writes, use direct `element.style.transform =
60
+ value` or `element.style.setProperty("--ds-measured-value", value)` with the exact
61
+ file/property boundary. The current guard rejects a style-object alias such as
62
+ `const style = element.style` as an escape; it does not trace the alias to later
63
+ writes. Keep writes explicit when adapting local controller code. This syntax
64
+ constraint is separate from deciding which measured properties belong to the
65
+ controller; it does not authorize unregistered visual writes.
66
+
9
67
  Shared provenance uses the existing `documentation` unit. Read its `filePolicy`
10
68
  from `ads_units`: one patch can create `CREDITS.md`, shared `licenses/*.txt` and
11
69
  README links together. Conventional root credits/license/notice names support
@@ -49,6 +107,17 @@ Keep contextual public parts public when they are intended consumer API. Demonst
49
107
  them through their actual public composition/customization path and list their real
50
108
  render references in covers. Do not count internal production renders as public API
51
109
  coverage. Behavioral APIs use exercises and registered guarantees instead.
52
- Scenario controls still use public UI; native link/button markup belongs in an
53
- owned public primitive/pattern or an owner-local test, including polymorphic/slot
54
- adaptation. Do not add demo-only component props or global styles for review glue.
110
+ Scenario controls still use public UI. A native link/button can supply semantics
111
+ as the single direct child of a public runtime slot when source tracing proves
112
+ that owner clones the child with its props. An `asChild` flag alone is insufficient;
113
+ standalone native controls and custom scenario styling remain rejected. See
114
+ [scenarios.md](scenarios.md) for evidence and limits. Preserve the intended
115
+ composition instead of substituting an unrelated styled control or adding a
116
+ demo-only component prop. Review framing belongs in public layout patterns.
117
+
118
+ A complete runner report containing an ordinary failed assertion preserves each
119
+ passing check as `passed`; the failed check remains `failed`, and overall
120
+ verification still rejects the mutation. Fix the actual failing checks instead of
121
+ assuming every affected owner failed. Suite errors, unexplained runner exits,
122
+ missing results and interrupted runs cannot certify passing guarantees. Use the
123
+ case's `reportedStatus`, message and report to distinguish these conditions.