@reqvire-org/ads 0.3.12 → 0.3.13

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 (41) hide show
  1. package/README.md +20 -1
  2. package/dist/capture/worker.mjs +38 -0
  3. package/dist/capture-store.mjs +123 -0
  4. package/dist/capture.mjs +117 -0
  5. package/dist/config.mjs +6 -1
  6. package/dist/mcp/context.mjs +1 -1
  7. package/dist/mcp/mutations.mjs +25 -5
  8. package/dist/mcp/server.mjs +62 -15
  9. package/dist/scenarios.mjs +9 -3
  10. package/dist/showcase/client.bundle.js +18 -18
  11. package/dist/showcase/style.css +2 -0
  12. package/dist/skills/ads-break-ui/SKILL.md +6 -1
  13. package/dist/skills/ads-break-ui/references/captures.md +147 -0
  14. package/dist/skills/ads-break-ui/references/guarded-editing.md +58 -0
  15. package/dist/skills/ads-break-ui/references/scenarios.md +5 -0
  16. package/dist/skills/ads-designer/SKILL.md +6 -1
  17. package/dist/skills/ads-designer/references/captures.md +147 -0
  18. package/dist/skills/ads-designer/references/guarded-editing.md +58 -0
  19. package/dist/skills/ads-designer/references/scenarios.md +5 -0
  20. package/dist/skills/ads-import/SKILL.md +6 -1
  21. package/dist/skills/ads-import/references/captures.md +147 -0
  22. package/dist/skills/ads-import/references/guarded-editing.md +58 -0
  23. package/dist/skills/ads-import/references/scenarios.md +5 -0
  24. package/dist/skills/ads-migrate/SKILL.md +6 -1
  25. package/dist/skills/ads-migrate/references/captures.md +147 -0
  26. package/dist/skills/ads-migrate/references/guarded-editing.md +58 -0
  27. package/dist/skills/ads-migrate/references/scenarios.md +5 -0
  28. package/dist/skills/ads-motion/SKILL.md +6 -1
  29. package/dist/skills/ads-motion/references/captures.md +147 -0
  30. package/dist/skills/ads-motion/references/guarded-editing.md +58 -0
  31. package/dist/skills/ads-motion/references/scenarios.md +5 -0
  32. package/dist/verification/browser.mjs +8 -3
  33. package/dist/verification/playwright-reporter.cjs +23 -8
  34. package/dist/verification.mjs +137 -33
  35. package/docs/ADS.md +5 -0
  36. package/docs/ARCHITECTURE.md +39 -2
  37. package/docs/BEHAVIORS.md +57 -3
  38. package/docs/CHANGELOG.md +17 -0
  39. package/docs/GUIDE.md +48 -1
  40. package/docs/MCP.md +49 -1
  41. package/package.json +1 -1
@@ -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
@@ -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.
@@ -32,7 +32,10 @@ also list a `ds.config.json` companion: change only that owner's `inlineStyleFil
32
32
  and `boundaries.styleWrites` entries together with its source. Unused permissions
33
33
  fail the core checks; do not pre-register a boundary without its implementation.
34
34
  Use `dryRun: true` to
35
- inspect validation; applying re-runs the gates. Fix rejected patches in their
35
+ inspect validation, including affected registered behavior checks on the isolated
36
+ candidate. For a prepared draft, request its diff and dry-run the returned
37
+ `patchFile`; `ads_verify` checks current source and accepts no draft argument.
38
+ Applying re-runs the gates. Fix rejected patches in their
36
39
  owner. Use `ads_apply_transaction` for genuinely coupled owner patches;
37
40
  never bypass a rejection with direct source writes. Shared styles/helpers use `foundation`, root guides use
38
41
  `documentation`, and branding/fonts use their discovered `assets/*` unit.
@@ -44,6 +47,8 @@ stay out of model context. Binary changes require Git. When editing is disabled,
44
47
  use the client's normal source-editing tools.
45
48
  For owner drafts, token-sample reconciliation, coupled transactions and local
46
49
  constant namespaces, read [guarded-editing.md](references/guarded-editing.md).
50
+ For on-demand current/candidate screenshots and visual dry-run review, read
51
+ [captures.md](references/captures.md).
47
52
 
48
53
 
49
54
  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
@@ -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.
@@ -52,7 +52,10 @@ also list a `ds.config.json` companion: change only that owner's `inlineStyleFil
52
52
  and `boundaries.styleWrites` entries together with its source. Unused permissions
53
53
  fail the core checks; do not pre-register a boundary without its implementation.
54
54
  Use `dryRun: true` to
55
- inspect validation; applying re-runs the gates. Fix rejected patches in their
55
+ inspect validation, including affected registered behavior checks on the isolated
56
+ candidate. For a prepared draft, request its diff and dry-run the returned
57
+ `patchFile`; `ads_verify` checks current source and accepts no draft argument.
58
+ Applying re-runs the gates. Fix rejected patches in their
56
59
  owner. Use `ads_apply_transaction` for genuinely coupled owner patches;
57
60
  never bypass a rejection with direct source writes. Shared styles/helpers use `foundation`, root guides use
58
61
  `documentation`, and branding/fonts use their discovered `assets/*` unit.
@@ -64,6 +67,8 @@ stay out of model context. Binary changes require Git. When editing is disabled,
64
67
  use the client's normal source-editing tools.
65
68
  For owner drafts, token-sample reconciliation, coupled transactions and local
66
69
  constant namespaces, read [guarded-editing.md](references/guarded-editing.md).
70
+ For on-demand current/candidate screenshots and visual dry-run review, read
71
+ [captures.md](references/captures.md).
67
72
 
68
73
 
69
74
  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.