my-frontend-observer 0.3.0 → 0.5.0
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/CHANGELOG.md +112 -0
- package/README.md +59 -7
- package/dist/application/comparisonService.d.ts +56 -0
- package/dist/application/comparisonService.js +77 -0
- package/dist/application/comparisonService.js.map +1 -0
- package/dist/application/frontendContractEvaluationService.d.ts +49 -0
- package/dist/application/frontendContractEvaluationService.js +112 -0
- package/dist/application/frontendContractEvaluationService.js.map +1 -0
- package/dist/application/frontendContractPersistenceService.d.ts +56 -0
- package/dist/application/frontendContractPersistenceService.js +91 -0
- package/dist/application/frontendContractPersistenceService.js.map +1 -0
- package/dist/artifacts/artifactReader.d.ts +19 -0
- package/dist/artifacts/artifactReader.js +36 -0
- package/dist/artifacts/artifactReader.js.map +1 -0
- package/dist/artifacts/comparisonArtifactReader.d.ts +18 -0
- package/dist/artifacts/comparisonArtifactReader.js +35 -0
- package/dist/artifacts/comparisonArtifactReader.js.map +1 -0
- package/dist/artifacts/comparisonArtifactWriter.d.ts +41 -0
- package/dist/artifacts/comparisonArtifactWriter.js +67 -0
- package/dist/artifacts/comparisonArtifactWriter.js.map +1 -0
- package/dist/artifacts/frontendContractArtifactReader.d.ts +24 -0
- package/dist/artifacts/frontendContractArtifactReader.js +47 -0
- package/dist/artifacts/frontendContractArtifactReader.js.map +1 -0
- package/dist/artifacts/frontendContractArtifactWriter.d.ts +34 -0
- package/dist/artifacts/frontendContractArtifactWriter.js +70 -0
- package/dist/artifacts/frontendContractArtifactWriter.js.map +1 -0
- package/dist/artifacts/frontendContractEvaluationArtifactReader.d.ts +17 -0
- package/dist/artifacts/frontendContractEvaluationArtifactReader.js +34 -0
- package/dist/artifacts/frontendContractEvaluationArtifactReader.js.map +1 -0
- package/dist/artifacts/frontendContractEvaluationArtifactWriter.d.ts +32 -0
- package/dist/artifacts/frontendContractEvaluationArtifactWriter.js +58 -0
- package/dist/artifacts/frontendContractEvaluationArtifactWriter.js.map +1 -0
- package/dist/cli.js +712 -3
- package/dist/cli.js.map +1 -1
- package/dist/domain/comparison.d.ts +198 -0
- package/dist/domain/comparison.js +324 -0
- package/dist/domain/comparison.js.map +1 -0
- package/dist/domain/comparisonEngine.d.ts +48 -0
- package/dist/domain/comparisonEngine.js +694 -0
- package/dist/domain/comparisonEngine.js.map +1 -0
- package/dist/domain/comparisonIdentity.d.ts +13 -0
- package/dist/domain/comparisonIdentity.js +46 -0
- package/dist/domain/comparisonIdentity.js.map +1 -0
- package/dist/domain/evidence.d.ts +2 -0
- package/dist/domain/evidence.js +4 -0
- package/dist/domain/evidence.js.map +1 -1
- package/dist/domain/frontendContractEvaluation.d.ts +57 -0
- package/dist/domain/frontendContractEvaluation.js +454 -0
- package/dist/domain/frontendContractEvaluation.js.map +1 -0
- package/dist/domain/frontendContractEvaluationArtifact.d.ts +65 -0
- package/dist/domain/frontendContractEvaluationArtifact.js +108 -0
- package/dist/domain/frontendContractEvaluationArtifact.js.map +1 -0
- package/dist/domain/frontendContractIdentity.d.ts +39 -0
- package/dist/domain/frontendContractIdentity.js +70 -0
- package/dist/domain/frontendContractIdentity.js.map +1 -0
- package/dist/domain/frontendContracts.d.ts +188 -0
- package/dist/domain/frontendContracts.js +260 -0
- package/dist/domain/frontendContracts.js.map +1 -0
- package/dist/domain/relationships.d.ts +168 -0
- package/dist/domain/relationships.js +343 -0
- package/dist/domain/relationships.js.map +1 -0
- package/dist/index.d.ts +36 -1
- package/dist/index.js +20 -1
- package/dist/index.js.map +1 -1
- package/docs/ARCHITECTURE.md +173 -3
- package/docs/CI_CD.md +76 -0
- package/docs/COMMANDS.md +299 -0
- package/docs/CONTRACTS.md +270 -6
- package/docs/CURRENT_STATE.md +183 -8
- package/docs/DEVELOPMENT.md +83 -12
- package/docs/PROJECT_OVERVIEW.md +18 -8
- package/docs/QUICKSTART.md +10 -0
- package/docs/RELEASE.md +17 -8
- package/docs/ROADMAP.md +10 -0
- package/docs/SECURITY.md +26 -1
- package/docs/WORKFLOWS.md +113 -6
- package/package.json +1 -1
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
## Current scaffold architecture
|
|
4
4
|
|
|
5
5
|
The current repository is one published TypeScript ESM package
|
|
6
|
-
(`my-frontend-observer@0.
|
|
6
|
+
(`my-frontend-observer@0.5.0`):
|
|
7
7
|
|
|
8
8
|
- `src/cli.ts` is the real, thin `observe` command entry point (argument
|
|
9
9
|
parsing/output only).
|
|
@@ -45,8 +45,8 @@ Playwright or the filesystem-write path directly. Batch 6 closed the
|
|
|
45
45
|
remaining real-Chromium coverage gap (a genuine navigation failure, distinct
|
|
46
46
|
from a readiness timeout or a pre-launch safety rejection) and validated the
|
|
47
47
|
packed npm tarball end to end in a clean consumer environment, independent
|
|
48
|
-
of the source checkout.
|
|
49
|
-
behavior.
|
|
48
|
+
of the source checkout. At the end of the v0.1 implementation there was no
|
|
49
|
+
controlled-scroll or comparison behavior.
|
|
50
50
|
|
|
51
51
|
## Current v0.2 architecture (released/current architecture)
|
|
52
52
|
|
|
@@ -108,6 +108,176 @@ is still one canonical `observe()` application use case and one artifact
|
|
|
108
108
|
writer; `scrollScenarioEvidence` is simply one more optional field on the
|
|
109
109
|
same `ObservationArtifact`.
|
|
110
110
|
|
|
111
|
+
## Current v0.4 architecture (released/current architecture)
|
|
112
|
+
|
|
113
|
+
v0.4 adds one new downstream pipeline that consumes `ObservationArtifact`
|
|
114
|
+
values rather than producing them - it never adds a second browser lifecycle,
|
|
115
|
+
target resolver, or observation engine:
|
|
116
|
+
|
|
117
|
+
```text
|
|
118
|
+
ObservationArtifact before ObservationArtifact after
|
|
119
|
+
\ /
|
|
120
|
+
`--------. .-------'
|
|
121
|
+
\ /
|
|
122
|
+
artifact reader (src/artifacts/artifactReader.ts)
|
|
123
|
+
↓
|
|
124
|
+
comparability evaluation (src/domain/comparisonEngine.ts)
|
|
125
|
+
↓
|
|
126
|
+
canonical relationship derivation, called for each side independently
|
|
127
|
+
(src/domain/relationships.ts#deriveLayoutRelationships)
|
|
128
|
+
↓
|
|
129
|
+
canonical comparison derivation
|
|
130
|
+
(src/domain/comparisonEngine.ts#compareObservations)
|
|
131
|
+
↓
|
|
132
|
+
ComparisonArtifact
|
|
133
|
+
↓
|
|
134
|
+
atomic comparison writer (src/artifacts/comparisonArtifactWriter.ts)
|
|
135
|
+
|
|
136
|
+
CLI `compare`
|
|
137
|
+
↓
|
|
138
|
+
application service only (src/application/comparisonService.ts)
|
|
139
|
+
↓
|
|
140
|
+
[reader → domain comparison → writer, as above]
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`src/domain/relationships.ts` froze the layout-relationship contract and
|
|
144
|
+
implements the one canonical pure derivation,
|
|
145
|
+
`deriveLayoutRelationships(observation, options?)`: horizontal/vertical
|
|
146
|
+
order, area overlap, relative width, geometric fit, vertical sequencing,
|
|
147
|
+
page-width fit/exceeds, and a standalone `deriveTargetClipping(record)` -
|
|
148
|
+
all computed only from an already-captured `ObservationArtifact`'s own
|
|
149
|
+
`targetEvidence`/`pageEvidence`, never from a second browser query. DOM
|
|
150
|
+
containment is read directly from the existing v0.2 `TargetContainment`
|
|
151
|
+
evidence rather than re-derived, and stays a distinct concept from
|
|
152
|
+
geometric fit.
|
|
153
|
+
|
|
154
|
+
`src/domain/comparisonEngine.ts` implements the one canonical pure
|
|
155
|
+
before/after engine, `compareObservations(before, after, config?)`:
|
|
156
|
+
validates both source artifacts, evaluates comparability *before* any
|
|
157
|
+
rendered difference is calculated, calls `deriveLayoutRelationships` once
|
|
158
|
+
per side with the same tolerance, and derives target/page differences and
|
|
159
|
+
relationship changes. `src/artifacts/comparisonArtifactWriter.ts` persists
|
|
160
|
+
the result atomically (sibling temp directory, then one rename) as
|
|
161
|
+
`<outputLocation>/<comparisonId>/manifest.json` only - no screenshot is
|
|
162
|
+
copied; the manifest's `before`/`after` references point back to the
|
|
163
|
+
source observations' own `screenshot.path`. `src/application/
|
|
164
|
+
comparisonService.ts` is the one application-layer seam: `compareAndPersist`
|
|
165
|
+
takes two in-memory `ObservationArtifact`s and does exactly one comparison
|
|
166
|
+
plus exactly one persist; `compareAndPersistFromArtifactRoots` is a thin
|
|
167
|
+
wrapper that additionally reads both sides from disk via the existing
|
|
168
|
+
`src/artifacts/artifactReader.ts#readObservationArtifact` reader (itself
|
|
169
|
+
just a `manifest.json` parse plus the same `isValidObservationArtifact`
|
|
170
|
+
structural gate the writer uses).
|
|
171
|
+
|
|
172
|
+
`src/cli.ts` gained one new top-level command, `compare`
|
|
173
|
+
(`--before`/`--after`/`--output`/`--config-file`), implemented with the same
|
|
174
|
+
thin-CLI-boundary discipline as `observe`: `parseCompareArgs` handles only
|
|
175
|
+
argument shape/duplication, an optional `loadComparisonConfigFile` reads and
|
|
176
|
+
validates only file readability/JSON-validity/non-array-object-root (exactly
|
|
177
|
+
like `--targets-file`/`--scroll-scenario-file`), and the command body calls
|
|
178
|
+
`compareAndPersistFromArtifactRoots` exactly once. **The CLI's comparison
|
|
179
|
+
path never launches Chromium** - `src/cli.ts` imports nothing from
|
|
180
|
+
`src/browser/` or `src/artifacts/` (it only reaches persistence and artifact
|
|
181
|
+
reading indirectly, through the application-layer seam above), matching the
|
|
182
|
+
same import-boundary discipline already enforced for `observe`.
|
|
183
|
+
|
|
184
|
+
## Current v0.5 architecture (released/current architecture)
|
|
185
|
+
|
|
186
|
+
v0.5 adds one new downstream layer that consumes `ComparisonArtifact` values
|
|
187
|
+
(plus the source `ObservationArtifact` pair) rather than producing them - no
|
|
188
|
+
new browser lifecycle, target resolver, or comparison engine is added:
|
|
189
|
+
|
|
190
|
+
```text
|
|
191
|
+
ObservationArtifact before + after
|
|
192
|
+
↓
|
|
193
|
+
existing v0.4 comparison/relationship pipeline (unchanged)
|
|
194
|
+
↓
|
|
195
|
+
ComparisonArtifact
|
|
196
|
+
↓ PersistentBaselineContract
|
|
197
|
+
| +
|
|
198
|
+
`------------------------→ PerChangeContract
|
|
199
|
+
↓
|
|
200
|
+
canonical contract evaluation
|
|
201
|
+
(src/domain/frontendContractEvaluation.ts#evaluateFrontendContract)
|
|
202
|
+
↓
|
|
203
|
+
clause results + unexpected changes + overall PASS/FAIL
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
`src/domain/frontendContracts.ts` froze the contract/change-scope type,
|
|
207
|
+
constant, and structural-validator vocabulary (Batch 1); `src/domain/
|
|
208
|
+
frontendContractIdentity.ts` froze deterministic contract/baseline/clause
|
|
209
|
+
identity in the same canonicalize+sha256(+opaque-nonce) style as `src/domain/
|
|
210
|
+
comparisonIdentity.ts`. `src/domain/frontendContractEvaluation.ts#evaluateFrontendContract`
|
|
211
|
+
(Batch 2) is the one canonical pure evaluation entry point: it validates its
|
|
212
|
+
five inputs (before/after `ObservationArtifact`, `ComparisonArtifact`,
|
|
213
|
+
`PersistentBaselineContract`, `PerChangeContract`) are structurally coherent
|
|
214
|
+
and mutually consistent, calculates the active baseline clause set after
|
|
215
|
+
explicit supersession, detects bounded structural conflicts, evaluates every
|
|
216
|
+
active clause via the frozen 15-primitive vocabulary against the existing
|
|
217
|
+
`ComparisonArtifact`/`ObservationArtifact` evidence (never re-deriving
|
|
218
|
+
clipping/relationship/scroll-owner facts), classifies unaccounted-for
|
|
219
|
+
`ComparisonArtifact.differences` entries as `unexpected`, and derives one
|
|
220
|
+
overall `PASS`/`FAIL` verdict. It performs no I/O, launches no browser, and
|
|
221
|
+
persists nothing - `src/domain/frontendContractEvaluation.ts` itself remains
|
|
222
|
+
untouched by the persistence layer below (Batch 3).
|
|
223
|
+
|
|
224
|
+
Batch 3 adds the persistence/application boundary around this frozen domain,
|
|
225
|
+
without redefining it:
|
|
226
|
+
|
|
227
|
+
```text
|
|
228
|
+
ComparisonArtifact (read via new src/artifacts/comparisonArtifactReader.ts)
|
|
229
|
+
+
|
|
230
|
+
PersistentBaselineContract / PerChangeContract
|
|
231
|
+
(read/written via src/artifacts/frontendContractArtifactReader.ts / ...Writer.ts)
|
|
232
|
+
↓
|
|
233
|
+
src/application/frontendContractEvaluationService.ts#evaluateAndPersist
|
|
234
|
+
↓
|
|
235
|
+
evaluateFrontendContract() [called exactly once, unmodified]
|
|
236
|
+
↓
|
|
237
|
+
src/domain/frontendContractEvaluationArtifact.ts
|
|
238
|
+
(minimal additive persisted envelope around the frozen result)
|
|
239
|
+
↓
|
|
240
|
+
src/artifacts/frontendContractEvaluationArtifactWriter.ts
|
|
241
|
+
(atomic write, exactly once on a structurally constructible result)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
`evaluateAndPersistFromArtifactRoots` is the CLI-facing wrapper, reading
|
|
245
|
+
before/after observations through the existing `readObservationArtifact` (no
|
|
246
|
+
second observation reader), the comparison and both contract classes
|
|
247
|
+
through the new readers, then delegating to `evaluateAndPersist` exactly
|
|
248
|
+
once - mirroring `application/comparisonService.ts#compareAndPersistFromArtifactRoots`'s
|
|
249
|
+
own thin-wrapper shape.
|
|
250
|
+
|
|
251
|
+
Batch 4 exposes this through the same thin-CLI boundary already established
|
|
252
|
+
by `observe`/`compare`:
|
|
253
|
+
|
|
254
|
+
```text
|
|
255
|
+
src/cli.ts (argument parsing, JSON-file-shape checks, help/output formatting,
|
|
256
|
+
exit-code selection only)
|
|
257
|
+
↓
|
|
258
|
+
src/application/frontendContractPersistenceService.ts#approveAndPersistBaseline
|
|
259
|
+
src/application/frontendContractPersistenceService.ts#persistPerChangeContract
|
|
260
|
+
src/application/frontendContractEvaluationService.ts#evaluateAndPersistFromArtifactRoots
|
|
261
|
+
↓
|
|
262
|
+
domain validators (isValidPersistentBaselineContract / isValidPerChangeContract)
|
|
263
|
+
+ artifact readers/writers (Batch 3)
|
|
264
|
+
+ evaluateFrontendContract() (Batch 2, unmodified)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Three new top-level commands - `approve-baseline`, `save-change-contract`,
|
|
268
|
+
`evaluate-contract` - each parse only CLI-syntax concerns (duplicate/missing
|
|
269
|
+
flags, JSON-file readability/parseability/object-root shape) and delegate to
|
|
270
|
+
exactly one application-layer call; `src/cli.ts` imports no artifact writer/
|
|
271
|
+
reader module and no browser code, matching the existing `observe`/`compare`
|
|
272
|
+
import-boundary discipline exactly. `approveAndPersistBaseline` adds the one
|
|
273
|
+
new coherence check Batch 3 did not need: verifying a baseline contract's
|
|
274
|
+
frozen `sourceObservation` reference actually matches the supplied
|
|
275
|
+
observation artifact before persisting - explicit approval only, never
|
|
276
|
+
inferred from a `compare` or `evaluate-contract` result. `--enforce` on
|
|
277
|
+
`evaluate-contract` is applied only after evaluation and persistence have
|
|
278
|
+
already completed; it selects the process exit status for an already-final
|
|
279
|
+
`FAIL` result and is never part of any identity or persisted field.
|
|
280
|
+
|
|
111
281
|
## Planned v0.1 architecture constraints
|
|
112
282
|
|
|
113
283
|
v0.1 planning must preserve these approved boundaries without treating module
|
package/docs/CI_CD.md
CHANGED
|
@@ -67,3 +67,79 @@ observations. This was proven on `validation/v0.3-pre-release`
|
|
|
67
67
|
hash-verified candidate tarball (SHA-256
|
|
68
68
|
`da6cbc18a98d005b84a2f12c06f5c01d390006b53c02176a6c9cdc6d9b9d24d0`) - the
|
|
69
69
|
release candidate for `v0.3.0`.
|
|
70
|
+
|
|
71
|
+
## v0.4 readiness coverage
|
|
72
|
+
|
|
73
|
+
The packed-candidate readiness gap that previously existed for the v0.4
|
|
74
|
+
`compare` public command - identified during the v0.4 implementation/
|
|
75
|
+
documentation audit as `V0_4_READINESS_VALIDATION_GAP_EXISTS` - has been
|
|
76
|
+
closed: `scripts/ci/runPackedObservationSmoke.mjs` now also installs the
|
|
77
|
+
candidate tarball, observes a deterministic before/after fixture through
|
|
78
|
+
the installed `observe`, and runs the installed `compare` against both
|
|
79
|
+
resulting artifacts, asserting (through the *installed package's own*
|
|
80
|
+
exported `isValidComparisonArtifact`/`isValidObservationArtifact`, resolved
|
|
81
|
+
from the consumer's own `node_modules` rather than the source checkout):
|
|
82
|
+
comparison artifact kind/schema `1.0.0`, a `comparable` result with a real
|
|
83
|
+
`moved`+`resized` target difference and a real relationship-family
|
|
84
|
+
transition (`does-not-overlap`→`overlaps` and `left-of`→
|
|
85
|
+
`horizontally-overlapping` simultaneously - the same pair the v0.4
|
|
86
|
+
relationship-family matching regression concerns), an explicit
|
|
87
|
+
`--config-file` non-causal dependency-evidence result, an `incomparable`
|
|
88
|
+
result (differing viewport) with the expected blocking comparability
|
|
89
|
+
reason and no fabricated ordinary differences, comparison-manifest path
|
|
90
|
+
privacy (no absolute operational path leakage), retained (never copied)
|
|
91
|
+
before/after screenshot references, a `manifest.json`-only comparison
|
|
92
|
+
directory, and byte-identical source-observation manifests/screenshots
|
|
93
|
+
before and after comparing - on every platform in the matrix, using the
|
|
94
|
+
same single hash-verified candidate tarball as the legacy/semantic/scroll
|
|
95
|
+
observations. This preserves every pre-existing v0.1-v0.3 packed
|
|
96
|
+
observation proof unchanged and adds the v0.4 proof additively, in the same
|
|
97
|
+
script and the same candidate, per the established same-candidate
|
|
98
|
+
architecture (no second `npm pack`, no per-platform rebuild).
|
|
99
|
+
|
|
100
|
+
## v0.5 readiness coverage
|
|
101
|
+
|
|
102
|
+
The packed-readiness coverage gap identified during the v0.5 implementation-
|
|
103
|
+
completeness audit as `V0_5_READINESS_VALIDATION_GAP_EXISTS` - the packed
|
|
104
|
+
candidate smoke exercised `observe`/`compare` but not `approve-baseline`/
|
|
105
|
+
`save-change-contract`/`evaluate-contract` - has been **corrected locally**:
|
|
106
|
+
`scripts/ci/runPackedObservationSmoke.mjs` now also installs the same
|
|
107
|
+
candidate tarball, observes a deterministic navigation/workspace/rail
|
|
108
|
+
fixture through the installed `observe`, and drives the complete installed
|
|
109
|
+
`approve-baseline` → `save-change-contract` → `observe` (candidate) →
|
|
110
|
+
`compare` → `evaluate-contract` sequence, proving both a fully successful
|
|
111
|
+
contract change (overall `PASS`, `--enforce` still exits `0`) and the
|
|
112
|
+
milestone-signature failure (a locally successful requested/expected-
|
|
113
|
+
dependent change alongside a real protected right-rail regression and a
|
|
114
|
+
real preserved navigation-clipping regression, overall `FAIL`) - including
|
|
115
|
+
`--enforce` producing a nonzero exit for the identical `FAIL` evidence
|
|
116
|
+
(`evaluationRequestId` and `clauseResults` unchanged), full source
|
|
117
|
+
observation/comparison/contract-artifact immutability, no copied
|
|
118
|
+
screenshots, and no repository-root artifact leakage. All product behavior
|
|
119
|
+
under test is invoked through the installed tarball's own executable, never
|
|
120
|
+
imported from the source checkout. This addition is exercised in every
|
|
121
|
+
existing matrix lane (`windows-latest`, `ubuntu-latest`, `macos-latest`)
|
|
122
|
+
because all three already invoke this same script against the same
|
|
123
|
+
candidate tarball - no workflow YAML change was required.
|
|
124
|
+
|
|
125
|
+
This correction has since been proven cross-platform on the validation
|
|
126
|
+
branch `validation/v0.5-pre-release`, first tested at commit
|
|
127
|
+
`90255a9175503664f3e65d4114ee205176b7040a`: GitHub Actions run
|
|
128
|
+
[`31727856546`](https://github.com/dailephd/my-frontend-observer/actions/runs/31727856546)
|
|
129
|
+
passed completely - the candidate job (Linux, Node 24) and all three
|
|
130
|
+
`matrix-smoke` lanes (`windows-latest` Node v24.18.1, `ubuntu-latest` Node
|
|
131
|
+
v24.19.0, `macos-latest` Node v24.18.0) - using one shared candidate
|
|
132
|
+
tarball, `my-frontend-observer-0.4.0.tgz`, SHA-256
|
|
133
|
+
`9533a53e475614cd80a29dfa8b0f85e533e3ad736596579f559479e90e78941a`,
|
|
134
|
+
independently hash-verified by each of the three matrix lanes before any of
|
|
135
|
+
them ran the smoke (no lane built its own tarball). Every lane's
|
|
136
|
+
`smoke-summary.json` reported byte-identical v0.5 evidence: the installed
|
|
137
|
+
candidate's `approve-baseline`/`save-change-contract`/`evaluate-contract`
|
|
138
|
+
`--help` all present, a real successful contract change (overall `PASS`,
|
|
139
|
+
`--enforce` exits `0`), and the real milestone-signature failure (requested/
|
|
140
|
+
expected-dependent `pass`, protected/preserved `fail`, overall `FAIL`,
|
|
141
|
+
identical `evaluationRequestId`/`clauseResults` with `--enforce` exiting `1`
|
|
142
|
+
and without it exiting `0`), plus source-artifact immutability, no copied
|
|
143
|
+
screenshot, no path leakage, and no repository-root leakage - alongside
|
|
144
|
+
every pre-existing v0.1-v0.4 packed observe/compare/scroll assertion, still
|
|
145
|
+
passing unchanged on all three platforms.
|
package/docs/COMMANDS.md
CHANGED
|
@@ -225,6 +225,305 @@ persists honestly - typically as `partial` - carrying the same
|
|
|
225
225
|
`target-missing`/`target-ambiguous`/`browser-evidence-unavailable` diagnostic
|
|
226
226
|
that any other unresolved configured target would produce.
|
|
227
227
|
|
|
228
|
+
## `compare`
|
|
229
|
+
|
|
230
|
+
**Current status: shipped as part of the published `my-frontend-observer@0.4.0`
|
|
231
|
+
package.** Comparison schema is `1.0.0`, independent of and never reused for
|
|
232
|
+
the observation schema (`1.2.0`).
|
|
233
|
+
|
|
234
|
+
`my-frontend-observer compare` (or `node dist/cli.js compare` from a source
|
|
235
|
+
checkout) reads two already-persisted observation artifacts and derives
|
|
236
|
+
before/after evidence purely from their existing content:
|
|
237
|
+
|
|
238
|
+
```text
|
|
239
|
+
my-frontend-observer compare --before <observation-artifact-root> --after <observation-artifact-root> --output <directory> [options]
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Required:
|
|
243
|
+
|
|
244
|
+
- `--before <path>` — root directory of the "before" persisted observation
|
|
245
|
+
artifact (the directory containing its `manifest.json`, as produced by
|
|
246
|
+
`observe`).
|
|
247
|
+
- `--after <path>` — root directory of the "after" persisted observation
|
|
248
|
+
artifact.
|
|
249
|
+
- `--output <directory>` — portable, relative output location for the
|
|
250
|
+
comparison artifact (same contract as `observe --output`).
|
|
251
|
+
|
|
252
|
+
Options:
|
|
253
|
+
|
|
254
|
+
- `--config-file <json-file>` — loads a comparison configuration directly
|
|
255
|
+
(no wrapper field): `{ "geometryTolerancePx": <0-10>,
|
|
256
|
+
"expectedDependencies": [...] }`. Without it, `geometryTolerancePx`
|
|
257
|
+
defaults to `0.5` CSS px with no declared dependencies. As with
|
|
258
|
+
`--targets-file`/`--scroll-scenario-file`, `--config-file` only validates
|
|
259
|
+
file readability, JSON validity, and a non-array object root; every
|
|
260
|
+
semantic rule (tolerance bounds, dependency property/direction
|
|
261
|
+
vocabulary, dependency source marker) is enforced by the same domain
|
|
262
|
+
validator the comparison engine itself uses.
|
|
263
|
+
- `--help` — show `compare` usage.
|
|
264
|
+
|
|
265
|
+
**Comparison never launches a browser.** It reads two manifests through the
|
|
266
|
+
existing observation-artifact reader, runs the pure comparison engine, and
|
|
267
|
+
persists a portable `manifest.json` — no navigation, no target
|
|
268
|
+
re-resolution, no Chromium process.
|
|
269
|
+
|
|
270
|
+
On success the command prints exactly:
|
|
271
|
+
|
|
272
|
+
```text
|
|
273
|
+
Comparison: <comparison-id>
|
|
274
|
+
State: <comparable|comparable-with-warnings|incomparable>
|
|
275
|
+
Artifact: <comparison-artifact-root>
|
|
276
|
+
Differences: <count>
|
|
277
|
+
Relationship changes: <count>
|
|
278
|
+
Diagnostics: <count>
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
and exits `0` — **including when `State` is `incomparable`**: comparison
|
|
282
|
+
determining that two observations should not be treated as equivalent
|
|
283
|
+
frontend states is itself a successful outcome, not a failure. The command
|
|
284
|
+
exits nonzero only for invalid CLI syntax, an unreadable/malformed/
|
|
285
|
+
structurally-invalid source artifact, invalid comparison configuration, or
|
|
286
|
+
a failed artifact write.
|
|
287
|
+
|
|
288
|
+
### Comparability
|
|
289
|
+
|
|
290
|
+
Before any rendered difference is calculated, the engine evaluates whether
|
|
291
|
+
the two observations are comparable at all:
|
|
292
|
+
|
|
293
|
+
- **Hard incompatibilities** (force `incomparable`): different logical page
|
|
294
|
+
URL, different viewport, different browser engine, or a mismatched scroll
|
|
295
|
+
scenario configuration (no scenario vs. a scenario, or two different
|
|
296
|
+
scenario configurations).
|
|
297
|
+
- **Warnings** (still `comparable-with-warnings`, comparison proceeds):
|
|
298
|
+
different producer package version, different browser version, or a
|
|
299
|
+
changed/added/removed configured target.
|
|
300
|
+
- **Unassessed dimensions** the observer does not yet model (theme,
|
|
301
|
+
authenticated state, application state) are always recorded, never
|
|
302
|
+
silently claimed identical.
|
|
303
|
+
|
|
304
|
+
An `incomparable` result still persists a structurally valid
|
|
305
|
+
`ComparisonArtifact`: the comparability reasons are recorded, and ordinary
|
|
306
|
+
rendered differences/relationship changes stay empty rather than fabricated.
|
|
307
|
+
|
|
308
|
+
### Difference and relationship evidence
|
|
309
|
+
|
|
310
|
+
For a `comparable`/`comparable-with-warnings` result, the manifest's
|
|
311
|
+
`differences` and `relationshipChanges` arrays carry structured before/
|
|
312
|
+
after evidence: appeared/disappeared targets (only for a stable target name
|
|
313
|
+
configured on both sides — a target added/removed from configuration is
|
|
314
|
+
recorded separately as a `configurationChanges` entry, never fabricated as
|
|
315
|
+
appeared/disappeared), moved/resized targets, visibility changes, clipping
|
|
316
|
+
changes, actual dimensional overflow changes, DOM containment changes,
|
|
317
|
+
page-size changes, scroll-owner changes, and layout-relationship
|
|
318
|
+
transitions (e.g. `does-not-overlap` → `overlaps`, or
|
|
319
|
+
`document-width-fits-viewport` → `document-width-exceeds-viewport`) reused
|
|
320
|
+
verbatim from the same canonical relationship engine `observe` output feeds
|
|
321
|
+
Batch 2's `deriveLayoutRelationships`.
|
|
322
|
+
|
|
323
|
+
### Explicit dependency evidence (non-causal)
|
|
324
|
+
|
|
325
|
+
`--config-file`'s `expectedDependencies` lets you declare an expected
|
|
326
|
+
layout relationship such as "`navigation.width` decreases →
|
|
327
|
+
`workspace.width` increases" using only the frozen `x`/`y`/`width`/`height`
|
|
328
|
+
property vocabulary and `increase`/`decrease`/`change`/`unchanged`
|
|
329
|
+
direction vocabulary. Each declaration is evaluated independently against
|
|
330
|
+
the two observations and persists exactly one outcome: `consistent`,
|
|
331
|
+
`not-observed`, `contradictory-to-declaration`, or `unavailable`. **The
|
|
332
|
+
observer never infers a dependency from co-change, and never emits a
|
|
333
|
+
causal claim, a PASS/FAIL verdict, or a change-contract decision** — v0.4
|
|
334
|
+
produces comparison evidence; whether that evidence satisfies some
|
|
335
|
+
contract is v0.5+ scope.
|
|
336
|
+
|
|
337
|
+
### Path privacy
|
|
338
|
+
|
|
339
|
+
`--before`, `--after`, `--config-file`, and `--output` are operational
|
|
340
|
+
filesystem input only. None of them affect `comparisonRequestId`, and none
|
|
341
|
+
of them are written into the persisted manifest — the manifest instead
|
|
342
|
+
retains logical source references (`observationId`, `requestId`,
|
|
343
|
+
`producer`, `observationSchemaVersion`, and the source `screenshot.path`).
|
|
344
|
+
Two semantically identical observation/config pairs read from different
|
|
345
|
+
filesystem locations produce the same `comparisonRequestId`; each execution
|
|
346
|
+
still gets a fresh `comparisonId`.
|
|
347
|
+
|
|
348
|
+
### Source observations remain immutable
|
|
349
|
+
|
|
350
|
+
Comparison is read-only with respect to its inputs: it never modifies
|
|
351
|
+
either source observation's `manifest.json` or `screenshot.png`, and it
|
|
352
|
+
never copies screenshot bytes into the comparison directory — the
|
|
353
|
+
comparison artifact directory contains `manifest.json` only.
|
|
354
|
+
|
|
355
|
+
Example:
|
|
356
|
+
|
|
357
|
+
```powershell
|
|
358
|
+
node dist/cli.js compare `
|
|
359
|
+
--before observations/<before-id> `
|
|
360
|
+
--after observations/<after-id> `
|
|
361
|
+
--output comparisons `
|
|
362
|
+
--config-file .\comparison-config.json
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## `approve-baseline`
|
|
366
|
+
|
|
367
|
+
**Current status: shipped as part of the published `my-frontend-observer@0.5.0`
|
|
368
|
+
package.** Frontend contract schema is `1.0.0`, independent of the
|
|
369
|
+
observation (`1.2.0`) and comparison (`1.0.0`) schemas.
|
|
370
|
+
|
|
371
|
+
`my-frontend-observer approve-baseline` is the *only* baseline-approval
|
|
372
|
+
operation in the observer — approval is never inferred from a successful
|
|
373
|
+
comparison or evaluation, and no command automatically supersedes or selects
|
|
374
|
+
a baseline. It explicitly approves and persists one already-authored
|
|
375
|
+
`PersistentBaselineContract` against the observation it claims to approve:
|
|
376
|
+
|
|
377
|
+
```text
|
|
378
|
+
my-frontend-observer approve-baseline --observation <observation-artifact-root> --contract-file <json-file> --output <directory>
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
Required:
|
|
382
|
+
|
|
383
|
+
- `--observation <path>` — root directory of the persisted observation
|
|
384
|
+
artifact this baseline claims to approve (read through the existing
|
|
385
|
+
observation-artifact reader).
|
|
386
|
+
- `--contract-file <json-file>` — local JSON file containing one raw
|
|
387
|
+
`PersistentBaselineContract` (no wrapper field). As with `--config-file`,
|
|
388
|
+
only file readability/JSON-validity/non-array-object-root is checked here;
|
|
389
|
+
every structural rule (artifact kind, schema version, clause shape) is
|
|
390
|
+
enforced by the existing frozen domain validator.
|
|
391
|
+
- `--output <directory>` — portable, relative output location for the
|
|
392
|
+
baseline artifact.
|
|
393
|
+
|
|
394
|
+
Before persisting, the application layer verifies the contract's frozen
|
|
395
|
+
`sourceObservation` reference (`observationId`, `requestId`, `producer`,
|
|
396
|
+
`observationSchemaVersion`) actually matches the supplied observation
|
|
397
|
+
artifact's stable identity — approving a baseline against an unrelated
|
|
398
|
+
observation is rejected, even if both artifacts are individually valid. Any
|
|
399
|
+
`supersedesBaselineId` already authored in the contract is preserved exactly
|
|
400
|
+
as supplied; this command never discovers a prior baseline, infers
|
|
401
|
+
supersession, or deletes anything.
|
|
402
|
+
|
|
403
|
+
On success the command prints exactly:
|
|
404
|
+
|
|
405
|
+
```text
|
|
406
|
+
Baseline: <baseline-id>
|
|
407
|
+
State: approved
|
|
408
|
+
Artifact: <baseline-artifact-root>
|
|
409
|
+
Clauses: <count>
|
|
410
|
+
Supersedes: <baseline-id|none>
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
and exits `0`. It exits nonzero for invalid CLI syntax, an unreadable/
|
|
414
|
+
malformed/structurally-invalid contract file, a `PerChangeContract` passed
|
|
415
|
+
where a baseline is expected, a source-observation mismatch, an existing
|
|
416
|
+
artifact collision (baseline identities are never overwritten), or a failed
|
|
417
|
+
artifact write.
|
|
418
|
+
|
|
419
|
+
## `save-change-contract`
|
|
420
|
+
|
|
421
|
+
**Current status: shipped as part of the published `my-frontend-observer@0.5.0`
|
|
422
|
+
package.**
|
|
423
|
+
|
|
424
|
+
`my-frontend-observer save-change-contract` validates and persists one
|
|
425
|
+
already-authored `PerChangeContract` so it can later be evaluated — this is
|
|
426
|
+
persistence only, never approval:
|
|
427
|
+
|
|
428
|
+
```text
|
|
429
|
+
my-frontend-observer save-change-contract --contract-file <json-file> --output <directory>
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
Required:
|
|
433
|
+
|
|
434
|
+
- `--contract-file <json-file>` — local JSON file containing one raw
|
|
435
|
+
`PerChangeContract` (no wrapper field).
|
|
436
|
+
- `--output <directory>` — portable, relative output location for the
|
|
437
|
+
change-contract artifact.
|
|
438
|
+
|
|
439
|
+
Domain/application validation rejects a `PersistentBaselineContract` passed
|
|
440
|
+
here, malformed clauses, an unsupported authored category, an authored
|
|
441
|
+
`category: "unexpected"` (the derived-only fifth classification can never be
|
|
442
|
+
authored as a permission), and invalid tolerance/mode fields — none of this
|
|
443
|
+
is duplicated in CLI code. Any `supersedesBaselineClauseIds` already
|
|
444
|
+
authored on a clause is preserved exactly; resolving those references
|
|
445
|
+
against a particular baseline remains `evaluate-contract`'s responsibility.
|
|
446
|
+
|
|
447
|
+
On success the command prints exactly:
|
|
448
|
+
|
|
449
|
+
```text
|
|
450
|
+
Change contract: <contract-id>
|
|
451
|
+
Artifact: <contract-artifact-root>
|
|
452
|
+
Clauses: <count>
|
|
453
|
+
Supersedes baseline clauses: <count>
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
and exits `0`. It exits nonzero for invalid CLI syntax, an unreadable/
|
|
457
|
+
malformed/structurally-invalid contract file, a persistent baseline contract
|
|
458
|
+
passed here, an existing artifact collision, or a failed artifact write.
|
|
459
|
+
|
|
460
|
+
## `evaluate-contract`
|
|
461
|
+
|
|
462
|
+
**Current status: shipped as part of the published `my-frontend-observer@0.5.0`
|
|
463
|
+
package.** Frontend contract evaluation artifact schema is `1.0.0`, its own
|
|
464
|
+
independent family.
|
|
465
|
+
|
|
466
|
+
`my-frontend-observer evaluate-contract` executes the canonical Batch 2
|
|
467
|
+
evaluator against already-persisted evidence/contracts and persists the
|
|
468
|
+
result:
|
|
469
|
+
|
|
470
|
+
```text
|
|
471
|
+
my-frontend-observer evaluate-contract --before <observation-artifact-root> --after <observation-artifact-root> --comparison <comparison-artifact-root> --baseline <baseline-contract-artifact-root> --change <per-change-contract-artifact-root> --output <directory> [--enforce]
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
Required:
|
|
475
|
+
|
|
476
|
+
- `--before <path>` / `--after <path>` — root directories of the persisted
|
|
477
|
+
before/after observation artifacts.
|
|
478
|
+
- `--comparison <path>` — root directory of the already-persisted comparison
|
|
479
|
+
artifact for that before/after pair.
|
|
480
|
+
- `--baseline <path>` — root directory of the already-approved baseline
|
|
481
|
+
contract artifact.
|
|
482
|
+
- `--change <path>` — root directory of the already-persisted per-change
|
|
483
|
+
contract artifact.
|
|
484
|
+
- `--output <directory>` — portable, relative output location for the
|
|
485
|
+
evaluation artifact.
|
|
486
|
+
|
|
487
|
+
Options:
|
|
488
|
+
|
|
489
|
+
- `--enforce` — makes a `FAIL` verdict produce a nonzero process exit
|
|
490
|
+
status. A `FAIL` evaluation is always persisted and printed identically
|
|
491
|
+
with or without this flag; `--enforce` changes only the process exit code
|
|
492
|
+
— never evaluation identity, contents, or persistence.
|
|
493
|
+
|
|
494
|
+
**`evaluate-contract` never launches a browser, never re-resolves targets,
|
|
495
|
+
and never recomputes comparison or relationship evidence** — it reads the
|
|
496
|
+
already-persisted before/after observations and comparison exactly as given
|
|
497
|
+
(through the existing observation-artifact reader and a new comparison
|
|
498
|
+
reader) and calls the canonical `evaluateFrontendContract` exactly once.
|
|
499
|
+
|
|
500
|
+
A `FAIL` verdict (a found regression or unsatisfied contract clause) is a
|
|
501
|
+
successful, persisted evaluation outcome — not an execution error. On
|
|
502
|
+
success (evaluation constructed and persisted, verdict `PASS`, or verdict
|
|
503
|
+
`FAIL` without `--enforce`), the command prints exactly:
|
|
504
|
+
|
|
505
|
+
```text
|
|
506
|
+
Evaluation: <evaluation-id>
|
|
507
|
+
Verdict: <PASS|FAIL>
|
|
508
|
+
Artifact: <evaluation-artifact-root>
|
|
509
|
+
Clauses: <total-clause-result-count>
|
|
510
|
+
Unexpected: <unexpected-change-count>
|
|
511
|
+
Enforced: <yes|no>
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
and exits `0`; with `--enforce` and verdict `FAIL`, it prints the same
|
|
515
|
+
result and exits nonzero. It exits nonzero and persists nothing for invalid
|
|
516
|
+
CLI syntax or an unreadable/malformed/incoherent source artifact (evaluation
|
|
517
|
+
could not even be constructed) — distinct from a legitimate persisted `FAIL`.
|
|
518
|
+
|
|
519
|
+
The evaluation artifact directory contains `manifest.json` only — no
|
|
520
|
+
screenshot is copied. Operational `--before`/`--after`/`--comparison`/
|
|
521
|
+
`--baseline`/`--change`/`--output` paths never enter the persisted
|
|
522
|
+
`evaluationRequestId` or any other semantic field; two semantically
|
|
523
|
+
identical evaluations invoked from different filesystem locations share the
|
|
524
|
+
same `evaluationRequestId` even though each execution gets a fresh
|
|
525
|
+
`evaluationId`.
|
|
526
|
+
|
|
228
527
|
## Foundation commands
|
|
229
528
|
|
|
230
529
|
- `npm install` — install dependencies (includes the `playwright` runtime
|