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.
- package/LICENSE +21 -0
- package/README.md +55 -0
- package/dist/adapters/route-manifest.d.ts +3 -0
- package/dist/adapters/route-manifest.js +98 -0
- package/dist/adapters/route-manifest.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +93 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/doctor.d.ts +4 -0
- package/dist/commands/doctor.js +5 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/gc.d.ts +8 -0
- package/dist/commands/gc.js +45 -0
- package/dist/commands/gc.js.map +1 -0
- package/dist/commands/review.d.ts +7 -0
- package/dist/commands/review.js +149 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/run.d.ts +10 -0
- package/dist/commands/run.js +168 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/update.d.ts +11 -0
- package/dist/commands/update.js +183 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/contracts/browser/assertions.d.ts +34 -0
- package/dist/contracts/browser/assertions.js +87 -0
- package/dist/contracts/browser/assertions.js.map +1 -0
- package/dist/contracts/browser/browser-contract.d.ts +13 -0
- package/dist/contracts/browser/browser-contract.js +35 -0
- package/dist/contracts/browser/browser-contract.js.map +1 -0
- package/dist/contracts/browser/console-observer.d.ts +6 -0
- package/dist/contracts/browser/console-observer.js +14 -0
- package/dist/contracts/browser/console-observer.js.map +1 -0
- package/dist/contracts/browser/overflow-check.d.ts +6 -0
- package/dist/contracts/browser/overflow-check.js +15 -0
- package/dist/contracts/browser/overflow-check.js.map +1 -0
- package/dist/contracts/browser/route-runner.d.ts +21 -0
- package/dist/contracts/browser/route-runner.js +98 -0
- package/dist/contracts/browser/route-runner.js.map +1 -0
- package/dist/contracts/component/component-visual-contract.d.ts +30 -0
- package/dist/contracts/component/component-visual-contract.js +147 -0
- package/dist/contracts/component/component-visual-contract.js.map +1 -0
- package/dist/contracts/design/command-adapter.d.ts +17 -0
- package/dist/contracts/design/command-adapter.js +60 -0
- package/dist/contracts/design/command-adapter.js.map +1 -0
- package/dist/contracts/design/design-contract.d.ts +8 -0
- package/dist/contracts/design/design-contract.js +149 -0
- package/dist/contracts/design/design-contract.js.map +1 -0
- package/dist/contracts/visual/baseline-compare.d.ts +19 -0
- package/dist/contracts/visual/baseline-compare.js +94 -0
- package/dist/contracts/visual/baseline-compare.js.map +1 -0
- package/dist/contracts/visual/image-diff.d.ts +27 -0
- package/dist/contracts/visual/image-diff.js +58 -0
- package/dist/contracts/visual/image-diff.js.map +1 -0
- package/dist/contracts/visual/thresholds.d.ts +15 -0
- package/dist/contracts/visual/thresholds.js +11 -0
- package/dist/contracts/visual/thresholds.js.map +1 -0
- package/dist/contracts/visual/visual-contract.d.ts +11 -0
- package/dist/contracts/visual/visual-contract.js +32 -0
- package/dist/contracts/visual/visual-contract.js.map +1 -0
- package/dist/core/artifact-store.d.ts +18 -0
- package/dist/core/artifact-store.js +105 -0
- package/dist/core/artifact-store.js.map +1 -0
- package/dist/core/baseline-store.d.ts +18 -0
- package/dist/core/baseline-store.js +56 -0
- package/dist/core/baseline-store.js.map +1 -0
- package/dist/core/config.d.ts +129 -0
- package/dist/core/config.js +159 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/define-reflection.d.ts +2 -0
- package/dist/core/define-reflection.js +4 -0
- package/dist/core/define-reflection.js.map +1 -0
- package/dist/core/exit-codes.d.ts +7 -0
- package/dist/core/exit-codes.js +9 -0
- package/dist/core/exit-codes.js.map +1 -0
- package/dist/core/failure-classifier.d.ts +3 -0
- package/dist/core/failure-classifier.js +19 -0
- package/dist/core/failure-classifier.js.map +1 -0
- package/dist/core/gc.d.ts +19 -0
- package/dist/core/gc.js +161 -0
- package/dist/core/gc.js.map +1 -0
- package/dist/core/manifest.d.ts +23 -0
- package/dist/core/manifest.js +21 -0
- package/dist/core/manifest.js.map +1 -0
- package/dist/core/redaction.d.ts +3 -0
- package/dist/core/redaction.js +63 -0
- package/dist/core/redaction.js.map +1 -0
- package/dist/core/report-schema.d.ts +262 -0
- package/dist/core/report-schema.js +112 -0
- package/dist/core/report-schema.js.map +1 -0
- package/dist/core/report-writer.d.ts +4 -0
- package/dist/core/report-writer.js +77 -0
- package/dist/core/report-writer.js.map +1 -0
- package/dist/core/server-manager.d.ts +23 -0
- package/dist/core/server-manager.js +64 -0
- package/dist/core/server-manager.js.map +1 -0
- package/dist/core/target-ir.d.ts +64 -0
- package/dist/core/target-ir.js +85 -0
- package/dist/core/target-ir.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/playwright/browser-manager.d.ts +2 -0
- package/dist/integrations/playwright/browser-manager.js +5 -0
- package/dist/integrations/playwright/browser-manager.js.map +1 -0
- package/dist/integrations/playwright/context-factory.d.ts +7 -0
- package/dist/integrations/playwright/context-factory.js +19 -0
- package/dist/integrations/playwright/context-factory.js.map +1 -0
- package/dist/integrations/playwright/trace-policy.d.ts +5 -0
- package/dist/integrations/playwright/trace-policy.js +7 -0
- package/dist/integrations/playwright/trace-policy.js.map +1 -0
- package/dist/integrations/storybook/index-json.d.ts +21 -0
- package/dist/integrations/storybook/index-json.js +44 -0
- package/dist/integrations/storybook/index-json.js.map +1 -0
- package/dist/integrations/storybook/server.d.ts +8 -0
- package/dist/integrations/storybook/server.js +23 -0
- package/dist/integrations/storybook/server.js.map +1 -0
- package/dist/integrations/storybook/story-url.d.ts +2 -0
- package/dist/integrations/storybook/story-url.js +13 -0
- package/dist/integrations/storybook/story-url.js.map +1 -0
- package/dist/utils/process.d.ts +9 -0
- package/dist/utils/process.js +69 -0
- package/dist/utils/process.js.map +1 -0
- package/docs/agent-workflows.md +146 -0
- package/docs/artifacts-and-gc.md +125 -0
- package/docs/browser-contract.md +98 -0
- package/docs/ci.md +44 -0
- package/docs/configuration.md +210 -0
- package/docs/getting-started.md +166 -0
- package/docs/plans/reflection-implementation-plan.md +898 -0
- package/docs/target-ir-and-adapters.md +111 -0
- package/docs/validation-process.md +172 -0
- package/docs/visual-contract.md +174 -0
- package/package.json +62 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Artifacts and garbage collection
|
|
2
|
+
|
|
3
|
+
Reflection writes every run as a reviewable artifact bundle. The bundle is designed for humans, agents, and CI systems to inspect after success or failure.
|
|
4
|
+
|
|
5
|
+
## Default artifact roots
|
|
6
|
+
|
|
7
|
+
| Context | Default root |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| Local run | `.reflection` |
|
|
10
|
+
| CI run with `--ci` | `artifacts/reflection` |
|
|
11
|
+
| Explicit override | `--report-dir <path>` |
|
|
12
|
+
|
|
13
|
+
A run is written under:
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
<report-root>/runs/<run-id>/
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The latest run pointer is:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
<report-root>/runs/latest
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Run bundle layout
|
|
26
|
+
|
|
27
|
+
Typical files:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
.reflection/runs/<run-id>/
|
|
31
|
+
report.md
|
|
32
|
+
report.json
|
|
33
|
+
manifest.json
|
|
34
|
+
browser/
|
|
35
|
+
<route-id>/
|
|
36
|
+
<viewport>/
|
|
37
|
+
actual.png
|
|
38
|
+
metadata.json
|
|
39
|
+
visual/
|
|
40
|
+
<case-id>/
|
|
41
|
+
expected.png
|
|
42
|
+
actual.png
|
|
43
|
+
diff.png
|
|
44
|
+
server/
|
|
45
|
+
app.log
|
|
46
|
+
storybook.log
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Not every run contains every folder. For example, a browser-only run may have screenshots but no component visual artifacts.
|
|
50
|
+
|
|
51
|
+
## `report.json`
|
|
52
|
+
|
|
53
|
+
`report.json` is the stable machine-readable summary for agents and CI. It includes:
|
|
54
|
+
|
|
55
|
+
- `runId`, `project`, mode, CI flag, and environment metadata;
|
|
56
|
+
- overall status: `pass`, `pass-with-review`, `fail`, or `error`;
|
|
57
|
+
- structured checks with `id`, `suite`, `target`, `status`, `severity`, artifacts, metadata, and suggested next steps;
|
|
58
|
+
- top-level artifact references.
|
|
59
|
+
|
|
60
|
+
Use `reflection review --json` rather than hand-parsing reports when an agent needs a compact summary of latest evidence.
|
|
61
|
+
|
|
62
|
+
## `report.md`
|
|
63
|
+
|
|
64
|
+
`report.md` is the human-readable report. Link this in PRs or task summaries when the recipient wants to inspect the run quickly.
|
|
65
|
+
|
|
66
|
+
## `manifest.json`
|
|
67
|
+
|
|
68
|
+
`manifest.json` records the report files currently tracked for run retention plus whether the run is pinned.
|
|
69
|
+
|
|
70
|
+
Garbage collection uses this manifest to decide whether a run directory is eligible for deletion. Runs with missing or malformed manifests are skipped rather than deleted. Runtime evidence such as browser screenshots, visual artifacts, and logs still belongs to the run directory even though the current manifest only enumerates report files.
|
|
71
|
+
|
|
72
|
+
## Evidence artifacts
|
|
73
|
+
|
|
74
|
+
Evidence artifacts are run-scoped and safe to regenerate:
|
|
75
|
+
|
|
76
|
+
- screenshots from browser and component checks;
|
|
77
|
+
- visual `expected`, `actual`, and `diff` images copied or generated for the run;
|
|
78
|
+
- server logs;
|
|
79
|
+
- reports and metadata.
|
|
80
|
+
|
|
81
|
+
Evidence artifacts are not approved baselines. Approved baselines live outside run directories in the configured baseline root.
|
|
82
|
+
|
|
83
|
+
## Garbage collection
|
|
84
|
+
|
|
85
|
+
Use GC to clean old run artifacts without touching baselines:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
reflection gc --dry-run
|
|
89
|
+
reflection gc --delete
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
With a custom report root:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
reflection gc --report-dir artifacts/reflection --dry-run
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Safety behavior:
|
|
99
|
+
|
|
100
|
+
- GC only operates under `<report-root>/runs`.
|
|
101
|
+
- `latest` is never treated as a run directory.
|
|
102
|
+
- Symlinked `runs` directories are refused.
|
|
103
|
+
- Symlinked run directories are skipped.
|
|
104
|
+
- Runs with missing, invalid, mismatched, or pinned manifests are skipped.
|
|
105
|
+
- Baseline roots are not part of GC.
|
|
106
|
+
|
|
107
|
+
Use `--dry-run` first in local development and CI diagnostics. Use `--delete` only when the listed eligible run directories are safe to remove.
|
|
108
|
+
|
|
109
|
+
## What to commit
|
|
110
|
+
|
|
111
|
+
Usually commit:
|
|
112
|
+
|
|
113
|
+
- `reflection.config.ts`;
|
|
114
|
+
- baseline images after explicit approval;
|
|
115
|
+
- docs or agent pointer sections;
|
|
116
|
+
- workflow files that run Reflection.
|
|
117
|
+
|
|
118
|
+
Usually do not commit:
|
|
119
|
+
|
|
120
|
+
- `.reflection/runs/**`;
|
|
121
|
+
- `artifacts/reflection/runs/**`;
|
|
122
|
+
- server logs;
|
|
123
|
+
- transient screenshots and visual diffs.
|
|
124
|
+
|
|
125
|
+
CI should upload run artifacts even when validation fails so humans and agents can inspect evidence.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Browser contract
|
|
2
|
+
|
|
3
|
+
The browser contract validates rendered routes in a real browser. It answers: can the app render the expected route, at the expected viewport, without obvious layout or console regressions?
|
|
4
|
+
|
|
5
|
+
Browser route checks run in `smoke` and `full` modes.
|
|
6
|
+
|
|
7
|
+
## What a route check does
|
|
8
|
+
|
|
9
|
+
For each configured route and viewport, Reflection:
|
|
10
|
+
|
|
11
|
+
1. Starts or reuses the configured app server.
|
|
12
|
+
2. Opens `baseUrl + route.path` in Playwright.
|
|
13
|
+
3. Applies configured screenshot masking selectors.
|
|
14
|
+
4. Evaluates route expectations.
|
|
15
|
+
5. Captures screenshot evidence when requested.
|
|
16
|
+
6. Emits a structured `CheckResult` into `report.json`.
|
|
17
|
+
|
|
18
|
+
A route check is browser evidence, not a baseline approval. A screenshot captured by `{ screenshot: 'final' }` is evidence for the current run. It becomes a visual comparison input only when referenced by a `visualSmoke` case.
|
|
19
|
+
|
|
20
|
+
## Config example
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
browser: {
|
|
24
|
+
enabled: true,
|
|
25
|
+
blocking: true,
|
|
26
|
+
baseUrl: 'http://127.0.0.1:5173',
|
|
27
|
+
server: {
|
|
28
|
+
command: 'pnpm dev --host 127.0.0.1',
|
|
29
|
+
readyUrl: 'http://127.0.0.1:5173',
|
|
30
|
+
reuseExisting: true,
|
|
31
|
+
timeoutMs: 60_000
|
|
32
|
+
},
|
|
33
|
+
maskSelectors: ['[data-reflection-mask]'],
|
|
34
|
+
routes: [
|
|
35
|
+
{
|
|
36
|
+
id: 'login',
|
|
37
|
+
path: '/login',
|
|
38
|
+
viewports: ['desktop', 'mobile'],
|
|
39
|
+
expects: [
|
|
40
|
+
{ role: 'heading', name: 'Login' },
|
|
41
|
+
{ label: 'Email' },
|
|
42
|
+
{ label: 'Password' },
|
|
43
|
+
{ role: 'button', name: 'Sign in' },
|
|
44
|
+
{ noText: 'Register' },
|
|
45
|
+
{ noHorizontalOverflow: true },
|
|
46
|
+
{ noConsoleErrors: true },
|
|
47
|
+
{ screenshot: 'final' }
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Expectations
|
|
55
|
+
|
|
56
|
+
| Expectation | Use for |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| `urlIncludes` | Route redirects or canonical URL fragments. |
|
|
59
|
+
| `urlEquals` | Exact final URL checks. |
|
|
60
|
+
| `role` + optional `name` | Accessible UI assertions; preferred for user-visible structure. |
|
|
61
|
+
| `label` | Form field labels. |
|
|
62
|
+
| `text` | Required visible text. |
|
|
63
|
+
| `noText` | Text that must not appear, such as old copy or errors. |
|
|
64
|
+
| `selector` | A CSS selector that must exist. |
|
|
65
|
+
| `elementVisible` | A CSS selector that must be visible. |
|
|
66
|
+
| `elementNotVisible` | A CSS selector that must not be visible. |
|
|
67
|
+
| `noHorizontalOverflow` | Mobile/layout guard against body-level horizontal overflow. |
|
|
68
|
+
| `noConsoleErrors` | Fails on browser console errors captured during the route visit. |
|
|
69
|
+
| `screenshot` | Captures current route screenshot evidence. |
|
|
70
|
+
|
|
71
|
+
Prefer accessible expectations (`role`, `label`, visible text) before selectors. Selectors are useful for stable app-specific contracts, but they should not become a substitute for testing what a user can perceive.
|
|
72
|
+
|
|
73
|
+
## Viewports
|
|
74
|
+
|
|
75
|
+
Routes can list one or more named viewports:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
viewports: ['desktop', 'mobile']
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Reflection stores viewport metadata in each route check. Route-level visual smoke cases match against this metadata, so the `route` and `viewport` fields in a `visualSmoke` case must correspond to an actual browser route result.
|
|
82
|
+
|
|
83
|
+
## Console and layout failures
|
|
84
|
+
|
|
85
|
+
`noConsoleErrors` and `noHorizontalOverflow` are intentionally simple high-signal checks:
|
|
86
|
+
|
|
87
|
+
- `noConsoleErrors` catches runtime exceptions and error logs that can otherwise hide behind a passing screenshot.
|
|
88
|
+
- `noHorizontalOverflow` catches common responsive regressions before visual review.
|
|
89
|
+
|
|
90
|
+
These should usually be included on smoke routes.
|
|
91
|
+
|
|
92
|
+
## Screenshots are evidence
|
|
93
|
+
|
|
94
|
+
Browser screenshots are run artifacts under `.reflection/runs/<run-id>/browser/<route-id>/<viewport>/actual.png`, with route metadata beside them. They answer "what did this run render?"
|
|
95
|
+
|
|
96
|
+
They are not approved baselines. Approved baselines are separate files configured by `visualSmoke` or component visual cases. Normal `reflection run` does not create or mutate baselines.
|
|
97
|
+
|
|
98
|
+
See `docs/visual-contract.md` for visual comparison behavior.
|
package/docs/ci.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Reflection in CI
|
|
2
|
+
|
|
3
|
+
Reflection is designed to be safe in CI: runs create evidence artifacts, but they do not mutate visual baselines.
|
|
4
|
+
|
|
5
|
+
## Recommended command
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
reflection run --ci --config reflection.config.ts --mode smoke
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
When `--ci` is enabled and `--report-dir` is not provided, Reflection writes artifacts to:
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
artifacts/reflection
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The generated report is written under:
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
artifacts/reflection/runs/<run-id>/report.md
|
|
21
|
+
artifacts/reflection/runs/<run-id>/report.json
|
|
22
|
+
artifacts/reflection/runs/latest
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## CI defaults
|
|
26
|
+
|
|
27
|
+
- `reflection run --ci` records the environment profile as `ci`.
|
|
28
|
+
- Workers default to `1` for stable browser and visual evidence.
|
|
29
|
+
- Videos and traces remain off by default unless a future explicit policy enables them.
|
|
30
|
+
- CI never updates baselines. Baseline promotion must use `reflection update` outside CI with an explicit human review step; `reflection update --ci` is refused.
|
|
31
|
+
|
|
32
|
+
## Exit codes
|
|
33
|
+
|
|
34
|
+
| Exit code | Meaning |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| `0` | Success, including `pass` and `pass-with-review` reports. Review items can be uploaded as artifacts without failing CI. |
|
|
37
|
+
| `1` | Blocking validation failure. A blocking check failed. |
|
|
38
|
+
| `2` | Tool/configuration error. Reflection could not complete because setup, config, dependency, or runtime execution failed. |
|
|
39
|
+
| `64` | Invalid CLI usage, such as an unsupported run mode or invalid option value. |
|
|
40
|
+
| `69` | Missing dependency. Reserved for dependency checks such as browser/runtime availability. |
|
|
41
|
+
|
|
42
|
+
## GitHub Actions example
|
|
43
|
+
|
|
44
|
+
See `.github/workflows/reflection.yml` for a repo-owned example that installs dependencies, builds Reflection, runs `reflection run --ci`, and uploads `artifacts/reflection` for review.
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# Reflection configuration
|
|
2
|
+
|
|
3
|
+
Reflection reads a project config from the path passed to `reflection run --config <path>`. Use `reflection.config.ts` as the conventional root filename. The config is validated before runners execute, so malformed contracts fail as setup errors instead of producing ambiguous reports.
|
|
4
|
+
|
|
5
|
+
The loader supports TypeScript config files (`.ts`, `.mts`, `.cts`) through `jiti` and JavaScript ESM config files through dynamic import.
|
|
6
|
+
|
|
7
|
+
## Minimal shape
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { defineReflection } from 'reflection-check';
|
|
11
|
+
|
|
12
|
+
export default defineReflection({
|
|
13
|
+
project: 'my-app',
|
|
14
|
+
contracts: {
|
|
15
|
+
browser: {
|
|
16
|
+
baseUrl: 'http://127.0.0.1:5173',
|
|
17
|
+
routes: []
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
During Reflection repository development, examples import `defineReflection` from source instead:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { defineReflection } from '../../src/core/define-reflection';
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Top-level fields
|
|
30
|
+
|
|
31
|
+
| Field | Required | Notes |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| `project` | yes | Stable project name written into reports. |
|
|
34
|
+
| `contracts.browser` | no | Browser route and route-level visual smoke checks. |
|
|
35
|
+
| `contracts.design` | no | Project-owned command checks. |
|
|
36
|
+
| `contracts.component` | no | Storybook-backed component visual checks. |
|
|
37
|
+
|
|
38
|
+
Run mode is currently selected by the CLI with `reflection run --mode <mode>` and defaults to `smoke` when omitted. The `--ci` flag changes CI/report-root behavior but does not select a separate config-defined run mode yet.
|
|
39
|
+
|
|
40
|
+
## Run modes
|
|
41
|
+
|
|
42
|
+
| Mode | Runs |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| `smoke` | Browser route checks and route-level visual smoke cases. |
|
|
45
|
+
| `design` | Design command checks. |
|
|
46
|
+
| `visual` | Storybook component visual checks. |
|
|
47
|
+
| `full` | Browser, design, and component contracts. |
|
|
48
|
+
|
|
49
|
+
## Browser contract
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
browser: {
|
|
53
|
+
enabled: true,
|
|
54
|
+
blocking: true,
|
|
55
|
+
baseUrl: 'http://127.0.0.1:5173',
|
|
56
|
+
server: {
|
|
57
|
+
command: 'pnpm dev --host 127.0.0.1',
|
|
58
|
+
readyUrl: 'http://127.0.0.1:5173',
|
|
59
|
+
reuseExisting: true,
|
|
60
|
+
timeoutMs: 60_000
|
|
61
|
+
},
|
|
62
|
+
maskSelectors: ['[data-reflection-mask]'],
|
|
63
|
+
routes: [
|
|
64
|
+
{
|
|
65
|
+
id: 'home',
|
|
66
|
+
name: 'Home route',
|
|
67
|
+
path: '/',
|
|
68
|
+
viewports: ['desktop', 'mobile'],
|
|
69
|
+
expects: [
|
|
70
|
+
{ role: 'heading', name: 'Home' },
|
|
71
|
+
{ noHorizontalOverflow: true },
|
|
72
|
+
{ noConsoleErrors: true },
|
|
73
|
+
{ screenshot: 'final' }
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
visualSmoke: [
|
|
78
|
+
{
|
|
79
|
+
id: 'home-mobile',
|
|
80
|
+
route: 'home',
|
|
81
|
+
viewport: 'mobile',
|
|
82
|
+
baselineRoot: 'tests/fixtures/baselines',
|
|
83
|
+
baseline: 'browser/home/mobile.chromium-linux.light.png',
|
|
84
|
+
threshold: { maxDiffPixelRatio: 0.01 },
|
|
85
|
+
strict: false
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Browser fields:
|
|
92
|
+
|
|
93
|
+
| Field | Notes |
|
|
94
|
+
| --- | --- |
|
|
95
|
+
| `enabled` | Defaults to `true`; set `false` to skip. |
|
|
96
|
+
| `blocking` | Defaults to `true`; route assertion failures are blocking unless configured otherwise by current runner behavior. |
|
|
97
|
+
| `baseUrl` | Absolute URL used to visit route paths. |
|
|
98
|
+
| `server` | Optional managed server. If omitted, Reflection assumes `baseUrl` is already reachable. |
|
|
99
|
+
| `maskSelectors` | Selectors masked in browser screenshots. |
|
|
100
|
+
| `routes` | Route assertions executed in `smoke` and `full` modes. |
|
|
101
|
+
| `visualSmoke` | Route screenshot baseline comparisons driven from successful browser screenshots. |
|
|
102
|
+
|
|
103
|
+
Supported browser expectations:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
{ urlIncludes: '/dashboard' }
|
|
107
|
+
{ urlEquals: 'http://127.0.0.1:5173/dashboard' }
|
|
108
|
+
{ role: 'heading', name: 'Dashboard' }
|
|
109
|
+
{ label: 'Email' }
|
|
110
|
+
{ text: 'Welcome back' }
|
|
111
|
+
{ noText: 'Stack trace' }
|
|
112
|
+
{ selector: '[data-ready="true"]' }
|
|
113
|
+
{ elementVisible: '[data-toast]' }
|
|
114
|
+
{ elementNotVisible: '[data-loading]' }
|
|
115
|
+
{ noHorizontalOverflow: true }
|
|
116
|
+
{ noConsoleErrors: true }
|
|
117
|
+
{ screenshot: 'final' }
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Route-level visual smoke cases
|
|
121
|
+
|
|
122
|
+
Route visual smoke cases compare the screenshot captured by a browser route check against a read-only baseline.
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
visualSmoke: [
|
|
126
|
+
{
|
|
127
|
+
id: 'login-mobile',
|
|
128
|
+
route: 'login',
|
|
129
|
+
viewport: 'mobile',
|
|
130
|
+
baselineRoot: 'tests/fixtures/baselines',
|
|
131
|
+
baseline: 'browser/login/mobile.chromium-linux.light.png',
|
|
132
|
+
threshold: {
|
|
133
|
+
maxDiffPixels: 25,
|
|
134
|
+
maxDiffPixelRatio: 0.01
|
|
135
|
+
},
|
|
136
|
+
blocking: false,
|
|
137
|
+
strict: false
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`blocking: true` or `strict: true` promotes a visual diff to a blocking failure. By default, visual diffs and missing baselines are review-only. The current `reflection update` command promotes route-level `visualSmoke` baselines only; component visual baselines should be copied or reviewed manually until component update support lands.
|
|
143
|
+
|
|
144
|
+
## Design command contract
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
design: {
|
|
148
|
+
enabled: true,
|
|
149
|
+
commands: [
|
|
150
|
+
{
|
|
151
|
+
id: 'tokens',
|
|
152
|
+
command: 'pnpm design:check',
|
|
153
|
+
cwd: '.',
|
|
154
|
+
blocking: true
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Each design command runs as a project-owned process and writes stdout/stderr evidence into the run artifacts. Use this for token checks, lint-like design-system checks, or other deterministic commands that should participate in the same report.
|
|
161
|
+
|
|
162
|
+
## Component visual contract
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
component: {
|
|
166
|
+
enabled: true,
|
|
167
|
+
storybook: {
|
|
168
|
+
command: 'pnpm storybook --host 127.0.0.1 --port 6006',
|
|
169
|
+
readyUrl: 'http://127.0.0.1:6006',
|
|
170
|
+
reuseExisting: true,
|
|
171
|
+
timeoutMs: 60_000
|
|
172
|
+
},
|
|
173
|
+
cases: [
|
|
174
|
+
{
|
|
175
|
+
id: 'primary-button',
|
|
176
|
+
storyId: 'atoms-button--primary',
|
|
177
|
+
viewport: 'component',
|
|
178
|
+
baselineRoot: 'tests/fixtures/baselines',
|
|
179
|
+
baseline: 'components/button/primary.chromium-linux.light.png',
|
|
180
|
+
threshold: { maxDiffPixelRatio: 0.005 },
|
|
181
|
+
stateNote: 'Preferred: story-controlled primary state.'
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
id: 'primary-button-hover',
|
|
185
|
+
storyId: 'atoms-button--primary',
|
|
186
|
+
baselineRoot: 'tests/fixtures/baselines',
|
|
187
|
+
baseline: 'components/button/primary-hover.chromium-linux.light.png',
|
|
188
|
+
browserState: {
|
|
189
|
+
kind: 'hover',
|
|
190
|
+
selector: 'button',
|
|
191
|
+
animationStabilization: { disableAnimations: true }
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Component visual cases resolve Storybook `/index.json`, open the story iframe, capture an actual screenshot, and compare it against the configured baseline.
|
|
199
|
+
|
|
200
|
+
Pseudo-state policy:
|
|
201
|
+
|
|
202
|
+
- Prefer story-controlled variants for hover, focus, active, selected, open, and disabled states.
|
|
203
|
+
- If a browser-forced state is necessary, configure `browserState` with `kind: 'hover' | 'focus'`, a selector, and effective animation stabilization.
|
|
204
|
+
- Reflection records `statePolicy` metadata in reports so reviewers can tell whether the state came from the story or was forced in the browser.
|
|
205
|
+
|
|
206
|
+
## Artifact roots
|
|
207
|
+
|
|
208
|
+
Local runs default to `.reflection`. CI runs default to `artifacts/reflection` when `--ci` is passed. Either can be overridden with `--report-dir`.
|
|
209
|
+
|
|
210
|
+
Baselines are separate from run artifacts. They live wherever `baselineRoot` plus `baseline` points, and normal runs never update them.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Getting started with Reflection
|
|
2
|
+
|
|
3
|
+
Reflection is a local-first CLI for evidence-backed rendered UI validation. A configured run can start a dev server, visit routes, assert browser expectations, capture screenshots, compare visual baselines, and write reviewable reports.
|
|
4
|
+
|
|
5
|
+
Use this guide when adding Reflection to a new repository manually.
|
|
6
|
+
|
|
7
|
+
## 1. Install
|
|
8
|
+
|
|
9
|
+
Install the package as a development dependency:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add -D reflection-check
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Then verify the CLI is available:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm exec reflection doctor
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The package install exposes both `reflection` and `reflection-check` binaries. The docs use `reflection` as the primary command.
|
|
22
|
+
|
|
23
|
+
For local tarball testing before a registry publish, create and install a packed artifact instead.
|
|
24
|
+
|
|
25
|
+
From the Reflection repository:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pnpm install --frozen-lockfile
|
|
29
|
+
pnpm pack
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
From the consuming repository:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pnpm add -D /absolute/path/to/reflection/reflection-check-0.0.1.tgz
|
|
36
|
+
pnpm exec reflection doctor
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 2. Add a minimal config
|
|
40
|
+
|
|
41
|
+
Create `reflection.config.ts` at the repository root:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { defineReflection } from 'reflection-check';
|
|
45
|
+
|
|
46
|
+
export default defineReflection({
|
|
47
|
+
project: 'my-app',
|
|
48
|
+
contracts: {
|
|
49
|
+
browser: {
|
|
50
|
+
enabled: true,
|
|
51
|
+
blocking: true,
|
|
52
|
+
baseUrl: 'http://127.0.0.1:5173',
|
|
53
|
+
server: {
|
|
54
|
+
command: 'pnpm dev --host 127.0.0.1',
|
|
55
|
+
readyUrl: 'http://127.0.0.1:5173',
|
|
56
|
+
reuseExisting: true,
|
|
57
|
+
timeoutMs: 60_000
|
|
58
|
+
},
|
|
59
|
+
routes: [
|
|
60
|
+
{
|
|
61
|
+
id: 'home',
|
|
62
|
+
path: '/',
|
|
63
|
+
viewports: ['desktop', 'mobile'],
|
|
64
|
+
expects: [
|
|
65
|
+
{ role: 'heading', name: 'Home' },
|
|
66
|
+
{ noHorizontalOverflow: true },
|
|
67
|
+
{ noConsoleErrors: true },
|
|
68
|
+
{ screenshot: 'final' }
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
During Reflection repository development, the in-repo fixture imports the helper from source instead of the published package.
|
|
78
|
+
|
|
79
|
+
## 3. Run the local validation loop
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
reflection doctor
|
|
83
|
+
reflection run --config reflection.config.ts --mode smoke
|
|
84
|
+
reflection review --latest
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
During Reflection development, use the built CLI:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pnpm build
|
|
91
|
+
node dist/cli.js doctor
|
|
92
|
+
node dist/cli.js run --config examples/basic-react/reflection.config.ts --mode smoke
|
|
93
|
+
node dist/cli.js review --latest
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`reflection run` writes reports and artifacts under `.reflection/runs/<run-id>/` by default.
|
|
97
|
+
|
|
98
|
+
`reflection doctor` is currently a lightweight setup check. The configured project contract is exercised by `reflection run --config reflection.config.ts ...`.
|
|
99
|
+
|
|
100
|
+
Important files:
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
.reflection/runs/latest
|
|
104
|
+
.reflection/runs/<run-id>/report.md
|
|
105
|
+
.reflection/runs/<run-id>/report.json
|
|
106
|
+
.reflection/runs/<run-id>/manifest.json
|
|
107
|
+
.reflection/runs/<run-id>/browser/**
|
|
108
|
+
.reflection/runs/<run-id>/visual/**
|
|
109
|
+
.reflection/runs/<run-id>/server/**
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## 4. Understand the result
|
|
113
|
+
|
|
114
|
+
Reflection has three useful completion states:
|
|
115
|
+
|
|
116
|
+
- `pass` — blocking checks passed and there are no review-only visual items.
|
|
117
|
+
- `pass-with-review` — blocking checks passed, but there are review items such as non-strict visual diffs or missing baselines.
|
|
118
|
+
- `fail` / `error` — blocking validation failed or the tool could not complete.
|
|
119
|
+
|
|
120
|
+
Agents and CI should treat `fail` and `error` as task blockers. `pass-with-review` is intentionally not the same as failure: it asks a human or agent to inspect artifacts and decide whether visible changes are expected.
|
|
121
|
+
|
|
122
|
+
## 5. Add a visual baseline only after review
|
|
123
|
+
|
|
124
|
+
The first visual run may produce review items if no baseline exists yet. Inspect the actual screenshot first. If it is intentional, propose an update dry-run:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
reflection update --config reflection.config.ts --route home --from-run latest --dry-run
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Only after explicit human approval should a non-dry update run:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
reflection update --config reflection.config.ts --route home --from-run latest
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Normal `reflection run` never updates baselines.
|
|
137
|
+
|
|
138
|
+
## 6. Add a short agent pointer
|
|
139
|
+
|
|
140
|
+
Do not copy every Reflection rule into `AGENTS.md` or another agent file. Add a short pointer to the canonical process instead:
|
|
141
|
+
|
|
142
|
+
````md
|
|
143
|
+
## Reflection validation
|
|
144
|
+
|
|
145
|
+
Use Reflection as the UI evidence gate before claiming frontend work is complete.
|
|
146
|
+
|
|
147
|
+
Run:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
reflection doctor
|
|
151
|
+
reflection run --config reflection.config.ts --mode smoke
|
|
152
|
+
reflection review --json
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Rules:
|
|
156
|
+
|
|
157
|
+
- Treat blocking failures as task blockers.
|
|
158
|
+
- Summarize review items with artifact paths.
|
|
159
|
+
- Use `reflection update --dry-run` only to propose intentional visual changes.
|
|
160
|
+
- Do not run non-dry `reflection update` unless the human explicitly approves it.
|
|
161
|
+
- Never run `reflection update` in CI.
|
|
162
|
+
|
|
163
|
+
Full protocol: `docs/validation-process.md`.
|
|
164
|
+
````
|
|
165
|
+
|
|
166
|
+
See `docs/agent-workflows.md` for the agent-facing workflow and `docs/configuration.md` for the full config shape currently supported.
|