reflection-check 0.0.1

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 (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +55 -0
  3. package/dist/adapters/route-manifest.d.ts +3 -0
  4. package/dist/adapters/route-manifest.js +98 -0
  5. package/dist/adapters/route-manifest.js.map +1 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.js +93 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/commands/doctor.d.ts +4 -0
  10. package/dist/commands/doctor.js +5 -0
  11. package/dist/commands/doctor.js.map +1 -0
  12. package/dist/commands/gc.d.ts +8 -0
  13. package/dist/commands/gc.js +45 -0
  14. package/dist/commands/gc.js.map +1 -0
  15. package/dist/commands/review.d.ts +7 -0
  16. package/dist/commands/review.js +149 -0
  17. package/dist/commands/review.js.map +1 -0
  18. package/dist/commands/run.d.ts +10 -0
  19. package/dist/commands/run.js +168 -0
  20. package/dist/commands/run.js.map +1 -0
  21. package/dist/commands/update.d.ts +11 -0
  22. package/dist/commands/update.js +183 -0
  23. package/dist/commands/update.js.map +1 -0
  24. package/dist/contracts/browser/assertions.d.ts +34 -0
  25. package/dist/contracts/browser/assertions.js +87 -0
  26. package/dist/contracts/browser/assertions.js.map +1 -0
  27. package/dist/contracts/browser/browser-contract.d.ts +13 -0
  28. package/dist/contracts/browser/browser-contract.js +35 -0
  29. package/dist/contracts/browser/browser-contract.js.map +1 -0
  30. package/dist/contracts/browser/console-observer.d.ts +6 -0
  31. package/dist/contracts/browser/console-observer.js +14 -0
  32. package/dist/contracts/browser/console-observer.js.map +1 -0
  33. package/dist/contracts/browser/overflow-check.d.ts +6 -0
  34. package/dist/contracts/browser/overflow-check.js +15 -0
  35. package/dist/contracts/browser/overflow-check.js.map +1 -0
  36. package/dist/contracts/browser/route-runner.d.ts +21 -0
  37. package/dist/contracts/browser/route-runner.js +98 -0
  38. package/dist/contracts/browser/route-runner.js.map +1 -0
  39. package/dist/contracts/component/component-visual-contract.d.ts +30 -0
  40. package/dist/contracts/component/component-visual-contract.js +147 -0
  41. package/dist/contracts/component/component-visual-contract.js.map +1 -0
  42. package/dist/contracts/design/command-adapter.d.ts +17 -0
  43. package/dist/contracts/design/command-adapter.js +60 -0
  44. package/dist/contracts/design/command-adapter.js.map +1 -0
  45. package/dist/contracts/design/design-contract.d.ts +8 -0
  46. package/dist/contracts/design/design-contract.js +149 -0
  47. package/dist/contracts/design/design-contract.js.map +1 -0
  48. package/dist/contracts/visual/baseline-compare.d.ts +19 -0
  49. package/dist/contracts/visual/baseline-compare.js +94 -0
  50. package/dist/contracts/visual/baseline-compare.js.map +1 -0
  51. package/dist/contracts/visual/image-diff.d.ts +27 -0
  52. package/dist/contracts/visual/image-diff.js +58 -0
  53. package/dist/contracts/visual/image-diff.js.map +1 -0
  54. package/dist/contracts/visual/thresholds.d.ts +15 -0
  55. package/dist/contracts/visual/thresholds.js +11 -0
  56. package/dist/contracts/visual/thresholds.js.map +1 -0
  57. package/dist/contracts/visual/visual-contract.d.ts +11 -0
  58. package/dist/contracts/visual/visual-contract.js +32 -0
  59. package/dist/contracts/visual/visual-contract.js.map +1 -0
  60. package/dist/core/artifact-store.d.ts +18 -0
  61. package/dist/core/artifact-store.js +105 -0
  62. package/dist/core/artifact-store.js.map +1 -0
  63. package/dist/core/baseline-store.d.ts +18 -0
  64. package/dist/core/baseline-store.js +56 -0
  65. package/dist/core/baseline-store.js.map +1 -0
  66. package/dist/core/config.d.ts +129 -0
  67. package/dist/core/config.js +159 -0
  68. package/dist/core/config.js.map +1 -0
  69. package/dist/core/define-reflection.d.ts +2 -0
  70. package/dist/core/define-reflection.js +4 -0
  71. package/dist/core/define-reflection.js.map +1 -0
  72. package/dist/core/exit-codes.d.ts +7 -0
  73. package/dist/core/exit-codes.js +9 -0
  74. package/dist/core/exit-codes.js.map +1 -0
  75. package/dist/core/failure-classifier.d.ts +3 -0
  76. package/dist/core/failure-classifier.js +19 -0
  77. package/dist/core/failure-classifier.js.map +1 -0
  78. package/dist/core/gc.d.ts +19 -0
  79. package/dist/core/gc.js +161 -0
  80. package/dist/core/gc.js.map +1 -0
  81. package/dist/core/manifest.d.ts +23 -0
  82. package/dist/core/manifest.js +21 -0
  83. package/dist/core/manifest.js.map +1 -0
  84. package/dist/core/redaction.d.ts +3 -0
  85. package/dist/core/redaction.js +63 -0
  86. package/dist/core/redaction.js.map +1 -0
  87. package/dist/core/report-schema.d.ts +262 -0
  88. package/dist/core/report-schema.js +112 -0
  89. package/dist/core/report-schema.js.map +1 -0
  90. package/dist/core/report-writer.d.ts +4 -0
  91. package/dist/core/report-writer.js +77 -0
  92. package/dist/core/report-writer.js.map +1 -0
  93. package/dist/core/server-manager.d.ts +23 -0
  94. package/dist/core/server-manager.js +64 -0
  95. package/dist/core/server-manager.js.map +1 -0
  96. package/dist/core/target-ir.d.ts +64 -0
  97. package/dist/core/target-ir.js +85 -0
  98. package/dist/core/target-ir.js.map +1 -0
  99. package/dist/index.d.ts +2 -0
  100. package/dist/index.js +2 -0
  101. package/dist/index.js.map +1 -0
  102. package/dist/integrations/playwright/browser-manager.d.ts +2 -0
  103. package/dist/integrations/playwright/browser-manager.js +5 -0
  104. package/dist/integrations/playwright/browser-manager.js.map +1 -0
  105. package/dist/integrations/playwright/context-factory.d.ts +7 -0
  106. package/dist/integrations/playwright/context-factory.js +19 -0
  107. package/dist/integrations/playwright/context-factory.js.map +1 -0
  108. package/dist/integrations/playwright/trace-policy.d.ts +5 -0
  109. package/dist/integrations/playwright/trace-policy.js +7 -0
  110. package/dist/integrations/playwright/trace-policy.js.map +1 -0
  111. package/dist/integrations/storybook/index-json.d.ts +21 -0
  112. package/dist/integrations/storybook/index-json.js +44 -0
  113. package/dist/integrations/storybook/index-json.js.map +1 -0
  114. package/dist/integrations/storybook/server.d.ts +8 -0
  115. package/dist/integrations/storybook/server.js +23 -0
  116. package/dist/integrations/storybook/server.js.map +1 -0
  117. package/dist/integrations/storybook/story-url.d.ts +2 -0
  118. package/dist/integrations/storybook/story-url.js +13 -0
  119. package/dist/integrations/storybook/story-url.js.map +1 -0
  120. package/dist/utils/process.d.ts +9 -0
  121. package/dist/utils/process.js +69 -0
  122. package/dist/utils/process.js.map +1 -0
  123. package/docs/agent-workflows.md +146 -0
  124. package/docs/artifacts-and-gc.md +125 -0
  125. package/docs/browser-contract.md +98 -0
  126. package/docs/ci.md +44 -0
  127. package/docs/configuration.md +210 -0
  128. package/docs/getting-started.md +166 -0
  129. package/docs/plans/reflection-implementation-plan.md +898 -0
  130. package/docs/target-ir-and-adapters.md +111 -0
  131. package/docs/validation-process.md +172 -0
  132. package/docs/visual-contract.md +174 -0
  133. package/package.json +62 -0
@@ -0,0 +1,111 @@
1
+ # Target IR and adapters
2
+
3
+ Reflection keeps external input formats separate from the contract runners by compiling them into a small Target IR: a normalized inventory of things Reflection can validate.
4
+
5
+ ```text
6
+ Reflection config optional adapters
7
+ │ │
8
+ └──────────┬─────────┘
9
+
10
+ Target IR
11
+
12
+ contract runners / reports
13
+ ```
14
+
15
+ ## Why Target IR exists
16
+
17
+ Target IR is the seam between _where validation targets come from_ and _how Reflection validates them_. The current Reflection config is one source. Future integrations can compile their own manifests into the same shape without making browser, visual, component, or design runners depend on that integration.
18
+
19
+ This gives us three useful constraints:
20
+
21
+ - **Neutral core:** runner code should work from Reflection concepts, not external product names.
22
+ - **Optional adapters:** adapters can be present or absent without changing normal `reflection run` behavior.
23
+ - **Agent-readable inventory:** agents can inspect a single list of targets and understand the validation surface.
24
+
25
+ ## Current target families
26
+
27
+ The IR currently supports four families:
28
+
29
+ | Family | Source contract | Typical run modes | Purpose |
30
+ | --- | --- | --- | --- |
31
+ | `browser-route` | browser routes | `smoke`, `full` | Render a route and evaluate DOM/layout/console expectations. |
32
+ | `route-visual` | browser visual smoke cases | `smoke`, `full` | Compare route screenshots against read-only baselines. |
33
+ | `component-visual` | Storybook component cases | `visual`, `full` | Compare component story screenshots against read-only baselines. |
34
+ | `design-command` | design command checks | `design`, `full` | Run project-owned design contract commands. |
35
+
36
+ All targets include:
37
+
38
+ - `id` — stable target id.
39
+ - `family` — one of the target families above.
40
+ - `source` — where the target came from, for example `reflection-config` or `adapter`.
41
+ - `runModes` — modes where the target is relevant.
42
+ - `blocking` — whether a failing target should block the run.
43
+
44
+ ## Reflection config compiler
45
+
46
+ `compileReflectionTargets(config)` compiles the typed Reflection config into Target IR.
47
+
48
+ Example:
49
+
50
+ ```ts
51
+ const ir = compileReflectionTargets(config);
52
+
53
+ console.log(ir.targets.map((target) => `${target.family}:${target.id}`));
54
+ // [
55
+ // 'browser-route:login',
56
+ // 'route-visual:login-mobile',
57
+ // 'component-visual:button-primary',
58
+ // 'design-command:tokens'
59
+ // ]
60
+ ```
61
+
62
+ The compiler preserves visual metadata that matters to later review, including zero-valued thresholds. Do not use truthiness checks when copying optional numeric metadata; use explicit `!== undefined` checks so values like `0` survive.
63
+
64
+ ## Adapter contract
65
+
66
+ Adapters should compile their input into the same target shape and mark targets with `source: 'adapter'`.
67
+
68
+ Adapters must not make core runners aware of the source format. The runner should receive normalized route/story/visual/command information and should not branch on adapter names.
69
+
70
+ A good adapter is:
71
+
72
+ - **optional** — normal Reflection config works without it;
73
+ - **validated** — malformed adapter input fails before runner execution;
74
+ - **neutral** — no external product names leak into core commands or reports unless the user explicitly names their own target ids;
75
+ - **lossless enough for review** — route paths, viewports, expectations, baselines, thresholds, and blocking semantics are preserved in IR.
76
+
77
+ ## Route manifest adapter proof
78
+
79
+ Phase 6.2 adds a fixture JSON route-manifest adapter in `src/adapters/route-manifest.ts`. Its job is deliberately narrow: prove that an external manifest can become browser-route Target IR without changing the browser runner.
80
+
81
+ The manifest shape is:
82
+
83
+ ```json
84
+ {
85
+ "project": "example-app",
86
+ "baseUrl": "http://127.0.0.1:5173",
87
+ "routes": [
88
+ {
89
+ "id": "login",
90
+ "path": "/login",
91
+ "viewports": ["desktop", "mobile"],
92
+ "expects": [{ "role": "heading", "name": "Welcome" }],
93
+ "blocking": true
94
+ }
95
+ ]
96
+ }
97
+ ```
98
+
99
+ That adapter emits `browser-route` targets using the generic Target IR. It does not introduce a new runner or a new user-facing external product concept.
100
+
101
+ Use the pure parser for already-loaded JSON:
102
+
103
+ ```ts
104
+ const ir = parseRouteManifestTargets(routeManifestJson);
105
+ ```
106
+
107
+ Use the loader when the manifest lives on disk:
108
+
109
+ ```ts
110
+ const ir = await loadRouteManifestTargets('routes.json');
111
+ ```
@@ -0,0 +1,172 @@
1
+ # Reflection validation process
2
+
3
+ Reflection is meant to be a validation protocol that humans, agents, and CI can run without guessing what changed or whether visual evidence is safe to accept.
4
+
5
+ The command split is intentional:
6
+
7
+ ```bash
8
+ reflection doctor # lightweight CLI/setup check; project contracts run through --config
9
+ reflection run # produce evidence and report.json; pass --config for project contracts
10
+ reflection review # summarize the latest evidence for humans/agents
11
+ reflection update # accept intentional visual changes; never automatic
12
+ ```
13
+
14
+ ## Local validation loop
15
+
16
+ Use this loop before claiming frontend work is complete:
17
+
18
+ ```bash
19
+ reflection doctor
20
+ reflection run --config reflection.config.ts --mode smoke
21
+ reflection review --json
22
+ ```
23
+
24
+ If Reflection is being run from this repository during development, build first and use the local CLI:
25
+
26
+ ```bash
27
+ pnpm build
28
+ node dist/cli.js doctor
29
+ node dist/cli.js run --config examples/basic-react/reflection.config.ts --mode smoke
30
+ node dist/cli.js review --json
31
+ ```
32
+
33
+ Interpret the review result as the contract:
34
+
35
+ - `status: "pass"` means the validation evidence passed.
36
+ - `status: "pass-with-review"` means blocking checks passed, but review items need a human-readable summary and artifact links.
37
+ - `status: "fail"` means blocking checks failed; fix those before calling the work complete.
38
+
39
+ Always include the `reportPath`, blocking failures, review items, and artifact paths when reporting results to a human.
40
+
41
+ ## Agent instructions
42
+
43
+ Agents should treat Reflection as an evidence gate, not as a self-healing tool.
44
+
45
+ Required agent behavior:
46
+
47
+ 1. Run `reflection doctor` before the validation flow when setup may be uncertain. It is currently a lightweight setup check; the configured project contract is exercised by `reflection run --config ...`.
48
+ 2. Run `reflection run --config reflection.config.ts --mode smoke` to generate current evidence.
49
+ 3. Run `reflection review --json` to get the machine-readable summary.
50
+ 4. Fix blocking failures before finishing the task.
51
+ 5. Summarize review items with artifact paths instead of hiding them.
52
+ 6. Never run non-dry `reflection update` without explicit human approval.
53
+ 7. Never run `reflection update` in CI.
54
+
55
+ Agents may propose intentional baseline changes with a dry run:
56
+
57
+ ```bash
58
+ reflection update --route <routeId> --from-run latest --dry-run
59
+ reflection update --case <routeVisualCaseId> --from-run latest --dry-run
60
+ ```
61
+
62
+ `reflection update` currently promotes route-level `visualSmoke` baselines. Component visual baselines still require manual review/copy until component baseline promotion is implemented.
63
+
64
+ Only after explicit human approval may an agent run the matching non-dry update:
65
+
66
+ ```bash
67
+ reflection update --route <routeId> --from-run latest
68
+ # or
69
+ reflection update --case <routeVisualCaseId> --from-run latest
70
+ ```
71
+
72
+ After any non-dry update, inspect the resulting git diff and report exactly which baseline files changed.
73
+
74
+ ## CI validation loop
75
+
76
+ CI should generate and publish evidence, but must never update baselines.
77
+
78
+ Recommended CI command shape:
79
+
80
+ ```bash
81
+ reflection doctor
82
+ reflection run --ci --config reflection.config.ts --mode smoke
83
+ reflection review --report-dir artifacts/reflection --json
84
+ ```
85
+
86
+ `reflection run --ci` writes to `artifacts/reflection` by default. `reflection review` defaults to `.reflection`, so CI review commands must pass the CI report root explicitly.
87
+
88
+ For this repository's built CLI:
89
+
90
+ ```bash
91
+ pnpm install --frozen-lockfile
92
+ pnpm build
93
+ node dist/cli.js doctor
94
+ node dist/cli.js run --ci --config examples/basic-react/reflection.config.ts --mode smoke
95
+ node dist/cli.js review --report-dir artifacts/reflection --json
96
+ ```
97
+
98
+ Upload Reflection run artifacts even on failure so humans and agents can inspect what happened:
99
+
100
+ ```text
101
+ artifacts/reflection/**
102
+ ```
103
+
104
+ For local non-CI runs, the default report root remains `.reflection`. If a project config or command uses `--report-dir`, upload and review that explicit report root instead.
105
+
106
+ ## Baseline update policy
107
+
108
+ Reflection does not silently heal visual diffs.
109
+
110
+ - Normal `reflection run` must not create or update baselines.
111
+ - `reflection update --dry-run` is safe for agents to use when proposing a change.
112
+ - Non-dry `reflection update` is a human-approved mutation step.
113
+ - CI must treat any `reflection update` attempt as invalid.
114
+ - Prefer targeted updates (`--route` or `--case`) over `--all`.
115
+ - `--all` must be explicit and should be reserved for deliberate broad rebaselines.
116
+
117
+ ## Adding Reflection to repository agent files
118
+
119
+ Reflection keeps its canonical operating instructions in this file. Repository-level agent files should carry only a short pointer section so agents discover the validation process without duplicating the whole protocol.
120
+
121
+ Use this file-selection logic when adding Reflection to a repository:
122
+
123
+ 1. Look for existing agent instruction files:
124
+ - `AGENTS.md`
125
+ - `CLAUDE.md`
126
+ - `.github/copilot-instructions.md`
127
+ - `copilot-instructions.md`
128
+ 2. If one or more of those files already exist, add the Reflection validation section to the existing file or files and do not create another agent instruction file.
129
+ 3. If none of those files exist, create `AGENTS.md` and add the Reflection validation section there.
130
+ 4. Keep the canonical process in `docs/validation-process.md`; the agent instruction file should point here instead of copying this full guide.
131
+
132
+ Suggested section:
133
+
134
+ ````markdown
135
+ ## Reflection validation
136
+
137
+ Use Reflection as the UI evidence gate before claiming frontend work is complete.
138
+
139
+ Run:
140
+
141
+ ```bash
142
+ reflection doctor
143
+ reflection run --config reflection.config.ts --mode smoke
144
+ reflection review --json
145
+ ```
146
+
147
+ Rules:
148
+
149
+ - Treat blocking failures as task blockers.
150
+ - Summarize review items with artifact paths for the human.
151
+ - Use `reflection update --route <routeId> --from-run latest --dry-run` only to propose intentional visual changes.
152
+ - Do not run non-dry `reflection update` unless the human explicitly approves it.
153
+ - Never run `reflection update` in CI.
154
+
155
+ Full protocol: `docs/validation-process.md`.
156
+ ````
157
+
158
+ Do not duplicate the full protocol into repository-level agent files. Keep the detailed process here and add only the pointer section above to the selected existing agent file, or to a new `AGENTS.md` when no supported agent instruction file exists.
159
+
160
+ ## Minimum completion evidence
161
+
162
+ When reporting a completed Reflection validation run, include:
163
+
164
+ - command(s) run
165
+ - pass/fail status
166
+ - report path
167
+ - blocking failures, if any
168
+ - review items, if any
169
+ - artifact paths relevant to changed or failing UI
170
+ - whether any baseline update was dry-run only or human-approved non-dry
171
+
172
+ A good final agent message should make the evidence easy to inspect without requiring the human to re-run commands immediately.
@@ -0,0 +1,174 @@
1
+ # Visual contract
2
+
3
+ The visual contract compares current screenshots against approved baseline images. It is deliberately review-first: visual diffs are review-only by default, and baseline updates are explicit human-approved mutations.
4
+
5
+ Reflection currently supports two visual surfaces:
6
+
7
+ - route-level visual smoke cases from browser route screenshots;
8
+ - Storybook component visual cases.
9
+
10
+ ## Evidence screenshots vs baselines
11
+
12
+ Keep these concepts separate:
13
+
14
+ | Concept | Location | Created by | Meaning |
15
+ | --- | --- | --- | --- |
16
+ | Evidence screenshot | `.reflection/runs/<run-id>/**` or configured report root | `reflection run` | What the current run rendered. Safe to regenerate. |
17
+ | Baseline image | Configured `baselineRoot` + `baseline` | `reflection update` after review, or manual fixture setup | Approved reference image. Should be source-controlled when intentional. |
18
+ | Diff image | `.reflection/runs/<run-id>/visual/**/diff.png` | `reflection run` during comparison | Highlight of pixels that differ between evidence and baseline. |
19
+
20
+ Normal runs never mutate baselines.
21
+
22
+ ## Review-only default
23
+
24
+ A visual mismatch or missing baseline is review-only unless the case opts into strict/blocking behavior.
25
+
26
+ Use review-only defaults while adopting Reflection:
27
+
28
+ ```ts
29
+ visualSmoke: [
30
+ {
31
+ id: 'home-mobile',
32
+ route: 'home',
33
+ viewport: 'mobile',
34
+ baselineRoot: 'tests/fixtures/baselines',
35
+ baseline: 'browser/home/mobile.chromium-linux.light.png'
36
+ }
37
+ ]
38
+ ```
39
+
40
+ Promote a visual case to blocking only when the baseline is stable and the team wants CI/task completion to fail on visual drift:
41
+
42
+ ```ts
43
+ {
44
+ id: 'home-mobile',
45
+ route: 'home',
46
+ viewport: 'mobile',
47
+ baseline: 'browser/home/mobile.chromium-linux.light.png',
48
+ strict: true
49
+ }
50
+ ```
51
+
52
+ `strict: true` or `blocking: true` makes a failing visual comparison blocking. Otherwise it produces `pass-with-review` so the artifact can be inspected without hiding the change.
53
+
54
+ ## Route-level visual smoke
55
+
56
+ Route visual smoke cases reuse browser route screenshots. A case must point at a configured route id and viewport:
57
+
58
+ ```ts
59
+ browser: {
60
+ routes: [
61
+ {
62
+ id: 'login',
63
+ path: '/login',
64
+ viewports: ['mobile'],
65
+ expects: [{ screenshot: 'final' }]
66
+ }
67
+ ],
68
+ visualSmoke: [
69
+ {
70
+ id: 'login-mobile',
71
+ route: 'login',
72
+ viewport: 'mobile',
73
+ baselineRoot: 'tests/fixtures/baselines',
74
+ baseline: 'browser/login/mobile.chromium-linux.light.png',
75
+ threshold: { maxDiffPixelRatio: 0.01 }
76
+ }
77
+ ]
78
+ }
79
+ ```
80
+
81
+ If the matching browser route result is missing, the visual check becomes a review item by default or a blocking failure when strict/blocking is enabled.
82
+
83
+ ## Component visual baselines
84
+
85
+ Component visual cases use Storybook. Reflection resolves the configured `storyId` through Storybook `/index.json`, opens the iframe URL, captures a screenshot, and compares it with the configured baseline.
86
+
87
+ ```ts
88
+ component: {
89
+ storybook: {
90
+ command: 'pnpm storybook --host 127.0.0.1 --port 6006',
91
+ readyUrl: 'http://127.0.0.1:6006',
92
+ reuseExisting: true,
93
+ timeoutMs: 60_000
94
+ },
95
+ cases: [
96
+ {
97
+ id: 'button-primary',
98
+ storyId: 'atoms-button--primary',
99
+ viewport: 'component',
100
+ baselineRoot: 'tests/fixtures/baselines',
101
+ baseline: 'components/button/primary.chromium-linux.light.png',
102
+ threshold: { maxDiffPixelRatio: 0.005 }
103
+ }
104
+ ]
105
+ }
106
+ ```
107
+
108
+ ### Pseudo states
109
+
110
+ Prefer story-controlled states. A story named `Button/Hover` or `Button/Focused` is usually more deterministic than moving the mouse in the browser.
111
+
112
+ When the browser must force a state, Reflection requires effective animation stabilization:
113
+
114
+ ```ts
115
+ {
116
+ id: 'button-hover',
117
+ storyId: 'atoms-button--primary',
118
+ baseline: 'components/button/hover.chromium-linux.light.png',
119
+ stateNote: 'Browser-forced hover until a story-controlled hover variant exists.',
120
+ browserState: {
121
+ kind: 'hover',
122
+ selector: 'button',
123
+ animationStabilization: {
124
+ disableAnimations: true
125
+ }
126
+ }
127
+ }
128
+ ```
129
+
130
+ Reports include `statePolicy` metadata:
131
+
132
+ - `story-controlled` when no `browserState` is configured;
133
+ - `browser-forced-with-stabilization` when Reflection applies hover/focus in the browser.
134
+
135
+ ## Thresholds
136
+
137
+ Visual thresholds support:
138
+
139
+ ```ts
140
+ threshold: {
141
+ maxDiffPixels: 25,
142
+ maxDiffPixelRatio: 0.01
143
+ }
144
+ ```
145
+
146
+ `maxDiffPixelRatio: 0` is valid and means exact pixel matching. Reflection preserves zero-valued thresholds intentionally.
147
+
148
+ ## Updating baselines
149
+
150
+ Always inspect artifacts first:
151
+
152
+ ```bash
153
+ reflection review --json
154
+ ```
155
+
156
+ Then dry-run a targeted update:
157
+
158
+ ```bash
159
+ reflection update --route login --from-run latest --dry-run
160
+ reflection update --case login-mobile --from-run latest --dry-run
161
+ ```
162
+
163
+ Only after explicit human approval:
164
+
165
+ ```bash
166
+ reflection update --route login --from-run latest
167
+ reflection update --case login-mobile --from-run latest
168
+ ```
169
+
170
+ `reflection update` currently promotes route-level `visualSmoke` baselines. For component visual cases, inspect the run artifacts and copy an approved `actual.png` to the configured component baseline path manually until targeted component baseline promotion is implemented.
171
+
172
+ After a non-dry update, inspect the git diff and report exactly which baseline files changed.
173
+
174
+ CI must never update baselines. `reflection update --ci` is refused.
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "reflection-check",
3
+ "version": "0.0.1",
4
+ "description": "CLI for evidence-backed rendered UI validation.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "bin": {
16
+ "reflection": "dist/cli.js",
17
+ "reflection-check": "dist/cli.js"
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "docs",
25
+ "LICENSE",
26
+ "README.md"
27
+ ],
28
+ "keywords": [
29
+ "ui-validation",
30
+ "playwright",
31
+ "visual-regression",
32
+ "design-systems",
33
+ "developer-tools"
34
+ ],
35
+ "scripts": {
36
+ "build": "tsc -p tsconfig.json",
37
+ "check": "pnpm typecheck && pnpm test && pnpm build",
38
+ "prepack": "pnpm build",
39
+ "smoke:package": "node scripts/smoke-package-install.mjs",
40
+ "typecheck": "tsc -p tsconfig.json --noEmit",
41
+ "test": "vitest run",
42
+ "reflection": "pnpm build && node dist/cli.js"
43
+ },
44
+ "dependencies": {
45
+ "commander": "^14.0.3",
46
+ "jiti": "^2.7.0",
47
+ "pixelmatch": "^7.2.0",
48
+ "pngjs": "^7.0.0",
49
+ "playwright": "^1.60.0",
50
+ "zod": "^4.4.3"
51
+ },
52
+ "devDependencies": {
53
+ "@types/node": "^25.7.0",
54
+ "@types/pngjs": "^6.0.5",
55
+ "typescript": "^5.6.3",
56
+ "vitest": "^2.1.9"
57
+ },
58
+ "engines": {
59
+ "node": ">=22"
60
+ },
61
+ "packageManager": "pnpm@10.12.1"
62
+ }