@dailephd/my-frontend-observer 0.8.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/CHANGELOG.md +397 -0
- package/LICENSE +21 -0
- package/README.md +255 -0
- package/dist/application/browserCaptureService.d.ts +11 -0
- package/dist/application/browserCaptureService.js +12 -0
- package/dist/application/browserCaptureService.js.map +1 -0
- 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/externalReferencePersistenceService.d.ts +75 -0
- package/dist/application/externalReferencePersistenceService.js +182 -0
- package/dist/application/externalReferencePersistenceService.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/application/observationPersistence.d.ts +59 -0
- package/dist/application/observationPersistence.js +79 -0
- package/dist/application/observationPersistence.js.map +1 -0
- package/dist/application/projectCheckService.d.ts +3 -0
- package/dist/application/projectCheckService.js +169 -0
- package/dist/application/projectCheckService.js.map +1 -0
- package/dist/application/projectWorkflowService.d.ts +46 -0
- package/dist/application/projectWorkflowService.js +92 -0
- package/dist/application/projectWorkflowService.js.map +1 -0
- package/dist/application/referenceFidelityEvaluationService.d.ts +28 -0
- package/dist/application/referenceFidelityEvaluationService.js +45 -0
- package/dist/application/referenceFidelityEvaluationService.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/artifactWriter.d.ts +25 -0
- package/dist/artifacts/artifactWriter.js +68 -0
- package/dist/artifacts/artifactWriter.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/externalReferenceArtifactReader.d.ts +18 -0
- package/dist/artifacts/externalReferenceArtifactReader.js +35 -0
- package/dist/artifacts/externalReferenceArtifactReader.js.map +1 -0
- package/dist/artifacts/externalReferenceArtifactWriter.d.ts +44 -0
- package/dist/artifacts/externalReferenceArtifactWriter.js +77 -0
- package/dist/artifacts/externalReferenceArtifactWriter.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/artifacts/types.d.ts +17 -0
- package/dist/artifacts/types.js +2 -0
- package/dist/artifacts/types.js.map +1 -0
- package/dist/browser/chromiumAdapter.d.ts +21 -0
- package/dist/browser/chromiumAdapter.js +204 -0
- package/dist/browser/chromiumAdapter.js.map +1 -0
- package/dist/browser/evidenceCapture.d.ts +62 -0
- package/dist/browser/evidenceCapture.js +500 -0
- package/dist/browser/evidenceCapture.js.map +1 -0
- package/dist/browser/scrollCapture.d.ts +32 -0
- package/dist/browser/scrollCapture.js +163 -0
- package/dist/browser/scrollCapture.js.map +1 -0
- package/dist/browser/types.d.ts +24 -0
- package/dist/browser/types.js +2 -0
- package/dist/browser/types.js.map +1 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +2411 -0
- package/dist/cli.js.map +1 -0
- package/dist/domain/boundedAgentContext.d.ts +226 -0
- package/dist/domain/boundedAgentContext.js +355 -0
- package/dist/domain/boundedAgentContext.js.map +1 -0
- package/dist/domain/boundedAgentContextCorrelation.d.ts +74 -0
- package/dist/domain/boundedAgentContextCorrelation.js +441 -0
- package/dist/domain/boundedAgentContextCorrelation.js.map +1 -0
- package/dist/domain/boundedAgentContextIdentity.d.ts +26 -0
- package/dist/domain/boundedAgentContextIdentity.js +69 -0
- package/dist/domain/boundedAgentContextIdentity.js.map +1 -0
- package/dist/domain/boundedAgentContextProjection.d.ts +71 -0
- package/dist/domain/boundedAgentContextProjection.js +477 -0
- package/dist/domain/boundedAgentContextProjection.js.map +1 -0
- package/dist/domain/comparison.d.ts +220 -0
- package/dist/domain/comparison.js +350 -0
- package/dist/domain/comparison.js.map +1 -0
- package/dist/domain/comparisonEngine.d.ts +76 -0
- package/dist/domain/comparisonEngine.js +734 -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/completion.d.ts +30 -0
- package/dist/domain/completion.js +22 -0
- package/dist/domain/completion.js.map +1 -0
- package/dist/domain/diagnostics.d.ts +17 -0
- package/dist/domain/diagnostics.js +77 -0
- package/dist/domain/diagnostics.js.map +1 -0
- package/dist/domain/evidence.d.ts +29 -0
- package/dist/domain/evidence.js +59 -0
- package/dist/domain/evidence.js.map +1 -0
- package/dist/domain/explicitState.d.ts +40 -0
- package/dist/domain/explicitState.js +54 -0
- package/dist/domain/explicitState.js.map +1 -0
- package/dist/domain/externalReference.d.ts +158 -0
- package/dist/domain/externalReference.js +167 -0
- package/dist/domain/externalReference.js.map +1 -0
- package/dist/domain/externalReferenceApplicability.d.ts +43 -0
- package/dist/domain/externalReferenceApplicability.js +52 -0
- package/dist/domain/externalReferenceApplicability.js.map +1 -0
- package/dist/domain/externalReferenceCompatibility.d.ts +40 -0
- package/dist/domain/externalReferenceCompatibility.js +48 -0
- package/dist/domain/externalReferenceCompatibility.js.map +1 -0
- package/dist/domain/externalReferenceFidelity.d.ts +171 -0
- package/dist/domain/externalReferenceFidelity.js +419 -0
- package/dist/domain/externalReferenceFidelity.js.map +1 -0
- package/dist/domain/externalReferenceIdentity.d.ts +38 -0
- package/dist/domain/externalReferenceIdentity.js +70 -0
- package/dist/domain/externalReferenceIdentity.js.map +1 -0
- package/dist/domain/externalReferenceImage.d.ts +35 -0
- package/dist/domain/externalReferenceImage.js +160 -0
- package/dist/domain/externalReferenceImage.js.map +1 -0
- package/dist/domain/externalReferenceRegionRelationships.d.ts +63 -0
- package/dist/domain/externalReferenceRegionRelationships.js +98 -0
- package/dist/domain/externalReferenceRegionRelationships.js.map +1 -0
- package/dist/domain/externalReferenceRegions.d.ts +65 -0
- package/dist/domain/externalReferenceRegions.js +105 -0
- package/dist/domain/externalReferenceRegions.js.map +1 -0
- package/dist/domain/externalReferenceRequirementIdentity.d.ts +12 -0
- package/dist/domain/externalReferenceRequirementIdentity.js +35 -0
- package/dist/domain/externalReferenceRequirementIdentity.js.map +1 -0
- package/dist/domain/externalReferenceRequirements.d.ts +215 -0
- package/dist/domain/externalReferenceRequirements.js +401 -0
- package/dist/domain/externalReferenceRequirements.js.map +1 -0
- package/dist/domain/externalReferenceRuntimeBinding.d.ts +146 -0
- package/dist/domain/externalReferenceRuntimeBinding.js +183 -0
- package/dist/domain/externalReferenceRuntimeBinding.js.map +1 -0
- 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/identity.d.ts +21 -0
- package/dist/domain/identity.js +47 -0
- package/dist/domain/identity.js.map +1 -0
- package/dist/domain/referenceCorrectionIdentity.d.ts +40 -0
- package/dist/domain/referenceCorrectionIdentity.js +77 -0
- package/dist/domain/referenceCorrectionIdentity.js.map +1 -0
- package/dist/domain/referenceCorrectionWorkflow.d.ts +160 -0
- package/dist/domain/referenceCorrectionWorkflow.js +165 -0
- package/dist/domain/referenceCorrectionWorkflow.js.map +1 -0
- package/dist/domain/referenceFidelityProjection.d.ts +65 -0
- package/dist/domain/referenceFidelityProjection.js +135 -0
- package/dist/domain/referenceFidelityProjection.js.map +1 -0
- package/dist/domain/relationships.d.ts +210 -0
- package/dist/domain/relationships.js +352 -0
- package/dist/domain/relationships.js.map +1 -0
- package/dist/domain/schema.d.ts +269 -0
- package/dist/domain/schema.js +442 -0
- package/dist/domain/schema.js.map +1 -0
- package/dist/domain/scrollEvidence.d.ts +51 -0
- package/dist/domain/scrollEvidence.js +134 -0
- package/dist/domain/scrollEvidence.js.map +1 -0
- package/dist/index.d.ts +114 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/dist/projectWorkflow/aliasCatalog.d.ts +21 -0
- package/dist/projectWorkflow/aliasCatalog.js +67 -0
- package/dist/projectWorkflow/aliasCatalog.js.map +1 -0
- package/dist/projectWorkflow/checkAcceptance.d.ts +25 -0
- package/dist/projectWorkflow/checkAcceptance.js +55 -0
- package/dist/projectWorkflow/checkAcceptance.js.map +1 -0
- package/dist/projectWorkflow/checkResult.d.ts +85 -0
- package/dist/projectWorkflow/checkResult.js +101 -0
- package/dist/projectWorkflow/checkResult.js.map +1 -0
- package/dist/projectWorkflow/projectConfig.d.ts +43 -0
- package/dist/projectWorkflow/projectConfig.js +84 -0
- package/dist/projectWorkflow/projectConfig.js.map +1 -0
- package/dist/projectWorkflow/projectDiscovery.d.ts +9 -0
- package/dist/projectWorkflow/projectDiscovery.js +20 -0
- package/dist/projectWorkflow/projectDiscovery.js.map +1 -0
- package/dist/projectWorkflow/projectPaths.d.ts +8 -0
- package/dist/projectWorkflow/projectPaths.js +23 -0
- package/dist/projectWorkflow/projectPaths.js.map +1 -0
- package/dist/request/paths.d.ts +14 -0
- package/dist/request/paths.js +33 -0
- package/dist/request/paths.js.map +1 -0
- package/dist/request/request.d.ts +111 -0
- package/dist/request/request.js +464 -0
- package/dist/request/request.js.map +1 -0
- package/dist/safety/policy.d.ts +14 -0
- package/dist/safety/policy.js +81 -0
- package/dist/safety/policy.js.map +1 -0
- package/dist/viewer/assets/index-CN_yb9Uf.css +1 -0
- package/dist/viewer/assets/index-D98S1_2d.js +9 -0
- package/dist/viewer/icons/icon-192.png +0 -0
- package/dist/viewer/icons/icon-512.png +0 -0
- package/dist/viewer/index.html +15 -0
- package/dist/viewer/manifest.webmanifest +1 -0
- package/dist/viewer/registerSW.js +1 -0
- package/dist/viewer/sw.js +1 -0
- package/dist/viewer/workbox-9c191d2f.js +1 -0
- package/dist/viewerServer/context.d.ts +48 -0
- package/dist/viewerServer/context.js +60 -0
- package/dist/viewerServer/context.js.map +1 -0
- package/dist/viewerServer/evidence/classify.d.ts +59 -0
- package/dist/viewerServer/evidence/classify.js +124 -0
- package/dist/viewerServer/evidence/classify.js.map +1 -0
- package/dist/viewerServer/evidence/comparisonView.d.ts +28 -0
- package/dist/viewerServer/evidence/comparisonView.js +43 -0
- package/dist/viewerServer/evidence/comparisonView.js.map +1 -0
- package/dist/viewerServer/evidence/contextSourceView.d.ts +25 -0
- package/dist/viewerServer/evidence/contextSourceView.js +20 -0
- package/dist/viewerServer/evidence/contextSourceView.js.map +1 -0
- package/dist/viewerServer/evidence/discovery.d.ts +31 -0
- package/dist/viewerServer/evidence/discovery.js +78 -0
- package/dist/viewerServer/evidence/discovery.js.map +1 -0
- package/dist/viewerServer/evidence/evaluationView.d.ts +32 -0
- package/dist/viewerServer/evidence/evaluationView.js +50 -0
- package/dist/viewerServer/evidence/evaluationView.js.map +1 -0
- package/dist/viewerServer/evidence/handles.d.ts +21 -0
- package/dist/viewerServer/evidence/handles.js +43 -0
- package/dist/viewerServer/evidence/handles.js.map +1 -0
- package/dist/viewerServer/evidence/index.d.ts +41 -0
- package/dist/viewerServer/evidence/index.js +82 -0
- package/dist/viewerServer/evidence/index.js.map +1 -0
- package/dist/viewerServer/evidence/limits.d.ts +27 -0
- package/dist/viewerServer/evidence/limits.js +28 -0
- package/dist/viewerServer/evidence/limits.js.map +1 -0
- package/dist/viewerServer/evidence/linkedEvidence.d.ts +43 -0
- package/dist/viewerServer/evidence/linkedEvidence.js +151 -0
- package/dist/viewerServer/evidence/linkedEvidence.js.map +1 -0
- package/dist/viewerServer/evidence/mediaResolver.d.ts +16 -0
- package/dist/viewerServer/evidence/mediaResolver.js +85 -0
- package/dist/viewerServer/evidence/mediaResolver.js.map +1 -0
- package/dist/viewerServer/evidence/observationView.d.ts +29 -0
- package/dist/viewerServer/evidence/observationView.js +46 -0
- package/dist/viewerServer/evidence/observationView.js.map +1 -0
- package/dist/viewerServer/evidence/pathSafety.d.ts +11 -0
- package/dist/viewerServer/evidence/pathSafety.js +31 -0
- package/dist/viewerServer/evidence/pathSafety.js.map +1 -0
- package/dist/viewerServer/evidence/projection.d.ts +55 -0
- package/dist/viewerServer/evidence/projection.js +152 -0
- package/dist/viewerServer/evidence/projection.js.map +1 -0
- package/dist/viewerServer/evidence/referenceView.d.ts +133 -0
- package/dist/viewerServer/evidence/referenceView.js +169 -0
- package/dist/viewerServer/evidence/referenceView.js.map +1 -0
- package/dist/viewerServer/httpServer.d.ts +27 -0
- package/dist/viewerServer/httpServer.js +381 -0
- package/dist/viewerServer/httpServer.js.map +1 -0
- package/dist/viewerServer/openBrowser.d.ts +7 -0
- package/dist/viewerServer/openBrowser.js +32 -0
- package/dist/viewerServer/openBrowser.js.map +1 -0
- package/dist/viewerServer/port.d.ts +16 -0
- package/dist/viewerServer/port.js +19 -0
- package/dist/viewerServer/port.js.map +1 -0
- package/dist/viewerServer/viewerService.d.ts +62 -0
- package/dist/viewerServer/viewerService.js +88 -0
- package/dist/viewerServer/viewerService.js.map +1 -0
- package/docs/ARCHITECTURE.md +1286 -0
- package/docs/CI_CD.md +250 -0
- package/docs/COMMANDS.md +972 -0
- package/docs/CONTRACTS.md +1856 -0
- package/docs/CURRENT_STATE.md +1049 -0
- package/docs/DEVELOPMENT.md +202 -0
- package/docs/DOCUMENTATION_PRESERVATION_POLICY.md +50 -0
- package/docs/PROJECT_DESCRIPTION.md +2221 -0
- package/docs/PROJECT_MILESTONES.md +2526 -0
- package/docs/PROJECT_OVERVIEW.md +150 -0
- package/docs/QUICKSTART.md +83 -0
- package/docs/RELEASE.md +27 -0
- package/docs/ROADMAP.md +641 -0
- package/docs/SECURITY.md +218 -0
- package/docs/WORKFLOWS.md +642 -0
- package/docs/plans/v0.8-implementation-plan.md +655 -0
- package/docs/plans/v0.8.1-cli-usability-patch-plan.md +505 -0
- package/docs/reports/v0.7-bounded-fidelity-context-prompt7.md +243 -0
- package/docs/reports/v0.7-implementation-completeness-documentation-reconciliation.md +497 -0
- package/docs/reports/v0.7-pre-release-readiness.md +337 -0
- package/docs/reports/v0.7-reference-binding-prompt5.md +223 -0
- package/docs/reports/v0.7-reference-compatibility-prompt4.md +234 -0
- package/docs/reports/v0.7-reference-correction-workflow-prompt8.md +222 -0
- package/docs/reports/v0.7-reference-fidelity-prompt6.md +216 -0
- package/docs/reports/v0.7-reference-foundation-prompt1.md +151 -0
- package/docs/reports/v0.7-reference-regions-prompt2.md +195 -0
- package/docs/reports/v0.7-reference-requirements-prompt3.md +217 -0
- package/docs/reports/v0.7-release-prep.md +423 -0
- package/docs/reports/v0.8-binding-fidelity-interaction-batch6.md +279 -0
- package/docs/reports/v0.8-bounded-context-correlation-batch7.md +233 -0
- package/docs/reports/v0.8-comparison-contract-inspection-batch4.md +279 -0
- package/docs/reports/v0.8-evidence-index-readers-batch2.md +247 -0
- package/docs/reports/v0.8-implementation-completeness-documentation-reconciliation.md +741 -0
- package/docs/reports/v0.8-integrated-viewer-acceptance-batch8.md +128 -0
- package/docs/reports/v0.8-observation-svg-inspection-batch3.md +223 -0
- package/docs/reports/v0.8-prerelease-readiness-cross-platform-security-code-rot.md +687 -0
- package/docs/reports/v0.8-reference-candidate-inspection-batch5.md +232 -0
- package/docs/reports/v0.8-viewer-runtime-pwa-batch1.md +278 -0
- package/docs/reports/v0.8.1-check-orchestration-prompt2.md +69 -0
- package/docs/reports/v0.8.1-implementation-completeness-documentation-reconciliation.md +114 -0
- package/docs/reports/v0.8.1-prerelease-readiness-cross-platform-security-code-rot.md +170 -0
- package/docs/reports/v0.8.1-project-workflow-foundation-prompt1.md +66 -0
- package/package.json +58 -0
|
@@ -0,0 +1,1856 @@
|
|
|
1
|
+
# Contracts
|
|
2
|
+
|
|
3
|
+
## Current contracts
|
|
4
|
+
|
|
5
|
+
The observation artifact contract is published in the current
|
|
6
|
+
`my-frontend-observer@0.7.0` package and proven both from the source checkout
|
|
7
|
+
and from the packed npm tarball, on Windows, Linux, and macOS. The observation
|
|
8
|
+
schema is `1.2.0` (see "v0.2 target contract" and "v0.3 scroll scenario
|
|
9
|
+
contract" below):
|
|
10
|
+
|
|
11
|
+
- artifact kind `my-frontend-observer/observation`, schema version `1.2.0`
|
|
12
|
+
(independent of the package version);
|
|
13
|
+
- one artifact root per observation, `<outputLocation>/<observationId>/`,
|
|
14
|
+
containing exactly `manifest.json` (the full `ObservationArtifact`, with
|
|
15
|
+
page/target evidence embedded inline) and `screenshot.png` - there is no
|
|
16
|
+
separate `evidence.json`;
|
|
17
|
+
- `manifest.json` is written last, after `screenshot.png`, via one atomic
|
|
18
|
+
directory rename, so a consumer never observes a partially-written
|
|
19
|
+
artifact; a filesystem failure anywhere in that sequence reports the
|
|
20
|
+
`artifact-write-failure` diagnostic and leaves no completed artifact;
|
|
21
|
+
- internal artifact references (e.g. `screenshot.png`) are relative to the
|
|
22
|
+
artifact root, never an absolute machine path; the observation's logical
|
|
23
|
+
identity is its `observationId`, not its filesystem location;
|
|
24
|
+
- evidence states `available`, `unavailable`, `not-applicable`, `partial`;
|
|
25
|
+
evidence sources `browser`, `computed-browser`, `derived`;
|
|
26
|
+
- a stable diagnostic vocabulary (`src/domain/diagnostics.ts`) and completion
|
|
27
|
+
states `complete`, `partial`, `warning`, `invalid-request`, `fatal`
|
|
28
|
+
(`src/domain/completion.ts`);
|
|
29
|
+
- observation/request identity, producer/package identity, and browser
|
|
30
|
+
provenance are all present in every persisted manifest.
|
|
31
|
+
|
|
32
|
+
This contract is implemented and published; no public programmatic-API
|
|
33
|
+
compatibility promise has been made for the observation engine itself. v0.6
|
|
34
|
+
additionally publishes the bounded-agent-context/correlation programmatic surface
|
|
35
|
+
described later in this document.
|
|
36
|
+
|
|
37
|
+
## v0.2 target contract (shipped as part of this release)
|
|
38
|
+
|
|
39
|
+
v0.2 introduces a canonical target-configuration model: each configured target has a stable
|
|
40
|
+
observer-level `name` plus an ordered array of bounded `locators`
|
|
41
|
+
(`role`, `id`, `data-attribute`, `semantic-element`, `css`, `text`). This
|
|
42
|
+
identity is distinct from both the browser locator definition that resolves
|
|
43
|
+
it and any source-code identity. The legacy `{name, selector}` shape remains
|
|
44
|
+
accepted and normalizes to a one-item `css` locator, so every published
|
|
45
|
+
`0.1.0` CLI invocation continues to work unchanged. Locator precedence is the
|
|
46
|
+
configured array order; resolution stops on the first unique match, on any
|
|
47
|
+
ambiguous match (never falling through to a later locator), or on an
|
|
48
|
+
unevaluable locator - never silently. All six frozen locator kinds are now
|
|
49
|
+
resolved against a real Chromium page (`role` via Playwright's accessibility-
|
|
50
|
+
role/name locator with exact name matching, `id`/`data-attribute` via exact
|
|
51
|
+
CSS attribute-equals matching that never reinterprets the configured value as
|
|
52
|
+
selector syntax, `semantic-element` via the frozen tag set, `css` via the
|
|
53
|
+
existing v0.1 behavior, `text` via exact-text matching only); every kind
|
|
54
|
+
converges on the same measurement path, so locator strategy never changes the
|
|
55
|
+
resulting target evidence shape.
|
|
56
|
+
|
|
57
|
+
Each resolved target's evidence record additionally carries three bounded
|
|
58
|
+
fields: `semanticState` (a first family of `disabled`/`expanded`/
|
|
59
|
+
`checked`/`selected`/`pressed`/`current` values read from the element's own
|
|
60
|
+
native form-control properties and explicit `aria-*` attributes - a key is
|
|
61
|
+
present only when the browser exposes that state as applicable to this
|
|
62
|
+
element, so an explicit `false` is always distinguishable from "not
|
|
63
|
+
applicable"; `not-applicable` when no supported state applies at all);
|
|
64
|
+
`landmark` (derived only from the already-captured browser-exposed
|
|
65
|
+
role - never from locator kind or HTML tag - against the standard landmark
|
|
66
|
+
role set `banner`/`navigation`/`main`/`complementary`/`contentinfo`/`form`/
|
|
67
|
+
`region`/`search`); and `containment` (bounded DOM containment checked only
|
|
68
|
+
among the other explicitly configured targets in the same observation, in
|
|
69
|
+
configured order, never a layout/relationship graph - `available` when every
|
|
70
|
+
other configured target was itself resolved and checked, `partial` when one
|
|
71
|
+
or more could not be, `unavailable` when the target itself never resolved).
|
|
72
|
+
Stable observer target identity is proven, not just declared: the same
|
|
73
|
+
target configuration produces the same `requestId` across repeated
|
|
74
|
+
observations (with a fresh `observationId` each time); changing a target's
|
|
75
|
+
locator strategy while keeping its stable name changes `requestId` but not
|
|
76
|
+
the `targetEvidence` key; and actual runtime disappearance of a
|
|
77
|
+
still-configured target changes only its resolution status, never the
|
|
78
|
+
`requestId`.
|
|
79
|
+
|
|
80
|
+
The full canonical semantic target model above is reachable through the
|
|
81
|
+
real public CLI: `my-frontend-observer observe --targets-file <json-file>`
|
|
82
|
+
supplies the structured `{ "targets": [...] }` collection (see
|
|
83
|
+
`docs/COMMANDS.md` "Structured semantic targets") as an alternative to the
|
|
84
|
+
existing `--target id=css-selector` shorthand - the two are mutually
|
|
85
|
+
exclusive per invocation, and both converge on the same
|
|
86
|
+
`normalizeRequest()`/browser-resolver/artifact path, so a semantic
|
|
87
|
+
observation produces exactly the same `manifest.json` shape as a
|
|
88
|
+
CSS-shorthand one. Schema `1.1.0` was the v0.2 published artifact schema;
|
|
89
|
+
schema `1.2.0` has been emitted since v0.3 and remains the observation schema
|
|
90
|
+
in the current published v0.7.0 package, for both target-input modes
|
|
91
|
+
(target semantics are unchanged from v0.2 - see the v0.3 scroll scenario
|
|
92
|
+
contract below for what schema `1.2.0` actually adds). `--targets-file`'s
|
|
93
|
+
local input path is never part of the persisted request identity or
|
|
94
|
+
artifact.
|
|
95
|
+
|
|
96
|
+
## v0.3 scroll scenario contract (shipped as part of this release)
|
|
97
|
+
|
|
98
|
+
v0.3 introduces one optional, additive request/evidence concern: a bounded
|
|
99
|
+
runtime scroll scenario, schema `1.2.0`.
|
|
100
|
+
|
|
101
|
+
A normalized request may carry `scrollScenario: { action }` with exactly one
|
|
102
|
+
of two frozen action kinds:
|
|
103
|
+
|
|
104
|
+
- `{ "kind": "window-scroll-by", "deltaX": <int>, "deltaY": <int> }`
|
|
105
|
+
- `{ "kind": "target-scroll-by", "target": "<stable target name>", "deltaX": <int>, "deltaY": <int> }`
|
|
106
|
+
|
|
107
|
+
`deltaX`/`deltaY` are signed integers bounded to `[-20000, 20000]`; at least
|
|
108
|
+
one must be non-zero. `target-scroll-by.target` refers only to an existing
|
|
109
|
+
stable configured target `name` (never a selector) and resolves through the
|
|
110
|
+
same canonical `resolveConfiguredTargets` algorithm every v0.2 locator kind
|
|
111
|
+
already uses - there is no second target-resolution path. A request with no
|
|
112
|
+
scenario normalizes and identifies exactly as it did before v0.3.
|
|
113
|
+
|
|
114
|
+
Execution (both action kinds share one code path): perform the immediate,
|
|
115
|
+
non-smooth scroll (`window.scrollBy`/`element.scrollBy`, `behavior:
|
|
116
|
+
'instant'`) on the already-navigated, already-ready page; wait exactly two
|
|
117
|
+
`requestAnimationFrame` cycles; capture a final runtime snapshot. No second
|
|
118
|
+
browser, page, or navigation is ever created. The resulting scroll position
|
|
119
|
+
is browser-authoritative and may be clamped by document/element boundaries;
|
|
120
|
+
a scenario producing no movement is still a valid, successfully persisted
|
|
121
|
+
observation.
|
|
122
|
+
|
|
123
|
+
The scenario evidence lives entirely inside the existing `manifest.json` as
|
|
124
|
+
one additional optional `scrollScenarioEvidence` field on `ObservationArtifact`
|
|
125
|
+
- there is no separate `scroll.json`/`scenario.json`. It contains:
|
|
126
|
+
|
|
127
|
+
- `initial`/`final`: bounded `ScrollRuntimeSnapshot`s (window `scrollX`/
|
|
128
|
+
`scrollY`; the browser's own scrolling-root/`documentElement`/`body`
|
|
129
|
+
metrics; per-configured-target `scrollTop`/`scrollLeft`/`scrollWidth`/
|
|
130
|
+
`scrollHeight`/`clientWidth`/`clientHeight`, actual overflow, bounding
|
|
131
|
+
rectangle, and viewport relation);
|
|
132
|
+
- `transition`: bounded before/after change evidence (window scroll deltas;
|
|
133
|
+
per-target `scrollTop`/`scrollLeft`/bounding-position/viewport-relation
|
|
134
|
+
changes; `enteredViewport`/`leftViewport`) - never a generic recursive
|
|
135
|
+
diff, and a target is simply omitted when either side's evidence isn't
|
|
136
|
+
itself usable (e.g. it never resolved);
|
|
137
|
+
- `scrollOwner`: one derived `EvidenceField<ScrollOwnerInterpretation>`
|
|
138
|
+
(`document` | `target:<stable-name>` | `none` | `indeterminate`), always
|
|
139
|
+
`source: "derived"` with non-empty `derivedFrom` naming the exact
|
|
140
|
+
contributing scroll-position measurements. Ownership is derived only from
|
|
141
|
+
observed `scrollTop`/`scrollLeft`/`window.scrollX`/`window.scrollY`
|
|
142
|
+
changes - never from bounding-rectangle movement (which moves for every
|
|
143
|
+
configured target whenever the document scrolls), computed overflow,
|
|
144
|
+
`position: fixed`/`sticky`, or DOM hierarchy.
|
|
145
|
+
|
|
146
|
+
Actual dimensional overflow (`scrollWidth > clientWidth` /
|
|
147
|
+
`scrollHeight > clientHeight`) is always reported separately from the
|
|
148
|
+
computed `overflow-x`/`overflow-y` CSS declaration; a declared
|
|
149
|
+
`overflow: auto` container with content that fits produces
|
|
150
|
+
`horizontalOverflow`/`verticalOverflow: false`. Viewport relation
|
|
151
|
+
(`above`/`intersecting`/`below`, `intersectsViewport`, `fullyWithinViewport`)
|
|
152
|
+
is derived only from bounding geometry plus viewport size, relative to the
|
|
153
|
+
browser viewport; a hidden/non-rendered target's viewport relation is
|
|
154
|
+
`not-applicable`, never a fabricated geometry claim - hidden and offscreen
|
|
155
|
+
remain distinct evidence concepts, and the existing `target-hidden`
|
|
156
|
+
diagnostic is unaffected.
|
|
157
|
+
|
|
158
|
+
The ordinary, already-existing `pageEvidence`/`targetEvidence`/
|
|
159
|
+
`screenshot.png` for a scenario observation always describe this same final
|
|
160
|
+
post-action state, never the pre-action state.
|
|
161
|
+
|
|
162
|
+
The scenario request participates in `requestId`; the runtime result
|
|
163
|
+
(actual scroll distance, clamping, or scroll-owner outcome) never does. The
|
|
164
|
+
public entry point is `my-frontend-observer observe --scroll-scenario-file
|
|
165
|
+
<json-file>` (see `docs/COMMANDS.md`); the file supplies the scenario value
|
|
166
|
+
directly, and its local path is operational input only, exactly like
|
|
167
|
+
`--targets-file`'s path - never persisted, never part of request identity.
|
|
168
|
+
|
|
169
|
+
## v0.4 comparison contract (shipped as part of this release)
|
|
170
|
+
|
|
171
|
+
**Current status: shipped as part of the published `my-frontend-observer@0.4.0`
|
|
172
|
+
package and unchanged through the current `0.7.0` release.** Observation
|
|
173
|
+
schema remains `1.2.0`. Comparison is a distinct artifact kind and schema,
|
|
174
|
+
never a bump to the observation schema:
|
|
175
|
+
|
|
176
|
+
- artifact kind: `my-frontend-observer/comparison`;
|
|
177
|
+
- comparison schema: `1.0.0`.
|
|
178
|
+
|
|
179
|
+
**Geometry tolerance**: `ComparisonConfig.geometryTolerancePx`, default
|
|
180
|
+
`0.5` CSS px, bounded `[0, 10]`. Suppresses insignificant subpixel noise
|
|
181
|
+
only - never a design contract, never permission for a change.
|
|
182
|
+
|
|
183
|
+
**Layout relationship graph**: `deriveLayoutRelationships(observation,
|
|
184
|
+
options?)` derives, per observation, a bounded `LayoutRelationshipGraph`
|
|
185
|
+
among configured targets only (≤20 targets, ≤190 unordered pairs):
|
|
186
|
+
horizontal order (`left-of`/`right-of`/`horizontally-overlapping`),
|
|
187
|
+
vertical order (`above`/`below`/`vertically-overlapping`), area overlap
|
|
188
|
+
(`overlaps`/`does-not-overlap`), relative width (`wider-than`/
|
|
189
|
+
`narrower-than`/`equal-width-within-tolerance`), geometric fit
|
|
190
|
+
(`fits-inside`/`does-not-fit-inside` - geometry-only, deliberately distinct
|
|
191
|
+
from DOM containment), vertical sequencing (`follows-vertically`), and one
|
|
192
|
+
page-level relationship (`document-width-fits-viewport`/
|
|
193
|
+
`document-width-exceeds-viewport`). Every relationship carries explicit
|
|
194
|
+
evidence-path provenance back to the source observation. A configured
|
|
195
|
+
target lacking usable geometry is listed as honestly unresolved
|
|
196
|
+
(`not-found`/`ambiguous`/`unavailable`/`hidden`), never fabricated as a
|
|
197
|
+
zero-sized region.
|
|
198
|
+
|
|
199
|
+
**Comparability**: evaluated before any rendered difference, using exactly
|
|
200
|
+
three states (`comparable`/`comparable-with-warnings`/`incomparable`) with
|
|
201
|
+
structured reasons, never a bare boolean. Hard incompatibilities (page URL,
|
|
202
|
+
viewport, browser engine, scroll-scenario configuration mismatch) force
|
|
203
|
+
`incomparable`; producer-version, browser-version, and target-configuration
|
|
204
|
+
differences are warning-only; theme/authenticated-state/application-state
|
|
205
|
+
identity are recorded as `unassessed` dimensions the observer does not yet
|
|
206
|
+
model - never silently claimed identical. An `incomparable` result still
|
|
207
|
+
persists a structurally valid `ComparisonArtifact` with empty rendered
|
|
208
|
+
differences, not a fabricated comparison.
|
|
209
|
+
|
|
210
|
+
**Difference categories**: `appeared`/`disappeared` (only for a stable
|
|
211
|
+
target name configured on both sides, transitioning between a definite
|
|
212
|
+
`not-found` and `matched` resolution status - never for a target merely
|
|
213
|
+
added/removed from configuration, which is its own separate
|
|
214
|
+
`configurationChanges` entry), `moved`/`resized` (tolerance-aware, a target
|
|
215
|
+
may be both), `visibility-changed`, `clipping-changed` (reusing the
|
|
216
|
+
canonical `deriveTargetClipping` helper, never re-derived), `horizontal-
|
|
217
|
+
overflow-changed`/`vertical-overflow-changed` (actual dimensional overflow,
|
|
218
|
+
reusing the existing `deriveOverflowEvidence` helper - never inferred from
|
|
219
|
+
a CSS declaration alone), `containment-changed` (reusing existing v0.2
|
|
220
|
+
`TargetContainment` evidence), `page-size-changed`, `scroll-owner-changed`
|
|
221
|
+
(comparing `scrollScenarioEvidence.scrollOwner` only when scenario
|
|
222
|
+
*configuration* already matched), `relative-position-changed` (a relation
|
|
223
|
+
in the horizontal-order/vertical-order/area-overlap families changed - kept
|
|
224
|
+
distinct from plain absolute target movement) and `relationship-changed`
|
|
225
|
+
(every other relationship-family transition). Relationship changes are
|
|
226
|
+
matched by structural identity (family + subject/related target, or the
|
|
227
|
+
page-level key), never by array position.
|
|
228
|
+
|
|
229
|
+
**Explicit dependency evidence**: `ComparisonConfig.expectedDependencies`
|
|
230
|
+
lets a caller declare an expected relationship between two targets' numeric
|
|
231
|
+
properties (`x`/`y`/`width`/`height`) and directions (`increase`/
|
|
232
|
+
`decrease`/`change`/`unchanged`), always carrying `source:
|
|
233
|
+
"explicit-config"`. The observer never synthesizes a declaration from
|
|
234
|
+
observed co-change. Each declaration evaluates independently to exactly one
|
|
235
|
+
of `consistent`/`not-observed`/`contradictory-to-declaration`/
|
|
236
|
+
`unavailable` - never a causal claim (no `causedBy`/`causalConfidence`/
|
|
237
|
+
`causalScore`/`dependencyStrength`) and never a PASS/FAIL/approval verdict.
|
|
238
|
+
That distinction (evidence vs. contract verdict) is the boundary between
|
|
239
|
+
v0.4 and v0.5+.
|
|
240
|
+
|
|
241
|
+
**Comparison identity**: `comparisonRequestId` is a pure, deterministic
|
|
242
|
+
function of `{beforeObservationId, afterObservationId, normalized
|
|
243
|
+
ComparisonConfig}` - direction-sensitive (`compare(A, B) !==
|
|
244
|
+
compare(B, A)`), and never includes an operational filesystem path.
|
|
245
|
+
`comparisonId` is fresh per execution (same pattern as `observationId`).
|
|
246
|
+
|
|
247
|
+
**Source references**: the comparison artifact retains enough logical
|
|
248
|
+
identity to trace back to its authoritative source observations
|
|
249
|
+
(`observationId`, `requestId`, `producer`, `observationSchemaVersion`, and
|
|
250
|
+
the source `screenshot.path`) without embedding the full
|
|
251
|
+
`ObservationArtifact` or copying screenshot bytes. The persisted comparison
|
|
252
|
+
directory contains `manifest.json` only.
|
|
253
|
+
|
|
254
|
+
The public entry point is `my-frontend-observer compare --before <root>
|
|
255
|
+
--after <root> --output <directory> [--config-file <json-file>]` (see
|
|
256
|
+
`docs/COMMANDS.md`) - comparison itself never launches a browser.
|
|
257
|
+
|
|
258
|
+
## v0.5 frontend contract and evaluation (shipped as part of this release)
|
|
259
|
+
|
|
260
|
+
Downstream of the v0.4 observation/comparison/relationship evidence above,
|
|
261
|
+
`src/domain/frontendContracts.ts` freezes the v0.5 contract/change-scope
|
|
262
|
+
model, `src/domain/frontendContractIdentity.ts` freezes deterministic
|
|
263
|
+
contract/baseline/clause identity, and `src/domain/frontendContractEvaluation.ts`
|
|
264
|
+
implements the one canonical pure evaluation engine. Baseline/per-change
|
|
265
|
+
contract persistence, evaluation-artifact persistence, explicit baseline
|
|
266
|
+
approval, and public CLI exposure are all implemented and shipped (see
|
|
267
|
+
"v0.5 contract and evaluation persistence" and "v0.5 public contract/
|
|
268
|
+
evaluation commands" below).
|
|
269
|
+
|
|
270
|
+
**Contract classes**: a `PersistentBaselineContract` (append/supersession-based
|
|
271
|
+
history via an optional `supersedesBaselineId`) and a `PerChangeContract`
|
|
272
|
+
(the allowed scope of one requested change). Both share `artifactKind:
|
|
273
|
+
"my-frontend-observer/frontend-contract"` and `schemaVersion: "1.0.0"` - an
|
|
274
|
+
independent family from the observation (`1.2.0`) and comparison (`1.0.0`)
|
|
275
|
+
schemas; the frontend-contract schema constant happens to share the version
|
|
276
|
+
string `1.0.0` with comparison's by coincidence only.
|
|
277
|
+
|
|
278
|
+
**Four authored categories, one derived classification**: every per-change
|
|
279
|
+
clause is authored as exactly one of `requested`, `expected-dependent`,
|
|
280
|
+
`protected`, or `preserved`. `unexpected` is a fifth, *derived-only*
|
|
281
|
+
classification the evaluator produces for a meaningful rendered difference no
|
|
282
|
+
active clause accounts for - it can never be authored as a permission.
|
|
283
|
+
|
|
284
|
+
**Bounded contract primitives**: 15 frozen `ContractPrimitive` kinds cover
|
|
285
|
+
visibility, clipping, width bounds, non-overlap, relative width, vertical
|
|
286
|
+
sequence, geometric fit (explicitly distinct from DOM containment),
|
|
287
|
+
document-width-vs-viewport, scroll ownership, initial-viewport position,
|
|
288
|
+
relationship-unchanged, and property-unchanged/increases/decreases - a closed
|
|
289
|
+
vocabulary, never a generic expression language.
|
|
290
|
+
|
|
291
|
+
**Contract tolerance**: `exact` / `absolute-px` / `percent`, independent of
|
|
292
|
+
`ComparisonConfig.geometryTolerancePx` (which only suppresses insignificant
|
|
293
|
+
comparison noise and is never contract authorization). Percent tolerance's
|
|
294
|
+
denominator is the absolute before-value.
|
|
295
|
+
|
|
296
|
+
**Required vs. permitted expected-dependent**: `required` clauses must occur
|
|
297
|
+
compliantly to pass; `permitted` clauses accept no change or a compliant
|
|
298
|
+
change, and fail only on a strictly contradictory change.
|
|
299
|
+
|
|
300
|
+
**Evaluation result vocabulary**: each clause resolves to `pass` / `fail` /
|
|
301
|
+
`unavailable` (with a required non-empty reason - required evidence gaps and
|
|
302
|
+
an `incomparable` source comparison never fabricate a `pass`) / `conflict`
|
|
303
|
+
(with at least two `conflictingClauseIds` - covers both an unresolved
|
|
304
|
+
baseline/per-change contradiction and an unknown `supersedesBaselineClauseIds`
|
|
305
|
+
reference). The overall verdict is `PASS` only when every clause result is
|
|
306
|
+
`pass` and no unexpected change remains; otherwise `FAIL` - there is no
|
|
307
|
+
partial-pass scoring.
|
|
308
|
+
|
|
309
|
+
**Explicit supersession, never inferred**: a per-change clause may list
|
|
310
|
+
`supersedesBaselineClauseIds` to remove specific baseline clauses from active
|
|
311
|
+
evaluation. Two clauses that structurally contradict each other on the same
|
|
312
|
+
(target, property) without explicit supersession produce a `conflict`, never
|
|
313
|
+
a silent preference for one side.
|
|
314
|
+
|
|
315
|
+
**Reuses existing v0.4 evidence directly**: the evaluator consumes an
|
|
316
|
+
already-computed `ComparisonArtifact` (`differences`, `relationshipChanges`,
|
|
317
|
+
`relationshipsBefore`/`relationshipsAfter`, `comparability`) and the source
|
|
318
|
+
`ObservationArtifact` pair - it never re-launches a browser, re-resolves a
|
|
319
|
+
target, or reimplements clipping/relationship/scroll-owner derivation.
|
|
320
|
+
Unexpected-change derivation reads `ComparisonArtifact.differences` only
|
|
321
|
+
(which already includes one difference per relationship change), so a single
|
|
322
|
+
logical transition is never double-counted.
|
|
323
|
+
|
|
324
|
+
## v0.5 contract and evaluation persistence (shipped as part of this release)
|
|
325
|
+
|
|
326
|
+
Persistence consumes the frozen v0.5 domain above; it never redefines it.
|
|
327
|
+
`src/artifacts/frontendContractArtifactWriter.ts`/`frontendContractArtifactReader.ts`
|
|
328
|
+
persist and read both `PersistentBaselineContract` and `PerChangeContract`
|
|
329
|
+
symmetrically (both already share `CONTRACT_ARTIFACT_KIND`/`CONTRACT_SCHEMA_VERSION`,
|
|
330
|
+
so one writer/reader pair serves both contract classes) as
|
|
331
|
+
`<outputLocation>/<baselineId|contractId>/manifest.json`, following the same
|
|
332
|
+
atomic-write discipline as `artifacts/artifactWriter.ts`/`artifacts/comparisonArtifactWriter.ts`
|
|
333
|
+
(sibling temporary directory, then one atomic rename; an existing directory at
|
|
334
|
+
the final identity is a genuine collision and is rejected, never overwritten -
|
|
335
|
+
prior baseline history is never rewritten). `src/artifacts/comparisonArtifactReader.ts`
|
|
336
|
+
is a new Batch 3 addition (no comparison reader existed before) mirroring
|
|
337
|
+
`artifacts/artifactReader.ts`'s discipline exactly, changing no comparison
|
|
338
|
+
semantics and keeping comparison schema `1.0.0`.
|
|
339
|
+
|
|
340
|
+
**Evaluation artifact envelope**: Batch 1 froze the evaluation-result
|
|
341
|
+
vocabulary (`ClauseEvaluationResult`, `OverallVerdict`) but not a persistable
|
|
342
|
+
envelope, so `src/domain/frontendContractEvaluationArtifact.ts` adds exactly
|
|
343
|
+
that - `artifactKind: "my-frontend-observer/frontend-contract-evaluation"`,
|
|
344
|
+
`schemaVersion: "1.0.0"` (its own independent family, distinct from
|
|
345
|
+
observation/comparison/frontend-contract), an `evaluationId`/`evaluationRequestId`
|
|
346
|
+
pair, bounded `before`/`after` source-observation references, and
|
|
347
|
+
`comparisonId`/`comparisonRequestId` plus `contracts: {baselineId,
|
|
348
|
+
contractId}` references - never an embedded `ObservationArtifact` or copied
|
|
349
|
+
screenshot. It reuses `ClauseEvaluationResult`/`OverallVerdict`/
|
|
350
|
+
`UnexpectedChangeResult` unchanged and contains no evaluation logic itself.
|
|
351
|
+
`evaluationRequestId` is a deterministic function of `{baselineId,
|
|
352
|
+
contractId, beforeObservationId, afterObservationId, comparisonRequestId}`
|
|
353
|
+
(`frontendContractIdentity.ts#buildFrontendContractEvaluationRequestIdentity` -
|
|
354
|
+
deliberately `comparisonRequestId`, not the fresh-per-execution
|
|
355
|
+
`comparisonId`, so semantically identical evaluations share an identity);
|
|
356
|
+
`evaluationId` reuses the existing generic `buildFrontendContractInstanceIdentity`
|
|
357
|
+
unchanged. `src/artifacts/frontendContractEvaluationArtifactWriter.ts`/
|
|
358
|
+
`frontendContractEvaluationArtifactReader.ts` persist/read it with the same
|
|
359
|
+
atomic-write discipline as above.
|
|
360
|
+
|
|
361
|
+
**Application seam**: `src/application/frontendContractEvaluationService.ts#evaluateAndPersist`
|
|
362
|
+
calls the existing pure `evaluateFrontendContract` exactly once and - only
|
|
363
|
+
for a structurally constructible result, whether the verdict is `PASS` or
|
|
364
|
+
`FAIL` - persists exactly one evaluation artifact; an `{ok: false}` evaluator
|
|
365
|
+
result (evidence could not be constructed into an evaluation at all) is never
|
|
366
|
+
persisted as a fabricated artifact. `evaluateAndPersistFromArtifactRoots` is
|
|
367
|
+
the future-CLI-facing wrapper: it reads two observations through the existing
|
|
368
|
+
`readObservationArtifact` (never a second observation reader), the
|
|
369
|
+
comparison and the two contracts through the readers above, then delegates
|
|
370
|
+
to `evaluateAndPersist` exactly once.
|
|
371
|
+
|
|
372
|
+
## v0.5 public contract/evaluation commands (shipped as part of this release)
|
|
373
|
+
|
|
374
|
+
Three public commands expose the persistence/evaluation contract above (see
|
|
375
|
+
`docs/COMMANDS.md` for exact flags/output/exit behavior, not duplicated
|
|
376
|
+
here):
|
|
377
|
+
|
|
378
|
+
- `approve-baseline` → `frontendContractPersistenceService.ts#approveAndPersistBaseline`
|
|
379
|
+
→ validates a `PersistentBaselineContract` and its `sourceObservation`
|
|
380
|
+
coherence against a supplied observation artifact → persists via
|
|
381
|
+
`frontendContractArtifactWriter.ts`. The only baseline-approval act in the
|
|
382
|
+
observer.
|
|
383
|
+
- `save-change-contract` → `frontendContractPersistenceService.ts#persistPerChangeContract`
|
|
384
|
+
→ validates a `PerChangeContract` (rejecting a baseline contract, an
|
|
385
|
+
authored `unexpected` category, or any other structural violation) →
|
|
386
|
+
persists via the same writer. Persistence only, never approval.
|
|
387
|
+
- `evaluate-contract` → `frontendContractEvaluationService.ts#evaluateAndPersistFromArtifactRoots`
|
|
388
|
+
→ `evaluateFrontendContract` exactly once → `frontendContractEvaluationArtifactWriter.ts`
|
|
389
|
+
exactly once. `--enforce` affects only the process exit status for an
|
|
390
|
+
already-persisted `FAIL` verdict.
|
|
391
|
+
|
|
392
|
+
No command infers baseline approval or supersession automatically - not
|
|
393
|
+
`compare`, not a `PASS` evaluation, not any artifact writer.
|
|
394
|
+
|
|
395
|
+
This full command sequence is proven against real Chromium observations (not
|
|
396
|
+
hand-constructed artifacts) - see "v0.5 real-browser workflow proof" below.
|
|
397
|
+
|
|
398
|
+
## v0.5 real-browser workflow proof (shipped as part of this release)
|
|
399
|
+
|
|
400
|
+
`tests/browser/cliFrontendContracts.test.ts` and
|
|
401
|
+
`scripts/dev/builtCliFrontendContractsBrowserSmoke.mjs` drive the complete
|
|
402
|
+
`observe` → `approve-baseline` → `save-change-contract` → `observe` →
|
|
403
|
+
`compare` → `evaluate-contract` sequence against a real disposable local HTTP
|
|
404
|
+
fixture and real Chromium, proving two scenarios:
|
|
405
|
+
|
|
406
|
+
- a fully successful contract change - a real observed navigation-width
|
|
407
|
+
decrease and workspace-width increase, both satisfying their authored
|
|
408
|
+
`requested`/`expected-dependent` clauses, an unchanged `protected` rail
|
|
409
|
+
width, and an unclipped `preserved` navigation - overall `PASS`;
|
|
410
|
+
- the "milestone signature" failure - the same locally successful requested
|
|
411
|
+
change (navigation shrinks, workspace expands, both still `pass`)
|
|
412
|
+
co-occurring with a genuine `protected` right-rail width regression (a real
|
|
413
|
+
`resized` comparison difference) and a genuine `preserved` navigation
|
|
414
|
+
clipping regression (a real `clipping-changed` difference, `not-clipped` →
|
|
415
|
+
`clipped`) - overall `FAIL`.
|
|
416
|
+
|
|
417
|
+
Both scenarios confirm: `--enforce` changes only the process exit status
|
|
418
|
+
(`0` without it, nonzero with it) for the identical persisted
|
|
419
|
+
`evaluationRequestId`/`clauseResults`; every source observation and
|
|
420
|
+
comparison artifact is byte-identical before and after evaluation; the
|
|
421
|
+
evaluation directory contains `manifest.json` only (no copied screenshot);
|
|
422
|
+
and no operational filesystem path is ever serialized into a persisted
|
|
423
|
+
manifest. This is real-browser evidence layered on top of the CLI-level
|
|
424
|
+
proof in `tests/unit/cliFrontendContracts.test.ts` and the Chromium-free
|
|
425
|
+
`scripts/dev/builtCliFrontendContractsSmoke.mjs` - it does not replace them.
|
|
426
|
+
|
|
427
|
+
## v0.6 bounded agent context and correlation contract (released as `0.6.0`)
|
|
428
|
+
|
|
429
|
+
**Current status: released as package version `0.6.0`, tag `v0.6.0`, from
|
|
430
|
+
the canonical `canonicalization/v0.6` lineage.** Bounded-agent-context is a new, independent
|
|
431
|
+
artifact-kind family, schema `1.0.0` (`BOUNDED_AGENT_CONTEXT_ARTIFACT_KIND =
|
|
432
|
+
"my-frontend-observer/bounded-agent-context"`) - never a bump to
|
|
433
|
+
observation/comparison/frontend-contract/evaluation schemas, which remain
|
|
434
|
+
`1.2.0`/`1.0.0`/`1.0.0`/`1.0.0` respectively. Unlike those families, there is
|
|
435
|
+
**no disk artifact writer/reader** for bounded-agent-context: it is a pure
|
|
436
|
+
programmatic contract and derivation layer, exported from `src/index.ts`
|
|
437
|
+
only.
|
|
438
|
+
|
|
439
|
+
**Bounded runtime projection**
|
|
440
|
+
(`src/domain/boundedAgentContextProjection.ts#projectBoundedAgentContext`)
|
|
441
|
+
produces a `BoundedRuntimeTargetProjection` from already-captured v0.1-v0.5
|
|
442
|
+
evidence, containing: page/viewport identity; stable target identities;
|
|
443
|
+
important geometry and runtime behavior; layout/behavior relationships;
|
|
444
|
+
before/after differences; contract clause results; requested/expected-
|
|
445
|
+
dependent/protected/preserved scope - reusing `src/domain/
|
|
446
|
+
frontendContracts.ts`'s existing clause types verbatim, never a
|
|
447
|
+
reimplementation; diagnostics; screenshot/artifact references; provenance;
|
|
448
|
+
and explicit `OmissionRecord`/`TruncationRecord` metadata with bounded
|
|
449
|
+
aggregate-cap summarization once a limit is reached.
|
|
450
|
+
|
|
451
|
+
**Adequacy**: every projection carries an `Adequacy` value
|
|
452
|
+
(`adequate`/`partial`/`inadequate`) plus a structured, closed
|
|
453
|
+
`ADEQUACY_REASON_CODES` vocabulary - evidence existing is not itself
|
|
454
|
+
adequacy; a required omission or an `incomparable`/unavailable upstream
|
|
455
|
+
source is reflected honestly rather than silently reported as sufficient.
|
|
456
|
+
|
|
457
|
+
**Runtime/static correlation**
|
|
458
|
+
(`src/domain/boundedAgentContextCorrelation.ts#deriveRuntimeStaticCorrelations`/
|
|
459
|
+
`attachRuntimeStaticCorrelations`) evaluates each stable runtime target
|
|
460
|
+
against caller-supplied candidate static-evidence records into exactly one of
|
|
461
|
+
three outcomes: `correlated`, `ambiguous` (multiple competing candidates,
|
|
462
|
+
all preserved and visible - never silently resolved to one), or
|
|
463
|
+
`unavailable` (no supported candidate). The module accepts only plain,
|
|
464
|
+
already-retrieved candidate records and has no dependency on
|
|
465
|
+
`@dailephd/my-dev-kit` - the audit preceding implementation found no generic
|
|
466
|
+
static-side retrieval capability actually missing (see `docs/ROADMAP.md` v0.6
|
|
467
|
+
"Dependency direction"). A runtime target identity is carried through
|
|
468
|
+
verbatim; correlation never produces a `sourceOwner`/`causedBy`-shaped field,
|
|
469
|
+
so a stable runtime identity is never silently reported as source ownership.
|
|
470
|
+
|
|
471
|
+
**Identity**: `src/domain/boundedAgentContextIdentity.ts#buildBoundedAgentContextRequestIdentity`/
|
|
472
|
+
`buildBoundedAgentContextInstanceIdentity` follow the same
|
|
473
|
+
canonicalize+sha256(+opaque-nonce) pattern as `comparisonIdentity.ts`/
|
|
474
|
+
`frontendContractIdentity.ts`: a deterministic logical identity distinct from
|
|
475
|
+
a fresh per-execution instance identity.
|
|
476
|
+
|
|
477
|
+
**Export/public boundary**: `src/index.ts` exports the complete
|
|
478
|
+
bounded-agent-context/correlation type and function surface as a
|
|
479
|
+
programmatic library contract. There is no CLI command (`observe`/`compare`/
|
|
480
|
+
`approve-baseline`/`save-change-contract`/`evaluate-contract` remain the only
|
|
481
|
+
public commands) and no orchestrator/lab code in this repository - bounded
|
|
482
|
+
runtime-evidence consumption by `my-dev-kit-orchestrator` and exact
|
|
483
|
+
readers/fixtures/evaluation in `my-dev-kit-lab` are separate sibling-
|
|
484
|
+
repository deliverables outside `my-frontend-observer`'s public surface.
|
|
485
|
+
|
|
486
|
+
**Compatibility evidence**: cross-repository neutral verification (observer
|
|
487
|
+
`514bf3bb513764815a0a5b9e508d5836aa7d7fd8`, orchestrator `9473e4c`, lab
|
|
488
|
+
`271e72c`) passed with 6/6 requirement coverage and no known product
|
|
489
|
+
blockers; on the canonical worktree, `npm run typecheck`, `npm run lint`,
|
|
490
|
+
`npm test` (627 tests), `npm run test:browser` (120 tests), `npm run
|
|
491
|
+
test:security`, `npm run build`, and `npm run check:docs` all pass.
|
|
492
|
+
|
|
493
|
+
## v0.7 external visual-reference contract direction (released as `0.7.0`; v0.8 viewer released as `0.8.0`; v0.9-v0.10 still future)
|
|
494
|
+
|
|
495
|
+
External visual-reference support is released as package version `0.7.0`
|
|
496
|
+
(see "v0.7 Prompt 1" through "v0.7 Prompt 8" below for the exact contract).
|
|
497
|
+
The exact public type names, artifact kinds, schema versions, persistence
|
|
498
|
+
layout, and command/programmatic entry points were designed during v0.7
|
|
499
|
+
implementation from current repository precedent, following the constraints
|
|
500
|
+
below. v0.8 (released as package version `0.8.0` - see
|
|
501
|
+
`docs/CURRENT_STATE.md`) has preserved them; v0.9-v0.10 remain future and
|
|
502
|
+
must continue to preserve them.
|
|
503
|
+
|
|
504
|
+
**Distinct evidence domain**: an external reference is desired-design evidence,
|
|
505
|
+
not an `ObservationArtifact` and not the "before" side of a v0.4
|
|
506
|
+
`ComparisonArtifact`. Reference design vs candidate is distinct from both
|
|
507
|
+
before vs after comparison and frontend-contract evaluation. The implementation
|
|
508
|
+
must not fake this distinction by wrapping a raster image in an observation
|
|
509
|
+
shape.
|
|
510
|
+
|
|
511
|
+
**Reference identity and provenance**: a future reference contract must preserve
|
|
512
|
+
a deterministic logical reference identity/version where appropriate, source
|
|
513
|
+
image reference plus dimensions/format, provenance, bounded region definitions,
|
|
514
|
+
applicable viewport/theme/application-state identity, authored design intent,
|
|
515
|
+
relationship/style evidence where supported, limits/diagnostics, and approval/
|
|
516
|
+
supersession history. Operational filesystem paths must not become semantic
|
|
517
|
+
identity. A raw imported image never silently becomes an approved active
|
|
518
|
+
reference.
|
|
519
|
+
|
|
520
|
+
**Reference regions and runtime targets stay distinct**: a reference region
|
|
521
|
+
must have its own identity and coordinate semantics. Reference-region to runtime-
|
|
522
|
+
target association must be explicit and capable of representing ambiguity or
|
|
523
|
+
unavailability. Runtime target identity and reference identity must never
|
|
524
|
+
silently become static source ownership; static association still goes through
|
|
525
|
+
the v0.6 runtime/static correlation boundary.
|
|
526
|
+
|
|
527
|
+
**Applicability before fidelity**: viewport, theme, application state, and
|
|
528
|
+
other selected compatibility dimensions must be evaluated before ordinary
|
|
529
|
+
reference/candidate differences are produced. If the reference and candidate
|
|
530
|
+
represent different intended states, the result must be explicitly incompatible
|
|
531
|
+
or incomparable rather than filled with fabricated visual failures. Planning
|
|
532
|
+
should reuse or extend the canonical v0.4 comparability conventions where they
|
|
533
|
+
mean the same thing rather than invent an unrelated reference-only state model.
|
|
534
|
+
|
|
535
|
+
**Canonical contract semantics remain authoritative**: executable reference-
|
|
536
|
+
derived requirements must map into the existing v0.5 authored categories
|
|
537
|
+
`requested`, `expected-dependent`, `protected`, or `preserved`. The derived-only
|
|
538
|
+
`unexpected` classification remains derived-only. Informational or unassessed
|
|
539
|
+
reference evidence may stay outside executable contract evaluation until
|
|
540
|
+
explicitly promoted. A second reference-only PASS/FAIL taxonomy is forbidden.
|
|
541
|
+
|
|
542
|
+
**Tolerance separation**: reference-fidelity tolerances are not automatically
|
|
543
|
+
the same as v0.4 `ComparisonConfig.geometryTolerancePx` or v0.5 contract
|
|
544
|
+
tolerances. Planning must define property-specific semantics for reference
|
|
545
|
+
geometry, spacing, selected style evidence, text/font rendering differences,
|
|
546
|
+
asset-sensitive regions, and optional image similarity. One global pixel-perfect
|
|
547
|
+
threshold is not an acceptable contract.
|
|
548
|
+
|
|
549
|
+
**Structured evidence first**: geometry, relationships, authored requirements,
|
|
550
|
+
applicability, provenance, and selected bounded style/asset evidence remain
|
|
551
|
+
inspectable primary evidence. Screenshot-region or image-similarity evidence may
|
|
552
|
+
supplement them where reliable, but pixel similarity alone must not determine
|
|
553
|
+
success and must never override active baseline/per-change contracts.
|
|
554
|
+
|
|
555
|
+
**Bounded correction evidence**: future reference/candidate results must support
|
|
556
|
+
a bounded projection suitable for coding-agent correction, such as reference
|
|
557
|
+
measurement, candidate measurement, delta, failed relationship/style condition,
|
|
558
|
+
relevant reference/runtime identities, provenance, and active protected/
|
|
559
|
+
preserved constraints. Heavy reference image bytes should be referenced, not
|
|
560
|
+
copied into every downstream context packet.
|
|
561
|
+
|
|
562
|
+
**Approval and supersession**: reference import, reference approval, baseline
|
|
563
|
+
approval, reference supersession, and baseline supersession are separate acts.
|
|
564
|
+
A reference-fidelity `PASS`, a frontend-contract `PASS`, or a successful
|
|
565
|
+
before/after comparison must not silently approve or replace any reference or
|
|
566
|
+
baseline.
|
|
567
|
+
|
|
568
|
+
The v0.8 viewer, released as package version `0.8.0`, consumes this v0.7
|
|
569
|
+
reference/evaluation contract exactly as required - it creates no UI-only
|
|
570
|
+
reference model (see `docs/ARCHITECTURE.md` "v0.8 Batch 5"/"v0.8 Batch 6"
|
|
571
|
+
and `docs/reports/v0.8-reference-candidate-inspection-batch5.md`). v0.9
|
|
572
|
+
annotations may originate
|
|
573
|
+
from runtime screenshots or external references but must preserve which source
|
|
574
|
+
identity/coordinate system they belong to and feed the same canonical contract
|
|
575
|
+
semantics. v0.10 combines both entry modes into the full correction/approval
|
|
576
|
+
workflow.
|
|
577
|
+
|
|
578
|
+
## Approved v0.1 design inputs
|
|
579
|
+
|
|
580
|
+
The historical greenfield scaffold plan recorded these v0.1 design decisions:
|
|
581
|
+
|
|
582
|
+
- artifact kind `my-frontend-observer/observation`;
|
|
583
|
+
- schema version `1.0.0`, independent of package version;
|
|
584
|
+
- one portable directory containing `manifest.json`, `evidence.json`, and
|
|
585
|
+
`screenshot.png`;
|
|
586
|
+
- evidence states `available`, `unavailable`, `not-applicable`, and `partial`;
|
|
587
|
+
- evidence sources `browser`, `computed-browser`, and `derived`;
|
|
588
|
+
- bounded explicitly requested targets, provenance, diagnostics, completion
|
|
589
|
+
state, limits, and relative artifact references.
|
|
590
|
+
|
|
591
|
+
These were planning inputs only at the time they were recorded. As shown in
|
|
592
|
+
"Current contracts" above, the implemented contract matches them except for
|
|
593
|
+
the file layout: there is no separate `evidence.json` - page/target evidence
|
|
594
|
+
is embedded directly inside `manifest.json`.
|
|
595
|
+
|
|
596
|
+
Comparison and relationship contracts belong to v0.4, and canonical
|
|
597
|
+
change-scope contracts belong to v0.5 - see "v0.5 frontend contract and
|
|
598
|
+
evaluation" above for the full shipped contract model, identity, evaluation
|
|
599
|
+
engine, persistence, baseline approval, and CLI exposure. Bounded
|
|
600
|
+
agent-context and runtime/static correlation contracts are v0.6 - see "v0.6
|
|
601
|
+
bounded agent context and correlation contract" above for the full released
|
|
602
|
+
model. The text/config-driven coding-agent review plus non-graphical external
|
|
603
|
+
visual-reference foundation is v0.7 - see "v0.7 Prompt 1 external-reference
|
|
604
|
+
artifact contract" below for the foundation layer implemented so far. Viewer
|
|
605
|
+
consumption of that reference model is released in v0.8 (see "v0.7 external
|
|
606
|
+
visual-reference contract direction" above); dual-context annotation follows
|
|
607
|
+
in v0.9; both visual entry modes converge with the existing workflow in
|
|
608
|
+
v0.10.
|
|
609
|
+
|
|
610
|
+
## v0.7 Prompt 1 external-reference artifact contract
|
|
611
|
+
|
|
612
|
+
Released as `0.7.0`. This is the foundation layer only: identity,
|
|
613
|
+
provenance, bounded image metadata, and a two-state lifecycle for one
|
|
614
|
+
externally supplied design-reference image. It implements no region,
|
|
615
|
+
geometry, relationship, requirement, tolerance, binding, or fidelity-
|
|
616
|
+
evaluation contract - those belong to later v0.7 prompts.
|
|
617
|
+
|
|
618
|
+
An external reference is a distinct evidence root, not a variant of
|
|
619
|
+
`ObservationArtifact`: it never reuses `ARTIFACT_KIND`/`SCHEMA_VERSION`
|
|
620
|
+
(observation), `COMPARISON_ARTIFACT_KIND`, or `CONTRACT_ARTIFACT_KIND`, and
|
|
621
|
+
those existing types gain no new field from this contract.
|
|
622
|
+
|
|
623
|
+
```ts
|
|
624
|
+
const EXTERNAL_REFERENCE_ARTIFACT_KIND = 'my-frontend-observer/external-reference';
|
|
625
|
+
const EXTERNAL_REFERENCE_SCHEMA_VERSION = '1.0.0'; // independent of package.json version and every other family's schema version
|
|
626
|
+
|
|
627
|
+
type ExternalReferenceImageFormat = 'png' | 'jpeg' | 'webp';
|
|
628
|
+
|
|
629
|
+
interface ExternalReferenceImageReference {
|
|
630
|
+
path: string; // bare relative filename within the artifact's own directory
|
|
631
|
+
format: ExternalReferenceImageFormat;
|
|
632
|
+
width: number;
|
|
633
|
+
height: number;
|
|
634
|
+
byteLength: number;
|
|
635
|
+
sha256: string; // identity-bearing content hash of the raw image bytes
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// Points back to the imported artifact that owns the image, without copying its bytes - mirrors ComparisonSourceObservationReference.
|
|
639
|
+
interface ExternalReferenceSourceReference {
|
|
640
|
+
referenceId: string;
|
|
641
|
+
referenceRequestId: string;
|
|
642
|
+
producer: { name: 'my-frontend-observer'; version: string };
|
|
643
|
+
schemaVersion: '1.0.0';
|
|
644
|
+
image: ExternalReferenceImageReference;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// Exactly two persisted states - no literal 'superseded' variant (see below).
|
|
648
|
+
type ExternalReferenceLifecycleState = { state: 'imported' } | { state: 'approved'; approvedAt: string };
|
|
649
|
+
|
|
650
|
+
interface ExternalReferenceArtifactBase {
|
|
651
|
+
artifactKind: 'my-frontend-observer/external-reference';
|
|
652
|
+
schemaVersion: '1.0.0';
|
|
653
|
+
referenceRequestId: string; // deterministic logical identity - shared by an imported artifact and every artifact produced by approving it
|
|
654
|
+
referenceId: string; // fresh per-persisted-instance identity
|
|
655
|
+
producer: { name: 'my-frontend-observer'; version: string };
|
|
656
|
+
provenance: { importedAt: string; label?: string };
|
|
657
|
+
supersedesReferenceId?: string; // explicit, forward-only supersession of a prior reference's referenceId
|
|
658
|
+
diagnostics: Diagnostic[];
|
|
659
|
+
completion: CompletionState;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// lifecycle.state === 'imported': owns the image.
|
|
663
|
+
interface ImportedExternalReferenceArtifact extends ExternalReferenceArtifactBase {
|
|
664
|
+
lifecycle: { state: 'imported' };
|
|
665
|
+
image: ExternalReferenceImageReference;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// lifecycle.state === 'approved': references, never copies, the imported artifact's image.
|
|
669
|
+
interface ApprovedExternalReferenceArtifact extends ExternalReferenceArtifactBase {
|
|
670
|
+
lifecycle: { state: 'approved'; approvedAt: string };
|
|
671
|
+
sourceReference: ExternalReferenceSourceReference;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
type ExternalReferenceArtifact = ImportedExternalReferenceArtifact | ApprovedExternalReferenceArtifact;
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
Key rules:
|
|
678
|
+
|
|
679
|
+
- `referenceRequestId` is a pure function of `{imageSha256, format, width,
|
|
680
|
+
height, supersedesReferenceId}` only - never a filesystem path, output
|
|
681
|
+
location, label, or timestamp. Byte-identical image content imported from a
|
|
682
|
+
different operational root produces the same `referenceRequestId`;
|
|
683
|
+
changing any of those fields changes it.
|
|
684
|
+
- `referenceId` is fresh (nonce-based) on every persisted write, including
|
|
685
|
+
every approval of an already-imported reference.
|
|
686
|
+
- Importing an image never approves it (`lifecycle.state` is always
|
|
687
|
+
`'imported'` immediately after import, regardless of a supplied label or
|
|
688
|
+
supersession target). Approval is a single explicit act
|
|
689
|
+
(`approveExternalReference`, mirroring `approveAndPersistBaseline`) that
|
|
690
|
+
refuses anything not currently in the `'imported'` state.
|
|
691
|
+
- Approving persists a *new* artifact instance (same `referenceRequestId`,
|
|
692
|
+
fresh `referenceId`) carrying a `sourceReference` back to the imported
|
|
693
|
+
artifact - it never mutates the imported artifact's own manifest, and never
|
|
694
|
+
copies the image bytes a second time.
|
|
695
|
+
- Supersession is represented only as a forward pointer
|
|
696
|
+
(`supersedesReferenceId` on the newer artifact); there is deliberately no
|
|
697
|
+
literal `'superseded'` lifecycle state, so an existing persisted artifact's
|
|
698
|
+
own manifest is never rewritten - immutability holds unconditionally rather
|
|
699
|
+
than depending on careful mutation discipline.
|
|
700
|
+
- Supported formats are frozen to exactly `png`/`jpeg`/`webp`, detected from
|
|
701
|
+
header/magic bytes only (never a caller-declared file extension), bounded
|
|
702
|
+
to `EXTERNAL_REFERENCE_MAX_IMAGE_BYTES` (20,000,000 bytes) and
|
|
703
|
+
`[EXTERNAL_REFERENCE_MIN_DIMENSION_PX, EXTERNAL_REFERENCE_MAX_DIMENSION_PX]`
|
|
704
|
+
(`[1, 8192]`) pixels per side. No OCR, no raster decode, no computer
|
|
705
|
+
vision, no automatic region detection.
|
|
706
|
+
|
|
707
|
+
Persisted as `<outputLocation>/<referenceId>/manifest.json` (+
|
|
708
|
+
`reference.<ext>` for an `'imported'` artifact only), via the same atomic
|
|
709
|
+
temp-dir-then-rename discipline as every other artifact family
|
|
710
|
+
(`src/artifacts/externalReferenceArtifactWriter.ts` /
|
|
711
|
+
`externalReferenceArtifactReader.ts`). CLI: `import-reference <image-file>
|
|
712
|
+
--output <dir> [--label] [--supersedes <root>]` and `approve-reference
|
|
713
|
+
--reference <root> --output <dir> [--supersedes <root>]`.
|
|
714
|
+
|
|
715
|
+
## v0.7 Prompt 2 explicit reference regions and relationships
|
|
716
|
+
|
|
717
|
+
Released as `0.7.0`. Additive extension of the Prompt 1 contract above:
|
|
718
|
+
one new, optional `regions?: ReferenceRegion[]` field on
|
|
719
|
+
`ExternalReferenceArtifact` (both lifecycle variants), plus a pure,
|
|
720
|
+
non-persisted relationship-derivation capability. No schema version bump -
|
|
721
|
+
`EXTERNAL_REFERENCE_SCHEMA_VERSION` remains `'1.0.0'`, because the field is
|
|
722
|
+
genuinely optional/additive and every Prompt 1 artifact (which predates this
|
|
723
|
+
field entirely) remains valid without it.
|
|
724
|
+
|
|
725
|
+
```ts
|
|
726
|
+
// domain/externalReferenceRegions.ts
|
|
727
|
+
interface ReferenceRegionRectangle { x: number; y: number; width: number; height: number; }
|
|
728
|
+
interface ReferenceRegion { id: string; rectangle: ReferenceRegionRectangle; }
|
|
729
|
+
|
|
730
|
+
// Pure derived geometry - never persisted, always recomputed, so it can never drift from the rectangle above.
|
|
731
|
+
interface ReferenceRegionGeometry {
|
|
732
|
+
x: number; y: number; width: number; height: number;
|
|
733
|
+
right: number; bottom: number; centerX: number; centerY: number;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
const REFERENCE_REGION_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/; // same convention as request/request.ts's target-name pattern
|
|
737
|
+
const MAX_REFERENCE_REGIONS = 20; // same bound value as request/request.ts's MAX_TARGETS - independently owned, coincidentally equal
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
Region coordinate semantics: origin at the reference image's top-left
|
|
741
|
+
corner, x increasing rightward, y increasing downward, unit is
|
|
742
|
+
reference-image pixels (explicitly not CSS pixels - a static image has no
|
|
743
|
+
CSS box model), coordinates may be fractional. A region's rectangle must lie
|
|
744
|
+
entirely within its owning image's own already-validated
|
|
745
|
+
width/height - out-of-bounds geometry is rejected outright, never clamped.
|
|
746
|
+
|
|
747
|
+
Key rules:
|
|
748
|
+
|
|
749
|
+
- Only `{x, y, width, height}` is canonical/authored. `right`, `bottom`,
|
|
750
|
+
`centerX`, `centerY` are pure calculations over it
|
|
751
|
+
(`deriveReferenceRegionGeometry`) - never a second, potentially-drifting
|
|
752
|
+
stored copy of the same fact.
|
|
753
|
+
- Region content is identity-bearing:
|
|
754
|
+
`buildExternalReferenceRequestIdentity` gained an additive, optional
|
|
755
|
+
trailing `regions` parameter. Omitting it entirely (every Prompt 1 call
|
|
756
|
+
site, and any Prompt 2 call that legitimately has no regions) produces the
|
|
757
|
+
byte-identical hash Prompt 1 already produced - the parameter is left out
|
|
758
|
+
of the hashed view rather than defaulted to `null`, unlike
|
|
759
|
+
`supersedesReferenceId`. Authored region order participates in identity
|
|
760
|
+
(arrays are never reordered by the shared `canonicalize()`), mirroring
|
|
761
|
+
`domain/identity.ts`'s treatment of configured targets.
|
|
762
|
+
- Region IDs are unique case-insensitively within one artifact (mirroring
|
|
763
|
+
`request/request.ts`'s target-name dedup convention exactly).
|
|
764
|
+
- `import-reference` gained an optional `--regions-file <json-file>` of the
|
|
765
|
+
form `{ "regions": [...] }` (same object-root-wrapper convention as
|
|
766
|
+
`--targets-file`); a legacy invocation without it behaves exactly as in
|
|
767
|
+
Prompt 1. `approve-reference` carries an imported artifact's `regions`
|
|
768
|
+
forward verbatim (never re-validated, never re-derived, never dropped) -
|
|
769
|
+
approval never adds, removes, or edits regions.
|
|
770
|
+
- One new diagnostic code, `invalid-reference-region` (error), covers every
|
|
771
|
+
region-validation failure (missing/duplicate/malformed id,
|
|
772
|
+
non-finite/negative/zero geometry, out-of-image-bounds, over the bounded
|
|
773
|
+
region count) - deliberately not split into several codes, per the
|
|
774
|
+
"don't proliferate diagnostics" convention.
|
|
775
|
+
|
|
776
|
+
Reference-region relationships (`domain/externalReferenceRegionRelationships.ts`)
|
|
777
|
+
reuse the exact same pure, tolerance-aware geometry predicates that
|
|
778
|
+
`domain/relationships.ts#deriveLayoutRelationships` uses for runtime targets
|
|
779
|
+
(`horizontalOrderOf`/`verticalOrderOf`/`areaOverlapOf`/`relativeWidthOf`/
|
|
780
|
+
`geometricFitOf`/`verticalSequenceOf`, now exported additively from that
|
|
781
|
+
module with unchanged formulas) and the same `PairwiseRelationshipKind`
|
|
782
|
+
vocabulary and `EvidenceReference` type - never a duplicated or
|
|
783
|
+
reinterpreted copy. Only the six geometry-only families apply (horizontal
|
|
784
|
+
order, vertical order, area overlap, relative width, geometric fit, vertical
|
|
785
|
+
sequencing); DOM containment, scroll ownership, runtime visibility, and
|
|
786
|
+
page-width-vs-viewport are runtime/browser concepts with no reference-image
|
|
787
|
+
equivalent and are not reused. `fits-inside`/`does-not-fit-inside` is
|
|
788
|
+
geometry-only fit - it never claims DOM containment, which an external image
|
|
789
|
+
cannot expose.
|
|
790
|
+
|
|
791
|
+
```ts
|
|
792
|
+
interface ReferenceRegionRelationship {
|
|
793
|
+
kind: PairwiseRelationshipKind;
|
|
794
|
+
subjectRegion: string; // deliberately distinct field name from PairwiseLayoutRelationship's subjectTarget
|
|
795
|
+
relatedRegion: string;
|
|
796
|
+
evidence: EvidenceReference[]; // e.g. { path: 'regions.header.rectangle' } - never a targetEvidence/browser path
|
|
797
|
+
}
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
Relationships are **not persisted** on the artifact - `deriveReferenceRegionRelationships(referenceRequestId, regions, options)`
|
|
801
|
+
is a pure, deterministic, synchronous function any caller (a future prompt,
|
|
802
|
+
a test) calls on demand against an artifact's own `regions` field, avoiding
|
|
803
|
+
any possibility of a persisted relationship graph drifting from the region
|
|
804
|
+
data it was derived from. Bounded at `MAX_REFERENCE_REGIONS` regions ->
|
|
805
|
+
`MAX_REFERENCE_REGION_PAIRS` pairs `x` 6 families =
|
|
806
|
+
`MAX_REFERENCE_REGION_RELATIONSHIP_RECORDS` records maximum - the same
|
|
807
|
+
bounding shape as `relationships.ts`'s `MAX_PAIRWISE_RELATIONSHIP_RECORDS`.
|
|
808
|
+
This is a maximum capacity, never a required minimum region count - there is
|
|
809
|
+
no contract requiring any specific number of authored regions.
|
|
810
|
+
|
|
811
|
+
A reference relationship is a fact about the reference image's geometry
|
|
812
|
+
only. It is not a design requirement, not a pass/fail verdict, and does not
|
|
813
|
+
claim a runtime target or source owner exists - see
|
|
814
|
+
`docs/WORKFLOWS.md` "Current external-reference foundation workflow" for
|
|
815
|
+
where those later concepts (Prompt 3+) will attach.
|
|
816
|
+
|
|
817
|
+
## v0.7 Prompt 3 selected design requirements, tolerance semantics, and reference-evidence adequacy
|
|
818
|
+
|
|
819
|
+
Released as `0.7.0`. Additive extension of the Prompt 1/2 contracts
|
|
820
|
+
above: one new, optional `requirements?: ExternalReferenceRequirement[]`
|
|
821
|
+
field on `ExternalReferenceArtifact` (both lifecycle variants). No schema
|
|
822
|
+
version bump - same reasoning as Prompt 2's `regions` field.
|
|
823
|
+
|
|
824
|
+
**Central distinction**: a region's geometry is REFERENCE EVIDENCE -
|
|
825
|
+
everything visibly/measurably present in the image. A requirement is
|
|
826
|
+
SELECTED DESIGN INTENT - only what the user/configuration explicitly chose
|
|
827
|
+
as mattering for later candidate evaluation. Nothing in this repository ever
|
|
828
|
+
turns a region property or a derived relationship into a requirement
|
|
829
|
+
automatically.
|
|
830
|
+
|
|
831
|
+
```ts
|
|
832
|
+
// domain/externalReferenceRequirements.ts
|
|
833
|
+
|
|
834
|
+
// Reused directly from domain/frontendContracts.ts - not reinvented as a
|
|
835
|
+
// "reference-only" taxonomy; that type carries no runtime-only coupling.
|
|
836
|
+
// 'unexpected' remains impossible to author (not a member of this union).
|
|
837
|
+
type AuthoredChangeScopeCategory = 'requested' | 'expected-dependent' | 'protected' | 'preserved';
|
|
838
|
+
type ExpectedDependentMode = 'required' | 'permitted'; // required only (and exactly) when category === 'expected-dependent'
|
|
839
|
+
|
|
840
|
+
type ReferenceRequirementRegionProperty = 'x' | 'y' | 'width' | 'height' | 'right' | 'bottom' | 'centerX' | 'centerY'; // exactly ReferenceRegionGeometry's own fields
|
|
841
|
+
type ReferenceRequirementMeasurement = 'vertical-gap' | 'horizontal-gap' | 'center-x-delta' | 'center-y-delta' | 'left-edge-delta' | 'right-edge-delta';
|
|
842
|
+
|
|
843
|
+
type ReferenceRequirementSubject =
|
|
844
|
+
| { kind: 'region-property'; region: string; property: ReferenceRequirementRegionProperty }
|
|
845
|
+
| { kind: 'region-relationship'; subjectRegion: string; relatedRegion: string; relationship: PairwiseRelationshipKind } // reused from relationships.ts - geometry-only families only
|
|
846
|
+
| { kind: 'region-measurement'; subjectRegion: string; relatedRegion: string; measurement: ReferenceRequirementMeasurement };
|
|
847
|
+
|
|
848
|
+
// Deliberately NOT a reuse of frontendContracts.ts's ContractTolerance: that
|
|
849
|
+
// type's 'absolute-px' is implicitly runtime/CSS pixels. Reference-image
|
|
850
|
+
// pixels are a distinct, explicitly-labeled unit - nothing here assumes
|
|
851
|
+
// 1 reference pixel = 1 CSS pixel (Prompt 6 will need an explicit mapping).
|
|
852
|
+
type ReferenceRequirementTolerance = { kind: 'exact' } | { kind: 'absolute-reference-px'; amount: number } | { kind: 'percent'; amount: number };
|
|
853
|
+
|
|
854
|
+
interface ExternalReferenceRequirement {
|
|
855
|
+
requirementId: string; // system-computed from {subject, category, expectedDependentMode, tolerance} only - never authored
|
|
856
|
+
category: AuthoredChangeScopeCategory;
|
|
857
|
+
expectedDependentMode?: ExpectedDependentMode;
|
|
858
|
+
subject: ReferenceRequirementSubject;
|
|
859
|
+
tolerance?: ReferenceRequirementTolerance; // required for region-property/region-measurement; must be absent for region-relationship
|
|
860
|
+
}
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
Key rules:
|
|
864
|
+
|
|
865
|
+
- Requirement identity (`requirementId`) is always system-computed
|
|
866
|
+
(`buildReferenceRequirementIdentity`, mirroring
|
|
867
|
+
`frontendContractIdentity.ts#buildClauseIdentity`'s exact shape) - the raw
|
|
868
|
+
authored input (`RawReferenceRequirement`) has no `requirementId` field at
|
|
869
|
+
all, and supplying one is a validation error. Unlike v0.5's
|
|
870
|
+
`BaselineClause`/`PerChangeClause` (which need an author-visible `clauseId`
|
|
871
|
+
for cross-document `supersedesBaselineClauseIds` references), Prompt 3
|
|
872
|
+
requirements have no cross-document reference need yet, so trusting an
|
|
873
|
+
authored id would only invite drift between a user-typed id and the
|
|
874
|
+
content it claims to identify.
|
|
875
|
+
- The reference-side expected value/relationship is never stored on the
|
|
876
|
+
requirement or the artifact - `deriveReferenceRequirementExpectation()` is
|
|
877
|
+
a pure function computed on demand from the artifact's own `regions`,
|
|
878
|
+
eliminating the exact drift risk of persisting e.g. `width: 424` alongside
|
|
879
|
+
a region whose rectangle could (in principle) later disagree with it.
|
|
880
|
+
- A requirement referencing a region id that does not exist in the
|
|
881
|
+
artifact's own `regions` is a **structural validation failure** (rejected
|
|
882
|
+
at construction/import time), never merely "unavailable" reference
|
|
883
|
+
evidence - `isValidReferenceRequirements` checks this before any
|
|
884
|
+
requirement reaches adequacy computation.
|
|
885
|
+
- **Duplicate/conflicting subject rule**: no two requirements in one
|
|
886
|
+
collection may share the same structural subject (same region+property,
|
|
887
|
+
or the same unordered region pair + relationship, or + measurement),
|
|
888
|
+
regardless of category. This single rule covers both "duplicate
|
|
889
|
+
requirement" and "conflicting categories on the same subject" (e.g. the
|
|
890
|
+
same region/property authored as both `requested` and `protected`) -
|
|
891
|
+
v0.5's `evaluateFrontendContract#primitivesConflict` is a *runtime-
|
|
892
|
+
evaluation-time* detector (it needs before/after `ObservationArtifact`
|
|
893
|
+
evidence that does not exist yet at this stage) and could not be reused
|
|
894
|
+
safely; Prompt 3 restricts invalid combinations at authoring time instead,
|
|
895
|
+
per the documented precedent-review outcome.
|
|
896
|
+
- Bounded at `MAX_REFERENCE_REQUIREMENTS` (50) requirements per artifact -
|
|
897
|
+
a maximum capacity, never a required minimum (there is no contract
|
|
898
|
+
requiring any specific number of authored requirements).
|
|
899
|
+
- One new diagnostic code, `invalid-reference-requirement` (error), covers
|
|
900
|
+
every requirement-authoring validation failure - deliberately not split
|
|
901
|
+
further, per the "don't proliferate diagnostics" convention already used
|
|
902
|
+
for `invalid-reference-region`.
|
|
903
|
+
- `import-reference` gained an optional `--requirements-file <json-file>`
|
|
904
|
+
(`{ "requirements": [...] }`, same object-root-wrapper convention as
|
|
905
|
+
`--regions-file`/`--targets-file`); `approve-reference` carries an
|
|
906
|
+
imported artifact's `requirements` forward verbatim (never re-validated,
|
|
907
|
+
never re-derived, never dropped), exactly mirroring how it already
|
|
908
|
+
handles `regions`.
|
|
909
|
+
|
|
910
|
+
**Reference-evidence adequacy** (`deriveReferenceRequirementAdequacy(regions, requirements)`)
|
|
911
|
+
answers only "does the reference definition itself contain enough evidence
|
|
912
|
+
to understand every selected requirement?" - never "does a runtime
|
|
913
|
+
target/candidate exist" (that is Prompt 4/5's responsibility). It is its own
|
|
914
|
+
small, reference-owned vocabulary (`REFERENCE_REQUIREMENT_ADEQUACY_STATES` =
|
|
915
|
+
`'adequate' | 'partial' | 'inadequate'`, and exactly two reason codes,
|
|
916
|
+
`no-selected-requirements` and `missing-reference-relationship-evidence`) -
|
|
917
|
+
deliberately **not** a reuse of
|
|
918
|
+
`boundedAgentContext.ts`'s `Adequacy`/`ADEQUACY_REASON_CODES`, which
|
|
919
|
+
describe runtime-target/static-correlation concerns that do not exist at
|
|
920
|
+
this stage; mislabeling reference adequacy as bounded-agent-context adequacy
|
|
921
|
+
would conflate two genuinely different evidence domains. Zero selected
|
|
922
|
+
requirements is explicitly `inadequate` (a region-rich, fully-valid
|
|
923
|
+
reference is still not usable for a correction task until the user has
|
|
924
|
+
actually selected what matters) - this is a documented product decision,
|
|
925
|
+
not an oversight. The result is never a numeric score, always structured
|
|
926
|
+
and inspectable, with reasons ordered deterministically by authored
|
|
927
|
+
requirement position.
|
|
928
|
+
|
|
929
|
+
```ts
|
|
930
|
+
interface ReferenceRequirementAdequacy {
|
|
931
|
+
status: 'adequate' | 'partial' | 'inadequate';
|
|
932
|
+
totalRequirements: number;
|
|
933
|
+
evaluableRequirements: number;
|
|
934
|
+
unavailableRequirements: number;
|
|
935
|
+
reasons: { code: 'no-selected-requirements' | 'missing-reference-relationship-evidence'; requirementId?: string; detail?: string }[];
|
|
936
|
+
}
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
## v0.7 Prompt 4 reference applicability and candidate-state compatibility
|
|
940
|
+
|
|
941
|
+
Released as `0.7.0`. Additive extension of the Prompt 1/2/3 contracts
|
|
942
|
+
above: one new, optional `applicability?: ExternalReferenceApplicability`
|
|
943
|
+
field on `ExternalReferenceArtifact` (both lifecycle variants), one new,
|
|
944
|
+
optional `explicitState?: ExplicitStateDimensions` field on
|
|
945
|
+
`ObservationArtifact.requestConfig`, and one new pure module,
|
|
946
|
+
`domain/externalReferenceCompatibility.ts`, that answers a single question:
|
|
947
|
+
"does this external reference describe the same frontend state as this
|
|
948
|
+
candidate `ObservationArtifact`?" No schema version bump on either artifact
|
|
949
|
+
- same reasoning as Prompt 2/3's additive fields.
|
|
950
|
+
|
|
951
|
+
**Central distinction**: this is page/state-level compatibility only -
|
|
952
|
+
never geometry, never fidelity, never a visual/pixel comparison, and never
|
|
953
|
+
region-to-runtime-target binding (Prompt 5). It answers "should a
|
|
954
|
+
reference-vs-candidate geometry comparison even be attempted", not "does the
|
|
955
|
+
candidate match the reference". Reference-evidence adequacy (Prompt 3) and
|
|
956
|
+
reference/candidate compatibility (Prompt 4) are deliberately independent:
|
|
957
|
+
a reference can be `adequate` (enough selected requirements to evaluate)
|
|
958
|
+
while simultaneously `incomparable` against a given candidate (wrong
|
|
959
|
+
viewport/theme/state), and vice versa - neither result constrains the
|
|
960
|
+
other.
|
|
961
|
+
|
|
962
|
+
**State identity is always explicit, never inferred.** `theme`,
|
|
963
|
+
`applicationState`, and `authenticatedState` are caller/configuration-
|
|
964
|
+
supplied labels only. The observer never reads screenshot pixels, CSS, DOM
|
|
965
|
+
classes/text, URLs, source code, filenames, accessibility labels,
|
|
966
|
+
localStorage, or cookies to determine state - there is no automatic state
|
|
967
|
+
detection anywhere in this codebase, and Prompt 4 does not add any. Labels
|
|
968
|
+
are bounded opaque identities (`^[A-Za-z0-9_-]{1,64}$`, the same pattern
|
|
969
|
+
already used for target names and region ids) compared by exact,
|
|
970
|
+
case-sensitive string equality only - `"dark"` and `"one-dark"` are
|
|
971
|
+
unrelated labels, never fuzzy-matched or normalized.
|
|
972
|
+
|
|
973
|
+
```ts
|
|
974
|
+
// domain/explicitState.ts - shared by both ObservationArtifact and ExternalReferenceArtifact
|
|
975
|
+
type AuthenticatedState = 'authenticated' | 'unauthenticated'; // closed vocabulary - never a place for credentials/tokens/cookies/session ids
|
|
976
|
+
interface ExplicitStateDimensions {
|
|
977
|
+
theme?: string;
|
|
978
|
+
applicationState?: string;
|
|
979
|
+
authenticatedState?: AuthenticatedState;
|
|
980
|
+
}
|
|
981
|
+
// isValidExplicitStateDimensions requires at least one dimension declared and rejects any unsupported field -
|
|
982
|
+
// this is a bounded, closed shape, never an arbitrary Record<string, unknown> metadata bag.
|
|
983
|
+
|
|
984
|
+
// domain/externalReferenceApplicability.ts
|
|
985
|
+
interface ApplicableViewport { width: number; height: number } // CSS pixels, bounds [200, 3840] mirroring request.ts's own viewport bounds
|
|
986
|
+
interface ExternalReferenceApplicability extends ExplicitStateDimensions {
|
|
987
|
+
viewport?: ApplicableViewport;
|
|
988
|
+
}
|
|
989
|
+
```
|
|
990
|
+
|
|
991
|
+
**Reference image size is never the same concept as applicable viewport.**
|
|
992
|
+
`ExternalReferenceImageReference.width/height` (Prompt 1) describes the
|
|
993
|
+
reference image's own pixel dimensions - a property of the image file,
|
|
994
|
+
detected from its header bytes. `applicability.viewport` describes the
|
|
995
|
+
CSS-pixel runtime viewport the design *represents* - a reference image may
|
|
996
|
+
be captured at any resolution or device-pixel-ratio (e.g. a 1920x1080
|
|
997
|
+
screenshot representing a 960x540 CSS-pixel layout at 2x DPR). Nothing in
|
|
998
|
+
`externalReferenceApplicability.ts` reads or derives a viewport from image
|
|
999
|
+
dimensions; `isValidExternalReferenceApplicability` is its own validator
|
|
1000
|
+
(not a reuse of `isValidExplicitStateDimensions`, whose "at least one
|
|
1001
|
+
dimension" rule would incorrectly reject a viewport-only applicability
|
|
1002
|
+
object).
|
|
1003
|
+
|
|
1004
|
+
**v0.4 comparability is reused, not duplicated.** `domain/comparison.ts`
|
|
1005
|
+
gained four additive reason codes (`viewport-unassessed`, `theme-mismatch`,
|
|
1006
|
+
`authenticated-state-mismatch`, `application-state-mismatch` - the
|
|
1007
|
+
`*-unassessed` codes for theme/authenticated-state/application-state
|
|
1008
|
+
already existed from v0.4) and two optional fields on `ComparabilityReason`
|
|
1009
|
+
(`referenceValue?: string`, `candidateValue?: string`, populated only for a
|
|
1010
|
+
mismatch reason). `domain/comparisonEngine.ts` gained one new exported pure
|
|
1011
|
+
helper, `assessOptionalComparabilityDimension(mismatchCode, unassessedCode,
|
|
1012
|
+
beforeValue, afterValue, mismatchMessage, unassessedMessage)`, extracted
|
|
1013
|
+
from - and now used by - both v0.4's own `evaluateComparability`
|
|
1014
|
+
(Observation-vs-Observation) and the new
|
|
1015
|
+
`evaluateReferenceCandidateCompatibility` (Reference-vs-Observation). The
|
|
1016
|
+
rule is identical either way: both values defined and equal -> no reason;
|
|
1017
|
+
both defined and different -> a `blocking` mismatch reason (with
|
|
1018
|
+
`referenceValue`/`candidateValue` populated); either value undefined ->
|
|
1019
|
+
an `unassessed` reason. This is a genuine, additive improvement to v0.4's
|
|
1020
|
+
own behavior: `evaluateComparability` now assesses theme/authenticated-
|
|
1021
|
+
state/application-state as matching or blocking-mismatched whenever *both*
|
|
1022
|
+
observations declare `requestConfig.explicitState`, rather than always
|
|
1023
|
+
reporting them unassessed - but every historical observation pair (and any
|
|
1024
|
+
pair where either side omits `explicitState`) retains the exact old
|
|
1025
|
+
unassessed-only behavior, verified by the frozen `evaluateComparability`
|
|
1026
|
+
regression test that predates this batch.
|
|
1027
|
+
|
|
1028
|
+
```ts
|
|
1029
|
+
// domain/externalReferenceCompatibility.ts
|
|
1030
|
+
interface ReferenceCandidateCompatibilityResult {
|
|
1031
|
+
referenceId: string;
|
|
1032
|
+
referenceRequestId: string;
|
|
1033
|
+
candidateObservationId: string;
|
|
1034
|
+
candidateRequestId: string;
|
|
1035
|
+
compatibility: ComparabilityResult; // v0.4's own reused result type - state/reasons, never a boolean or a visual score
|
|
1036
|
+
}
|
|
1037
|
+
function evaluateReferenceCandidateCompatibility(reference: ExternalReferenceArtifact, candidate: ObservationArtifact): ReferenceCandidateCompatibilityResult;
|
|
1038
|
+
```
|
|
1039
|
+
|
|
1040
|
+
Key rules:
|
|
1041
|
+
|
|
1042
|
+
- Pure and synchronous - no browser, no filesystem, no network, no target
|
|
1043
|
+
binding. Only `reference.applicability` and
|
|
1044
|
+
`candidate.requestConfig.viewport`/`candidate.requestConfig.explicitState`
|
|
1045
|
+
are consulted; reference regions/requirements are never read here (a
|
|
1046
|
+
distinct, separate concern - see Prompt 3 above).
|
|
1047
|
+
- A dimension the reference constrains but the candidate entirely omits
|
|
1048
|
+
(or vice versa) is `unassessed`, never treated as compatible-by-default
|
|
1049
|
+
and never fabricated as a mismatch - fail-closed, honest non-assessment.
|
|
1050
|
+
- A reference that declares no `applicability` at all produces a fully
|
|
1051
|
+
`unassessed` (never automatically `incomparable`, never automatically
|
|
1052
|
+
`comparable` beyond "no blocking reasons found") result across all four
|
|
1053
|
+
dimensions - Prompt 1/2/3 references remain fully usable, just
|
|
1054
|
+
unassessed for compatibility until applicability is authored.
|
|
1055
|
+
- No automatic persisted compatibility artifact. This is a pure
|
|
1056
|
+
programmatic result, produced on demand by an application/CLI caller
|
|
1057
|
+
that already holds both a reference and a candidate artifact - inventing
|
|
1058
|
+
a new persisted artifact kind for a value this cheap to recompute would
|
|
1059
|
+
add drift risk (a candidate/reference re-imported later could silently
|
|
1060
|
+
disagree with a stale persisted compatibility record) with no
|
|
1061
|
+
corresponding benefit; this may be revisited only if a later prompt's
|
|
1062
|
+
architecture proves persistence necessary.
|
|
1063
|
+
- Identity impact: `buildExternalReferenceRequestIdentity` gained a final
|
|
1064
|
+
optional `applicability` parameter (omitted, never `null`, when absent -
|
|
1065
|
+
byte-identical to Prompt 1/2/3 hashes for every call that doesn't supply
|
|
1066
|
+
it); `buildRequestIdentity` gained a final optional `explicitState`
|
|
1067
|
+
parameter with the identical omission convention. Neither identity
|
|
1068
|
+
function ever takes a file path.
|
|
1069
|
+
- CLI: `import-reference` gained an optional `--applicability-file
|
|
1070
|
+
<json-file>` (the raw, unwrapped applicability object - not a
|
|
1071
|
+
`{ "requirements": [...] }`-style wrapper, since applicability is a
|
|
1072
|
+
single object rather than a named list); `observe` gained an optional
|
|
1073
|
+
`--state-file <json-file>` (the raw, unwrapped `ExplicitStateDimensions`
|
|
1074
|
+
object). Both follow the existing `--scroll-scenario-file` convention
|
|
1075
|
+
exactly: relative paths resolve from the current working directory, the
|
|
1076
|
+
path itself is never persisted or included in any identity, and CLI code
|
|
1077
|
+
owns only flag syntax/file reading/JSON parsing/object-root validation -
|
|
1078
|
+
all semantic validation happens in the domain layer.
|
|
1079
|
+
|
|
1080
|
+
## v0.7 Prompt 5 explicit reference-region <-> runtime-target binding
|
|
1081
|
+
|
|
1082
|
+
Released as `0.7.0`. One new pure domain module,
|
|
1083
|
+
`domain/externalReferenceRuntimeBinding.ts`, answering "which stable
|
|
1084
|
+
observer runtime target, if any, does this candidate observation resolve
|
|
1085
|
+
for each explicitly declared reference region?" No new field is added to
|
|
1086
|
+
either `ExternalReferenceArtifact` or `ObservationArtifact` - both remain
|
|
1087
|
+
exactly as Prompt 4 left them - and no schema version bump on either.
|
|
1088
|
+
|
|
1089
|
+
**Two identity domains, kept strictly separate.** A binding declaration
|
|
1090
|
+
names a Prompt 2 `ReferenceRegion.id` and a v0.2 `NamedTarget.name` (the
|
|
1091
|
+
stable observer runtime target identity established since v0.2 - never a
|
|
1092
|
+
CSS selector, DOM node handle, source file, React component name, or
|
|
1093
|
+
my-dev-kit node id). These two strings living in the same textual namespace
|
|
1094
|
+
never implies a binding - a region id `"header"` and a target name
|
|
1095
|
+
`"header"` bind to each other only because of an explicit declaration, not
|
|
1096
|
+
because the strings match (verified by a dedicated test: the same
|
|
1097
|
+
observation with and without the explicit declaration produces `bound`
|
|
1098
|
+
only in the former case).
|
|
1099
|
+
|
|
1100
|
+
```ts
|
|
1101
|
+
// domain/externalReferenceRuntimeBinding.ts
|
|
1102
|
+
interface ReferenceRuntimeBindingDeclaration {
|
|
1103
|
+
referenceRegion: string; // Prompt 2 ReferenceRegion.id
|
|
1104
|
+
runtimeTarget: string; // v0.2 NamedTarget.name
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
const REFERENCE_RUNTIME_BINDING_STATUSES = ['bound', 'ambiguous', 'unavailable'] as const;
|
|
1108
|
+
|
|
1109
|
+
interface ReferenceRuntimeBindingResult {
|
|
1110
|
+
referenceRegion: string;
|
|
1111
|
+
runtimeTarget: string;
|
|
1112
|
+
status: 'bound' | 'ambiguous' | 'unavailable';
|
|
1113
|
+
reasonCode?: 'runtime-target-not-configured' | 'runtime-target-not-found' | 'runtime-target-ambiguous' | 'runtime-target-evidence-unavailable';
|
|
1114
|
+
detail: string;
|
|
1115
|
+
targetResolutionStatus?: TargetSelectionStatus; // v0.2's own resolution status, when evidence for it exists
|
|
1116
|
+
targetVisible?: boolean; // provenance only - never affects status
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
interface ReferenceRuntimeBindingEvaluation {
|
|
1120
|
+
referenceId: string;
|
|
1121
|
+
referenceRequestId: string;
|
|
1122
|
+
candidateObservationId: string;
|
|
1123
|
+
candidateRequestId: string;
|
|
1124
|
+
compatibility: ComparabilityResult; // reused verbatim from v0.7 Prompt 4
|
|
1125
|
+
bindings: ReferenceRuntimeBindingResult[]; // empty exactly when compatibility.state === 'incomparable'
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
function evaluateReferenceRuntimeBindings(
|
|
1129
|
+
reference: ExternalReferenceArtifact,
|
|
1130
|
+
candidate: ObservationArtifact,
|
|
1131
|
+
declarations: readonly ReferenceRuntimeBindingDeclaration[],
|
|
1132
|
+
): { ok: true; evaluation: ReferenceRuntimeBindingEvaluation } | { ok: false; reason: string };
|
|
1133
|
+
```
|
|
1134
|
+
|
|
1135
|
+
Key rules:
|
|
1136
|
+
|
|
1137
|
+
- **Explicit, never inferred.** A binding declaration is user/configuration
|
|
1138
|
+
input asserting a conceptual correspondence; this module never discovers
|
|
1139
|
+
it from screenshot geometry, matching names, matching text, or source
|
|
1140
|
+
code. There is no automatic-matching algorithm anywhere in this module.
|
|
1141
|
+
- **Reuses, never duplicates.** The compatibility gate reuses
|
|
1142
|
+
`evaluateReferenceCandidateCompatibility` (Prompt 4) verbatim - viewport/
|
|
1143
|
+
theme/application-state/authenticated-state comparison logic is never
|
|
1144
|
+
re-implemented here. Runtime-target resolution reuses `targetPresence`
|
|
1145
|
+
(v0.4 `comparisonEngine.ts`, now additively exported alongside
|
|
1146
|
+
`assessOptionalComparabilityDimension`) - the exact same "how do I read a
|
|
1147
|
+
`TargetEvidenceRecord`'s resolution" rule v0.4's own before/after target
|
|
1148
|
+
comparison already uses. No second target resolver, no browser launch, no
|
|
1149
|
+
Chromium query, no selector evaluation, no live-DOM inspection - this
|
|
1150
|
+
module consumes only an already-captured `ObservationArtifact`'s
|
|
1151
|
+
`requestConfig.targets`/`targetEvidence`.
|
|
1152
|
+
- **Compatibility gates before evaluation, structurally.** If
|
|
1153
|
+
`evaluateReferenceCandidateCompatibility` reports `incomparable`,
|
|
1154
|
+
`bindings` is the empty array and the caller reads the reason from the
|
|
1155
|
+
embedded `compatibility` field - there is no binding-local "incompatible"
|
|
1156
|
+
status; Prompt 4's compatibility result is represented exactly once, not
|
|
1157
|
+
duplicated into a parallel vocabulary.
|
|
1158
|
+
- **Two-layer validation.** Reference-region existence, declaration shape,
|
|
1159
|
+
bounds, and duplicate/conflict rules are validated structurally against
|
|
1160
|
+
the `ExternalReferenceArtifact` alone (`isValidReferenceRuntimeBindingDeclarations`)
|
|
1161
|
+
- independent of any candidate, mirroring Prompt 3's "unknown region
|
|
1162
|
+
reference is a structural validation failure" precedent exactly: a
|
|
1163
|
+
declaration naming a nonexistent reference region, or any reference with
|
|
1164
|
+
no `regions` declared at all, fails the whole evaluation closed before a
|
|
1165
|
+
candidate is even considered. Runtime-target availability, by contrast,
|
|
1166
|
+
is evaluated per-candidate inside `evaluateReferenceRuntimeBindings`
|
|
1167
|
+
itself, since the same declaration can be `bound` against one candidate
|
|
1168
|
+
and `unavailable` against another.
|
|
1169
|
+
- **Duplicate/conflicting-declaration rule** (mirrors Prompt 3's
|
|
1170
|
+
requirement-subject uniqueness rule): no two declarations may name the
|
|
1171
|
+
same `referenceRegion` (case-insensitively), whether they agree on
|
|
1172
|
+
`runtimeTarget` (an exact duplicate) or disagree (a conflict) - both fail
|
|
1173
|
+
the same way, never silently resolved by keeping the first. The reverse -
|
|
1174
|
+
several distinct reference regions naming the same `runtimeTarget` - is
|
|
1175
|
+
deliberately allowed (e.g. two design sub-regions legitimately
|
|
1176
|
+
corresponding to one runtime container element).
|
|
1177
|
+
- **Target-resolution-state handling.** `targetPresence`'s four outcomes
|
|
1178
|
+
map onto binding status as: `matched` -> `bound`; `ambiguous` -> `ambiguous`
|
|
1179
|
+
(the candidate's own configured target resolved ambiguously - never
|
|
1180
|
+
reported bound even though its stable name exists); `not-found` ->
|
|
1181
|
+
`unavailable` (`runtime-target-not-found` - the target was configured but
|
|
1182
|
+
the resolver found nothing on the page); no usable resolution evidence at
|
|
1183
|
+
all -> `unavailable` (`runtime-target-evidence-unavailable`). A declared
|
|
1184
|
+
`runtimeTarget` that was never part of the candidate's configured target
|
|
1185
|
+
set at all is a fifth, CLI/config-boundary-only outcome -> `unavailable`
|
|
1186
|
+
(`runtime-target-not-configured`) - never a dynamic page search.
|
|
1187
|
+
- **Hidden-target decision.** A uniquely resolved (`matched`) but hidden
|
|
1188
|
+
target is still reported `bound` - visibility never changes `status`.
|
|
1189
|
+
`targetVisible` (from the existing `TargetVisibility` evidence, when
|
|
1190
|
+
available) is carried as provenance only. Binding identity (does a stable
|
|
1191
|
+
correspondence exist) and later fidelity evaluability (can this evidence
|
|
1192
|
+
actually be used to check the design) are treated as distinct questions;
|
|
1193
|
+
this prompt answers only the former.
|
|
1194
|
+
- **Not every region needs a binding.** `isValidReferenceRuntimeBindingDeclarations`
|
|
1195
|
+
never requires full region coverage - a reference may have regions no
|
|
1196
|
+
declaration names at all (they simply have no bound runtime target for
|
|
1197
|
+
this candidate). This is not a completeness gate; Prompt 6 (or later) may
|
|
1198
|
+
add one for the regions that selected requirements actually need.
|
|
1199
|
+
- **No persisted artifact family.** `evaluateReferenceRuntimeBindings` is a
|
|
1200
|
+
pure, on-demand function over an already-persisted reference, an
|
|
1201
|
+
already-persisted candidate observation, and an in-memory declaration
|
|
1202
|
+
collection. No `ExternalReferenceBindingArtifact` (or equivalent) is
|
|
1203
|
+
introduced - the same "cheap to recompute, persisting invites drift"
|
|
1204
|
+
reasoning Prompt 4 already applied to its own compatibility result.
|
|
1205
|
+
Neither the reference nor the observation artifact is ever rewritten to
|
|
1206
|
+
carry a binding result: a design reference may later be evaluated against
|
|
1207
|
+
several different candidates, and one observation may be evaluated
|
|
1208
|
+
against several different references, so binding is kept as downstream,
|
|
1209
|
+
candidate-specific, reference-specific derived evidence rather than
|
|
1210
|
+
mutating either immutable source artifact.
|
|
1211
|
+
- **No new identity function.** Unlike `buildRequestIdentity`/
|
|
1212
|
+
`buildExternalReferenceRequestIdentity`, no hash-based logical identity is
|
|
1213
|
+
computed for a binding declaration or its evaluated result - there is no
|
|
1214
|
+
persistence and no cross-document reference-by-id need yet (mirroring
|
|
1215
|
+
Prompt 4's `ReferenceCandidateCompatibilityResult`, which took the same
|
|
1216
|
+
approach). Provenance is instead carried directly as plain fields
|
|
1217
|
+
(`referenceId`, `referenceRequestId`, `candidateObservationId`,
|
|
1218
|
+
`candidateRequestId`, plus each result's own `referenceRegion`/
|
|
1219
|
+
`runtimeTarget`) - already deterministic, already sufficient for a caller
|
|
1220
|
+
to trace every result back to its inputs, without inventing a fifth
|
|
1221
|
+
identity-hashing convention for a value this prompt does not persist.
|
|
1222
|
+
- **Deterministic ordering.** `bindings` preserves authored declaration
|
|
1223
|
+
order (mirroring the "authored order is semantic" convention already used
|
|
1224
|
+
for regions/requirements) rather than sorting by any derived key.
|
|
1225
|
+
- **Bounded.** `MAX_REFERENCE_RUNTIME_BINDINGS` (20) caps the declaration
|
|
1226
|
+
collection, mirroring `MAX_REFERENCE_REGIONS`.
|
|
1227
|
+
- **No public CLI surface yet.** Only the programmatic
|
|
1228
|
+
`evaluateReferenceRuntimeBindings`/`isValidReferenceRuntimeBindingDeclarations`
|
|
1229
|
+
functions are exported. A standalone CLI command was deliberately not
|
|
1230
|
+
added merely for symmetry with `import-reference`/`observe`; Prompt 6
|
|
1231
|
+
(structured fidelity evaluation) is expected to become the first concrete
|
|
1232
|
+
consumer and public-surface owner for this capability.
|
|
1233
|
+
|
|
1234
|
+
## v0.7 Prompt 6 structured reference-vs-candidate fidelity evaluation
|
|
1235
|
+
|
|
1236
|
+
Released as `0.7.0`. One new pure domain module,
|
|
1237
|
+
`domain/externalReferenceFidelity.ts`, and its CLI-facing counterpart,
|
|
1238
|
+
`application/referenceFidelityEvaluationService.ts` plus the new
|
|
1239
|
+
`evaluate-reference-fidelity` CLI command - the first point in this whole
|
|
1240
|
+
v0.7 stack where a reference's authored expectation is actually compared
|
|
1241
|
+
against live candidate evidence. No new artifact field, no schema version
|
|
1242
|
+
bump: this prompt reuses Prompt 1-5's artifacts and result types entirely.
|
|
1243
|
+
|
|
1244
|
+
```ts
|
|
1245
|
+
// domain/externalReferenceFidelity.ts
|
|
1246
|
+
const REFERENCE_REQUIREMENT_FIDELITY_STATUSES = ['pass', 'fail', 'unavailable'] as const;
|
|
1247
|
+
const REFERENCE_FIDELITY_STATES = ['not-evaluated', 'pass', 'fail'] as const;
|
|
1248
|
+
const REFERENCE_FIDELITY_BLOCK_REASONS = ['reference-inadequate', 'incompatible'] as const;
|
|
1249
|
+
|
|
1250
|
+
interface ReferenceRequirementFidelityResult {
|
|
1251
|
+
requirementId: string;
|
|
1252
|
+
category: AuthoredChangeScopeCategory;
|
|
1253
|
+
expectedDependentMode?: ExpectedDependentMode;
|
|
1254
|
+
subject: ReferenceRequirementSubject;
|
|
1255
|
+
boundRuntimeTargets: string[];
|
|
1256
|
+
status: 'pass' | 'fail' | 'unavailable';
|
|
1257
|
+
reasonCode?: 'reference-evidence-unavailable' | 'reference-relationship-not-exhibited' | 'binding-unavailable' | 'candidate-evidence-unavailable' | 'coordinate-mapping-unavailable'; // present iff status === 'unavailable'
|
|
1258
|
+
detail?: string;
|
|
1259
|
+
// region-property/region-measurement subjects only:
|
|
1260
|
+
referenceValue?: number; // reference-image pixels
|
|
1261
|
+
candidateRawValue?: number; // CSS pixels, as captured
|
|
1262
|
+
candidateValue?: number; // candidateRawValue converted into reference-image-pixel space
|
|
1263
|
+
delta?: number; // candidateValue - referenceValue
|
|
1264
|
+
tolerance?: ReferenceRequirementTolerance;
|
|
1265
|
+
// region-relationship subjects only:
|
|
1266
|
+
expectedRelationship?: PairwiseRelationshipKind;
|
|
1267
|
+
actualRelationship?: PairwiseRelationshipKind;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
interface ReferenceCandidateFidelityEvaluation {
|
|
1271
|
+
referenceId: string;
|
|
1272
|
+
referenceRequestId: string;
|
|
1273
|
+
candidateObservationId: string;
|
|
1274
|
+
candidateRequestId: string;
|
|
1275
|
+
adequacy: ReferenceRequirementAdequacy; // reused verbatim from Prompt 3
|
|
1276
|
+
compatibility?: ComparabilityResult; // reused verbatim from Prompt 4; absent only when adequacy itself is inadequate
|
|
1277
|
+
bindings?: ReferenceRuntimeBindingEvaluation; // reused verbatim from Prompt 5; absent when an earlier gate blocked
|
|
1278
|
+
state: 'not-evaluated' | 'pass' | 'fail';
|
|
1279
|
+
blockedBy?: 'reference-inadequate' | 'incompatible'; // present iff state === 'not-evaluated'
|
|
1280
|
+
requirementResults: ReferenceRequirementFidelityResult[]; // empty iff state === 'not-evaluated'
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
function evaluateReferenceCandidateFidelity(
|
|
1284
|
+
reference: ExternalReferenceArtifact,
|
|
1285
|
+
candidate: ObservationArtifact,
|
|
1286
|
+
bindingDeclarations: readonly ReferenceRuntimeBindingDeclaration[],
|
|
1287
|
+
options?: { geometryTolerancePx?: number },
|
|
1288
|
+
): { ok: true; evaluation: ReferenceCandidateFidelityEvaluation } | { ok: false; reason: string };
|
|
1289
|
+
```
|
|
1290
|
+
|
|
1291
|
+
**Result vocabulary.** `pass`/`fail`/`unavailable` is reused from v0.5's
|
|
1292
|
+
`CLAUSE_RESULT_STATUSES` shape (the same honest three-state idea: a result
|
|
1293
|
+
either satisfies its condition, fails it, or cannot be evaluated - never a
|
|
1294
|
+
score) but is its own independently-owned constant, deliberately excluding
|
|
1295
|
+
v0.5's fourth member, `'conflict'` - Prompt 6 has no cross-requirement
|
|
1296
|
+
authoring-conflict concept (each requirement is evaluated independently
|
|
1297
|
+
against its own subject), so reusing `conflict` would invite a status this
|
|
1298
|
+
prompt can never actually produce.
|
|
1299
|
+
|
|
1300
|
+
**Evaluation order (frozen, never reordered):** reference structural
|
|
1301
|
+
validation -> candidate structural validation -> binding-declaration
|
|
1302
|
+
structural validation -> Prompt 3 reference adequacy -> Prompt 4
|
|
1303
|
+
compatibility -> Prompt 5 binding evaluation -> per-requirement candidate-
|
|
1304
|
+
evidence/coordinate-mapping checks -> per-requirement tolerance/
|
|
1305
|
+
relationship comparison -> overall result. The first three (structural
|
|
1306
|
+
validation) failures return `{ ok: false, reason }` - a caller/config error,
|
|
1307
|
+
never a fidelity outcome. The next two (adequacy `inadequate`, compatibility
|
|
1308
|
+
`incomparable`) short-circuit to `state: 'not-evaluated'` with an empty
|
|
1309
|
+
`requirementResults` - an earlier blocking gate never lets an ordinary
|
|
1310
|
+
PASS/FAIL requirement set get fabricated past it. `adequacy` "partial" (some,
|
|
1311
|
+
but not all, authored requirements individually unavailable) does **not**
|
|
1312
|
+
block evaluation - it proceeds normally, and the individual unavailable
|
|
1313
|
+
reference-side requirements simply also report `unavailable` at the
|
|
1314
|
+
per-requirement level (their own reference-evidence problem, re-derived
|
|
1315
|
+
identically by `evaluateOneRequirement`, not looked up from the adequacy
|
|
1316
|
+
result).
|
|
1317
|
+
|
|
1318
|
+
**Coordinate mapping - the central problem this prompt solves.** Prompt 2
|
|
1319
|
+
regions and Prompt 3 tolerances are authored in reference-image pixels;
|
|
1320
|
+
`ObservationArtifact` target geometry is CSS pixels. This module establishes
|
|
1321
|
+
exactly one explicit, deterministic scale from `reference.applicability.viewport`
|
|
1322
|
+
(the CSS-pixel runtime viewport, Prompt 4) and the reference image's own
|
|
1323
|
+
pixel dimensions (Prompt 1) - `scaleX = imageWidth / viewportWidth`,
|
|
1324
|
+
`scaleY = imageHeight / viewportHeight` - and converts every candidate
|
|
1325
|
+
measurement into reference-image-pixel space before comparing it against a
|
|
1326
|
+
Prompt 3 tolerance. It never assumes 1 reference-image pixel equals 1 CSS
|
|
1327
|
+
pixel, and it never performs cropping, offset, rotation, or perspective
|
|
1328
|
+
registration - only a deliberately bounded full-frame mapping. `scaleX`/
|
|
1329
|
+
`scaleY` must agree within a small, independently-owned coordinate-mapping-
|
|
1330
|
+
validity tolerance (1% relative, never a user-authored design tolerance) or
|
|
1331
|
+
the mapping is rejected outright; a reference with no applicable viewport at
|
|
1332
|
+
all likewise has no mapping. Either way, every numeric (`region-property`/
|
|
1333
|
+
`region-measurement`) requirement becomes `unavailable`/`coordinate-mapping-unavailable`
|
|
1334
|
+
- categorical `region-relationship` requirements are unaffected (they never
|
|
1335
|
+
need a scale). Horizontal fields (`x`/`width`/`right`/`centerX` and the
|
|
1336
|
+
horizontal measurements) always scale by `scaleX`; vertical fields (`y`/
|
|
1337
|
+
`height`/`bottom`/`centerY` and the vertical measurements) always scale by
|
|
1338
|
+
`scaleY` - this falls out automatically from converting a full
|
|
1339
|
+
`TargetGeometry` into a `ReferenceRegionGeometry`-shaped value per axis,
|
|
1340
|
+
never a hand-picked per-property axis table.
|
|
1341
|
+
|
|
1342
|
+
**Tolerance is reused exactly, never redefined.** A single rule -
|
|
1343
|
+
`abs(delta) <= allowedAmount` - covers all three Prompt 3 tolerance kinds:
|
|
1344
|
+
`exact` is simply the zero-tolerance case (`allowedAmount = 0`);
|
|
1345
|
+
`absolute-reference-px` uses its authored `amount` directly (already in
|
|
1346
|
+
reference-image pixels); `percent`'s denominator is `Math.abs(referenceValue)`,
|
|
1347
|
+
mirroring v0.5's own `toleranceToPx` "may vary by up to N%" convention
|
|
1348
|
+
exactly (independently reimplemented in reference-image-pixel units, never
|
|
1349
|
+
imported - `frontendContractEvaluation.ts`'s `ContractTolerance` is a
|
|
1350
|
+
different, CSS-pixel-implicit unit). No hidden epsilon is added anywhere;
|
|
1351
|
+
subpixel precision is preserved through to the final comparison, so a
|
|
1352
|
+
tolerance-boundary value (e.g. delta exactly equal to the allowed amount)
|
|
1353
|
+
passes and one unit past it fails, exactly as authored.
|
|
1354
|
+
|
|
1355
|
+
**Region-property evaluation** reads `TargetGeometry` from the bound
|
|
1356
|
+
target's `targetEvidence` entry, converts it into a `ReferenceRegionGeometry`-
|
|
1357
|
+
shaped value (adding `centerX`/`centerY`, computed identically to
|
|
1358
|
+
`deriveReferenceRegionGeometry`) both raw (CSS) and scaled (reference-image
|
|
1359
|
+
pixels), and reads `[subject.property]` off each - `candidateRawValue`
|
|
1360
|
+
(CSS) and `candidateValue` (reference-image pixels) are both reported.
|
|
1361
|
+
|
|
1362
|
+
**Region-measurement evaluation** converts *both* bound targets' geometries
|
|
1363
|
+
the same way and calls the existing `deriveReferenceRequirementMeasurement`
|
|
1364
|
+
(Prompt 3) on the converted geometries directly - reusing Prompt 3's exact
|
|
1365
|
+
gap/delta formulas rather than reimplementing a parallel "runtime version"
|
|
1366
|
+
of them, and never inventing a generic geometry expression language. A
|
|
1367
|
+
geometrically-undefined gap (the two targets overlap on the relevant axis)
|
|
1368
|
+
is `unavailable`, mirroring Prompt 3's own reference-side treatment of the
|
|
1369
|
+
identical situation.
|
|
1370
|
+
|
|
1371
|
+
**Region-relationship evaluation** first confirms the reference itself
|
|
1372
|
+
actually exhibits its own selected relationship (`deriveReferenceRequirementExpectation`'s
|
|
1373
|
+
`matches` field) - if not, the result is `unavailable`/
|
|
1374
|
+
`reference-relationship-not-exhibited` (a reference-authoring problem, never
|
|
1375
|
+
a candidate `fail`). It then resolves both bound targets and calls the
|
|
1376
|
+
canonical `deriveLayoutRelationships` (v0.4) over the *whole* candidate
|
|
1377
|
+
observation - never a second, parallel relationship formula - and looks up
|
|
1378
|
+
the pairwise record for the bound target pair **scoped to the exact
|
|
1379
|
+
requested relationship family** (an independently-owned, third duplicate of
|
|
1380
|
+
the same `RELATIONSHIP_FAMILY_GROUPS` shape already used by
|
|
1381
|
+
`frontendContractEvaluation.ts` and `externalReferenceRequirements.ts` -
|
|
1382
|
+
this is the same real bug class Prompt 3 fixed: matching the first record
|
|
1383
|
+
for a target pair regardless of family would silently compare against the
|
|
1384
|
+
wrong relationship kind). A record only derivable in the reversed target
|
|
1385
|
+
order is `unavailable`, never auto-flipped - identical to Prompt 3's own
|
|
1386
|
+
reference-side handling of the same situation. `pass` requires the
|
|
1387
|
+
candidate's actual relationship kind to equal the requirement's authored
|
|
1388
|
+
`relationship` exactly.
|
|
1389
|
+
|
|
1390
|
+
**Binding gate.** Every subject's dependent reference region(s) must have a
|
|
1391
|
+
`bound` (never `ambiguous`/`unavailable`, and never simply absent from the
|
|
1392
|
+
supplied declarations) Prompt 5 binding result, or the requirement is
|
|
1393
|
+
`unavailable`/`binding-unavailable` - this module never guesses another
|
|
1394
|
+
target and never auto-binds based on geometry or names. A `bound` target
|
|
1395
|
+
that is not visible (`TargetVisibility.visible !== true`, including when
|
|
1396
|
+
visibility evidence itself is unavailable) is treated as having no usable
|
|
1397
|
+
geometry - `unavailable`/`candidate-evidence-unavailable` - preserving the
|
|
1398
|
+
distinction between "binding succeeded" (Prompt 5's question) and "this
|
|
1399
|
+
evidence is usable for fidelity evaluation" (this prompt's question): a
|
|
1400
|
+
hidden-but-uniquely-resolved target still has a stable identity, but its
|
|
1401
|
+
geometry is never treated as meaningful for a numeric/relationship
|
|
1402
|
+
comparison.
|
|
1403
|
+
|
|
1404
|
+
**Categories are preserved, never given different PASS/FAIL rules.**
|
|
1405
|
+
`category`/`expectedDependentMode` are carried through to each result as
|
|
1406
|
+
provenance only; Prompt 3 never implemented a `required`-vs-`permitted`
|
|
1407
|
+
directional evaluation difference for its own expectation/adequacy
|
|
1408
|
+
derivation (unlike v0.5's runtime-directional contract clauses), so Prompt 6
|
|
1409
|
+
does not invent one now - every requirement in the reference's authored
|
|
1410
|
+
collection is evaluated by the identical rule and counts identically toward
|
|
1411
|
+
the overall result, regardless of category.
|
|
1412
|
+
|
|
1413
|
+
**Overall fidelity result.** For an evaluated (non-blocked) pair, `state`
|
|
1414
|
+
is `'pass'` only when every requirement result is `'pass'`; any `'fail'` or
|
|
1415
|
+
`'unavailable'` result forces `state: 'fail'` - there is no meaningful third
|
|
1416
|
+
overall bucket once evaluation has actually run, since "some/all
|
|
1417
|
+
unavailable" and "some/all fail" both equally mean "not every selected
|
|
1418
|
+
requirement is confirmed satisfied." A reference with zero selected
|
|
1419
|
+
requirements never reaches this stage at all - it is `inadequate` (Prompt
|
|
1420
|
+
3's own zero-requirements rule) and therefore `not-evaluated`, never a
|
|
1421
|
+
meaningless `pass`.
|
|
1422
|
+
|
|
1423
|
+
**Persistence decision: none.** `evaluateReferenceCandidateFidelity` (and
|
|
1424
|
+
its CLI-facing wrapper, `evaluateReferenceCandidateFidelityFromArtifactRoots`)
|
|
1425
|
+
is a pure, on-demand function over already-persisted/in-memory evidence -
|
|
1426
|
+
no new `ExternalReferenceFidelityEvaluationArtifact` (or equivalent) is
|
|
1427
|
+
introduced. Rationale, identical to Prompt 4/5's own precedent: the result
|
|
1428
|
+
is cheap to recompute deterministically from its inputs (a reference, a
|
|
1429
|
+
candidate, and a caller-supplied binding-declaration collection), and
|
|
1430
|
+
persisting it would invite drift with no corresponding benefit at this
|
|
1431
|
+
stage; this may be revisited only if Prompt 7's architecture proves
|
|
1432
|
+
persistence necessary.
|
|
1433
|
+
|
|
1434
|
+
**CLI**: `evaluate-reference-fidelity --reference <root> --candidate <root>
|
|
1435
|
+
[--bindings-file <json-file>] [--enforce]` - the CLI surface Prompt 5
|
|
1436
|
+
deliberately deferred. `--bindings-file` follows the exact
|
|
1437
|
+
`--requirements-file`/`--regions-file` wrapped-object convention
|
|
1438
|
+
(`{ "bindings": [...] }`); CLI code owns only flag syntax/file reading/JSON
|
|
1439
|
+
parsing/root-shape validation, with every binding-declaration rule staying
|
|
1440
|
+
owned by `isValidReferenceRuntimeBindingDeclarations`. `--enforce` mirrors
|
|
1441
|
+
`evaluate-contract`'s exact precedent: it changes only the process exit
|
|
1442
|
+
status for an already-computed `state: 'fail'` result, never the printed
|
|
1443
|
+
content - and has no effect on `not-evaluated`, which always exits 0 (a
|
|
1444
|
+
compatibility/adequacy blocker is a successful, structured, honest
|
|
1445
|
+
non-evaluation, never an execution error and never a design mismatch).
|
|
1446
|
+
Persists nothing; there is no `--output` flag.
|
|
1447
|
+
|
|
1448
|
+
## v0.7 Prompt 7 bounded reference-fidelity projection and v0.6 bounded-agent-context integration
|
|
1449
|
+
|
|
1450
|
+
Released as `0.7.0`. Additive extension of the v0.6 bounded-agent-context
|
|
1451
|
+
contract above and of the v0.7 Prompt 6 fidelity evaluator - no new bounded-
|
|
1452
|
+
context artifact family, no second visual-context system, no schema version
|
|
1453
|
+
bump (`BOUNDED_AGENT_CONTEXT_SCHEMA_VERSION` stays `1.0.0`, following the
|
|
1454
|
+
exact precedent already set when `correlations?` was added in v0.6 Batch 3).
|
|
1455
|
+
|
|
1456
|
+
**Chosen integration owner.** `projectBoundedAgentContext` itself gains one
|
|
1457
|
+
new optional input (`fidelity?: ReferenceCandidateFidelityEvaluation`, plus
|
|
1458
|
+
`fidelityRequired?: boolean`) rather than a separate `VisualAgentContext`/
|
|
1459
|
+
`VisualPromptPacket`/`ReferencePromptBuilder`. This was chosen over a pure
|
|
1460
|
+
post-hoc "attach" step (the shape `attachRuntimeStaticCorrelations` uses)
|
|
1461
|
+
because fidelity-relevant runtime targets must compete fairly for
|
|
1462
|
+
`MAX_RUNTIME_TARGETS` capacity and receive the exact same geometry/
|
|
1463
|
+
visibility/screenshot assembly contract-clause-derived targets already get -
|
|
1464
|
+
an attach-only step run after target allocation could never produce that. A
|
|
1465
|
+
new pure module, `domain/referenceFidelityProjection.ts`
|
|
1466
|
+
(`projectReferenceFidelity`), derives the bounded, prioritized fidelity
|
|
1467
|
+
content plus the target-id/omission/truncation contributions
|
|
1468
|
+
`projectBoundedAgentContext` folds into its own existing pipeline - it is
|
|
1469
|
+
not a second fidelity-evaluation engine, only a selection over Prompt 6's
|
|
1470
|
+
already-computed result.
|
|
1471
|
+
|
|
1472
|
+
```ts
|
|
1473
|
+
// domain/boundedAgentContext.ts - additive
|
|
1474
|
+
interface BoundedAgentContextSourceReferences {
|
|
1475
|
+
// ...unchanged fields...
|
|
1476
|
+
referenceId?: string; // new, optional
|
|
1477
|
+
referenceRequestId?: string; // new, optional
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
const MAX_FIDELITY_MISMATCHES = 15;
|
|
1481
|
+
const MAX_FIDELITY_PROTECTED_CONTEXT = 10; // reuses MAX_RELATIONSHIP_EVIDENCE_PER_TARGET's value
|
|
1482
|
+
|
|
1483
|
+
interface BoundedReferenceFidelityProjection {
|
|
1484
|
+
referenceId: string;
|
|
1485
|
+
referenceRequestId: string;
|
|
1486
|
+
candidateObservationId: string;
|
|
1487
|
+
candidateRequestId: string;
|
|
1488
|
+
adequacy: ReferenceRequirementAdequacy; // reused verbatim from Prompt 3
|
|
1489
|
+
compatibility?: ComparabilityResult; // reused verbatim from Prompt 4
|
|
1490
|
+
state: ReferenceFidelityState; // reused verbatim from Prompt 6
|
|
1491
|
+
blockedBy?: ReferenceFidelityBlockReason; // reused verbatim from Prompt 6
|
|
1492
|
+
mismatches: ReferenceRequirementFidelityResult[]; // bounded, prioritized non-pass requirements (Prompt 6 type, unmodified)
|
|
1493
|
+
protectedContext: ReferenceRequirementFidelityResult[]; // bounded passing protected/preserved requirements, as "do not break this" context
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
interface BoundedAgentContextArtifact {
|
|
1497
|
+
// ...unchanged fields...
|
|
1498
|
+
fidelity?: BoundedReferenceFidelityProjection; // new, optional - mirrors `correlations?`'s own additive precedent exactly
|
|
1499
|
+
}
|
|
1500
|
+
```
|
|
1501
|
+
|
|
1502
|
+
**Selection policy** (`domain/referenceFidelityProjection.ts#projectReferenceFidelity`):
|
|
1503
|
+
only Prompt 6's non-`pass` requirement results are ever candidates for
|
|
1504
|
+
`mismatches` - passing requirements are never dumped by default, satisfying
|
|
1505
|
+
this prompt's "bounded coding-agent use" design goal. Each candidate is
|
|
1506
|
+
classified into a tier by its authored category/mode, reusing
|
|
1507
|
+
`boundedAgentContextProjection.ts#clauseTier`'s exact rule (duplicated, not
|
|
1508
|
+
imported, per this repository's established per-module small-helper
|
|
1509
|
+
convention - never a reference-specific protected/preserved taxonomy):
|
|
1510
|
+
`protected`/`preserved` are always `required`; `expected-dependent` is
|
|
1511
|
+
`required` only in `'required'` mode; `requested` and `expected-dependent`/
|
|
1512
|
+
`'permitted'` are `optional`.
|
|
1513
|
+
|
|
1514
|
+
**Priority policy**: 1) `fail` + `required` tier, 2) `unavailable` +
|
|
1515
|
+
`required` tier, 3) any other non-`pass` (optional-tier) result. Within one
|
|
1516
|
+
priority class, Prompt 6's own authored requirement order is preserved (a
|
|
1517
|
+
stable sort by priority rank only) - never re-ranked by an opaque score.
|
|
1518
|
+
The final `mismatches` array is reported in priority order (highest first),
|
|
1519
|
+
not restored to authored order, since the whole point of prioritization is
|
|
1520
|
+
that the most actionable evidence appears first when the set is large.
|
|
1521
|
+
|
|
1522
|
+
**Cap values**: `MAX_FIDELITY_MISMATCHES = 15` and
|
|
1523
|
+
`MAX_FIDELITY_PROTECTED_CONTEXT = 10` (reusing
|
|
1524
|
+
`MAX_RELATIONSHIP_EVIDENCE_PER_TARGET`'s value) - both judgment-call bounds
|
|
1525
|
+
in the same spirit as v0.6 Batch 1's own frozen caps (no measured fixture
|
|
1526
|
+
corpus exists yet for either concept).
|
|
1527
|
+
|
|
1528
|
+
**Omission/truncation behavior**: reuses `OmissionRecord`/`TruncationRecord`
|
|
1529
|
+
wholesale, no second reporting model. When mismatches exceed the cap, a
|
|
1530
|
+
`{subject: 'fidelity-mismatches', limit, actualCount, required}` truncation
|
|
1531
|
+
is recorded, plus one `{subject: 'fidelity-mismatch:<requirementId>',
|
|
1532
|
+
reason: 'required-evidence-lost-by-bound', required: true}` omission for
|
|
1533
|
+
*each* dropped required-tier mismatch (optional-tier drops are truncated
|
|
1534
|
+
but never separately omitted as "required loss", since they were never
|
|
1535
|
+
required). `protectedContext` truncation is always `required: false` - it
|
|
1536
|
+
is confirmatory/passing context, never a design-fidelity failure. These
|
|
1537
|
+
records are folded into `projectBoundedAgentContext`'s own `omissions`/
|
|
1538
|
+
`truncations` arrays *before* its existing aggregate `capOmissions`/
|
|
1539
|
+
`capTruncations` calls and its existing adequacy computation run - fidelity
|
|
1540
|
+
loss is never a separate adequacy code path, it simply participates in the
|
|
1541
|
+
exact same `anyRequiredLoss`/`anyOptionalLoss` rule every other evidence
|
|
1542
|
+
source already uses.
|
|
1543
|
+
|
|
1544
|
+
**Adequacy behavior**: a `not-evaluated` fidelity (blocked by Prompt 6's own
|
|
1545
|
+
`reference-inadequate`/`incompatible` gates) is never converted into "no
|
|
1546
|
+
problems" - `projectReferenceFidelity` records an explicit
|
|
1547
|
+
`{subject: 'fidelity', reason: 'unsupported-or-unavailable', required,
|
|
1548
|
+
detail}` omission, where `required` defaults to `true` (supplying a
|
|
1549
|
+
fidelity evaluation to be projected at all is itself the signal that the
|
|
1550
|
+
task depends on it, mirroring `CorrelationTargetInput.required`'s existing
|
|
1551
|
+
v0.6 convention - callers who want fidelity as purely incidental context set
|
|
1552
|
+
`fidelityRequired: false`). A `required: true` fidelity omission, folded
|
|
1553
|
+
into the existing adequacy computation, prevents `adequacy.state` from
|
|
1554
|
+
remaining `'adequate'` (it becomes `'partial'`, or `'inadequate'` when
|
|
1555
|
+
combined with other required loss reaching the existing threshold) - it is
|
|
1556
|
+
never silently ignored. A `required: false` omission can degrade adequacy
|
|
1557
|
+
to at most `'partial'`, per v0.6's own pre-existing "optional-only loss
|
|
1558
|
+
never means inadequate" rule - unchanged, not redefined. A `pass` fidelity
|
|
1559
|
+
result contributes no omissions/truncations at all and never degrades
|
|
1560
|
+
adequacy.
|
|
1561
|
+
|
|
1562
|
+
**Not-evaluated fidelity behavior**: preserved exactly as Prompt 6 reported
|
|
1563
|
+
it - `fidelity.state`/`fidelity.blockedBy` on the output artifact are a
|
|
1564
|
+
direct pass-through of Prompt 6's own values, with `mismatches`/
|
|
1565
|
+
`protectedContext` both empty (there is nothing to select from an empty
|
|
1566
|
+
`requirementResults`).
|
|
1567
|
+
|
|
1568
|
+
**Per-target organization**: every fidelity mismatch's `boundRuntimeTargets`
|
|
1569
|
+
(Prompt 5/6's own field, never truncated) becomes a required- or permitted-
|
|
1570
|
+
tier addition to `projectBoundedAgentContext`'s existing target-id sets,
|
|
1571
|
+
so those runtime targets receive full `BoundedRuntimeTargetProjection`
|
|
1572
|
+
treatment (geometry/visibility/overflow/scrollOwner/screenshotRef) through
|
|
1573
|
+
the exact existing assembly code - no duplicated target-projection logic.
|
|
1574
|
+
Reference regions are never used as a correlation or target-selection key;
|
|
1575
|
+
only the already-bound stable v0.2 runtime target ids are.
|
|
1576
|
+
|
|
1577
|
+
**Multi-target relationship representation**: a `region-relationship`
|
|
1578
|
+
mismatch's `boundRuntimeTargets` array (already carrying both bound
|
|
1579
|
+
targets, from Prompt 6) is used as-is - both targets are added to the
|
|
1580
|
+
required/permitted set, so both appear in `targets`. Nothing collapses a
|
|
1581
|
+
two-target relationship failure onto a single target.
|
|
1582
|
+
|
|
1583
|
+
**Static-correlation reuse**: entirely unchanged. `deriveRuntimeStaticCorrelations`/
|
|
1584
|
+
`attachRuntimeStaticCorrelations` are not modified, not called from within
|
|
1585
|
+
this prompt's new code, and remain the caller's own separate step -
|
|
1586
|
+
`BoundedRuntimeTargetProjection.targetId`/`RuntimeStaticCorrelationRecord.runtimeTargetId`
|
|
1587
|
+
already share the same stable v0.2 identity a fidelity mismatch's
|
|
1588
|
+
`boundRuntimeTargets` also uses, so a caller (Prompt 8) joins fidelity,
|
|
1589
|
+
target, and correlation evidence by that one shared id without this module
|
|
1590
|
+
ever needing to read source, run my-dev-kit, or choose among ambiguous
|
|
1591
|
+
candidates itself.
|
|
1592
|
+
|
|
1593
|
+
**Ambiguous/unavailable correlation behavior**: unaffected - a
|
|
1594
|
+
`RuntimeStaticCorrelationRecord` with `status: 'ambiguous'` continues to
|
|
1595
|
+
preserve every competing candidate (v0.6's own frozen invariant,
|
|
1596
|
+
untouched), and `status: 'unavailable'` never causes a fidelity mismatch
|
|
1597
|
+
for that same runtime target to be dropped - the two evidence kinds
|
|
1598
|
+
(runtime fidelity, static correlation) are attached independently and
|
|
1599
|
+
neither erases the other.
|
|
1600
|
+
|
|
1601
|
+
**Provenance**: every included mismatch remains traceable to the reference
|
|
1602
|
+
(`sources.referenceId`/`referenceRequestId`, new), the requirement
|
|
1603
|
+
(`requirementId`, `category`, `subject` - naming its reference region(s)),
|
|
1604
|
+
the Prompt 5 binding (`boundRuntimeTargets`), the candidate
|
|
1605
|
+
(`sources.observationIds`), and the full Prompt 6 evidence
|
|
1606
|
+
(`referenceValue`/`candidateRawValue`/`candidateValue`/`delta`/`tolerance`
|
|
1607
|
+
or `expectedRelationship`/`actualRelationship`) - nothing is replaced by a
|
|
1608
|
+
prose-only summary. No raw image bytes are ever embedded (fidelity carries
|
|
1609
|
+
only identifiers and numeric/categorical evidence, never pixels), and no
|
|
1610
|
+
source-ownership field (`sourceOwner`/`sourceFile`/`component`/`symbol`/
|
|
1611
|
+
`causedBy`) is ever produced - Prompt 7 stops at the runtime target exactly
|
|
1612
|
+
as Prompt 6 did; v0.6's own, unmodified static correlation is the only
|
|
1613
|
+
source-adjacent evidence this context ever carries, and it remains
|
|
1614
|
+
evidence, never edit authorization.
|
|
1615
|
+
|
|
1616
|
+
**Identity impact**: `buildBoundedAgentContextRequestIdentity` gained a
|
|
1617
|
+
final optional `fidelity?: unknown` parameter - omitted (never `null`) from
|
|
1618
|
+
the hashed semantic view when absent, so every pre-Prompt-7 call site keeps
|
|
1619
|
+
producing its exact byte-identical hash (verified by a frozen-vector-style
|
|
1620
|
+
regression test). When present, the caller's already-derived, bounded
|
|
1621
|
+
`BoundedReferenceFidelityProjection` (not the raw Prompt 6 evaluation) is
|
|
1622
|
+
hashed, so identity changes exactly when the content a caller would
|
|
1623
|
+
actually receive changes - never merely because an unselected, dropped
|
|
1624
|
+
requirement result changed somewhere upstream. `sources.referenceId`/
|
|
1625
|
+
`referenceRequestId` follow the identical omit-when-absent convention.
|
|
1626
|
+
Operational paths were never an identity input for this artifact family to
|
|
1627
|
+
begin with (no path parameter exists anywhere in this contract), so path
|
|
1628
|
+
independence holds trivially.
|
|
1629
|
+
|
|
1630
|
+
**Schema-version decision**: no bump. Every new field
|
|
1631
|
+
(`BoundedAgentContextSourceReferences.referenceId`/`referenceRequestId`,
|
|
1632
|
+
`BoundedAgentContextArtifact.fidelity`) is additive and optional; a
|
|
1633
|
+
pre-Prompt-7 artifact/consumer remains fully valid and behaviorally
|
|
1634
|
+
unchanged with all of them absent, matching the exact precedent
|
|
1635
|
+
`correlations?` already established without a version bump in v0.6 Batch 3.
|
|
1636
|
+
|
|
1637
|
+
**Persistence decision: none.** `projectBoundedAgentContext` and
|
|
1638
|
+
`projectReferenceFidelity` both remain pure, programmatic, in-memory
|
|
1639
|
+
functions - no new writer/reader, no new artifact family. This mirrors
|
|
1640
|
+
Prompt 6's own "no persisted fidelity artifact" decision and v0.6's
|
|
1641
|
+
existing "bounded agent context is library-only" architecture.
|
|
1642
|
+
|
|
1643
|
+
**CLI decision**: none added. v0.6 bounded agent context has never had a
|
|
1644
|
+
CLI surface, and this prompt does not introduce one - Prompt 8 is expected
|
|
1645
|
+
to become the first concrete consumer of `projectBoundedAgentContext`'s
|
|
1646
|
+
(now fidelity-aware) programmatic output.
|
|
1647
|
+
|
|
1648
|
+
## v0.7 Prompt 8 controlled end-to-end external-reference coding-agent correction workflow
|
|
1649
|
+
|
|
1650
|
+
Released as `0.7.0`. One new pure domain module,
|
|
1651
|
+
`domain/referenceCorrectionWorkflow.ts`, plus its identity counterpart,
|
|
1652
|
+
`domain/referenceCorrectionIdentity.ts` - the first stage that composes
|
|
1653
|
+
every Prompt 1-7 and v0.1/v0.4/v0.5/v0.6 owner into one traceable
|
|
1654
|
+
reference-driven correction cycle. It reimplements none of them: reference
|
|
1655
|
+
lifecycle/adequacy (Prompt 1/3), compatibility (Prompt 4), binding (Prompt
|
|
1656
|
+
5), fidelity (Prompt 6), bounded context (Prompt 7), runtime comparison
|
|
1657
|
+
(v0.4 `compareObservations`), and contract evaluation (v0.5
|
|
1658
|
+
`evaluateFrontendContract`) are all called, never re-derived. No new
|
|
1659
|
+
persisted artifact family, no CLI surface, no remote AI dependency, and no
|
|
1660
|
+
mechanism anywhere in this module (or any module it calls) that edits
|
|
1661
|
+
target source.
|
|
1662
|
+
|
|
1663
|
+
```ts
|
|
1664
|
+
// domain/referenceCorrectionWorkflow.ts
|
|
1665
|
+
function prepareReferenceCorrection(input: {
|
|
1666
|
+
reference: ExternalReferenceArtifact; // must be approved
|
|
1667
|
+
baselineObservation: ObservationArtifact; // approved baseline / pre-change state
|
|
1668
|
+
baselineContract: PersistentBaselineContract;
|
|
1669
|
+
changeContract: PerChangeContract;
|
|
1670
|
+
bindingDeclarations: readonly ReferenceRuntimeBindingDeclaration[];
|
|
1671
|
+
currentObservation: ObservationArtifact; // fidelity is measured against this (= baselineObservation for the canonical proof)
|
|
1672
|
+
generatedAt: string; producerVersion: string; projectionProfile: ProjectionProfile;
|
|
1673
|
+
}): { ok: true; status: 'handoff-ready'; reviewRequestId: string; fidelity: ReferenceCandidateFidelityEvaluation; handoff: ReferenceCorrectionHandoff }
|
|
1674
|
+
| { ok: true; status: 'blocked-not-evaluated'; reviewRequestId: string; fidelity: ReferenceCandidateFidelityEvaluation }
|
|
1675
|
+
| { ok: false; reason: string };
|
|
1676
|
+
|
|
1677
|
+
function reviewReferenceCorrectionAttempt(input: {
|
|
1678
|
+
// ...same reference/baselineObservation/baselineContract/changeContract/bindingDeclarations...
|
|
1679
|
+
reviewRequestId: string; // must match the id prepareReferenceCorrection returned for this exact semantic review
|
|
1680
|
+
candidateObservation: ObservationArtifact; // fresh, post-edit capture
|
|
1681
|
+
priorAttemptId?: string;
|
|
1682
|
+
}): { ok: true; attempt: ReferenceCorrectionAttemptResult } | { ok: false; reason: string };
|
|
1683
|
+
```
|
|
1684
|
+
|
|
1685
|
+
**Workflow architecture.** A narrowly-scoped coordinator, not a second
|
|
1686
|
+
workflow engine: it holds no stage catalog, no job scheduler, and no
|
|
1687
|
+
generic orchestration graph. It performs exactly two operations - "prepare"
|
|
1688
|
+
(pre-change evidence -> bounded handoff) and "review" (post-edit candidate
|
|
1689
|
+
-> one composed overall result) - matching this prompt's own explicit
|
|
1690
|
+
guidance that the external-edit boundary must remain a visible seam between
|
|
1691
|
+
two separate calls, never one command that blocks waiting for an external
|
|
1692
|
+
actor.
|
|
1693
|
+
|
|
1694
|
+
**New owners introduced**: `prepareReferenceCorrection`,
|
|
1695
|
+
`reviewReferenceCorrectionAttempt` (composition only - no new evaluation
|
|
1696
|
+
logic), `buildReferenceCorrectionReviewIdentity`/
|
|
1697
|
+
`buildReferenceCorrectionAttemptIdentity` (deterministic identity, see
|
|
1698
|
+
below), and the plain `ReferenceCorrectionHandoff`/
|
|
1699
|
+
`ReferenceCorrectionAttemptResult` result shapes.
|
|
1700
|
+
|
|
1701
|
+
**Existing owners reused, verbatim**: `isApprovedExternalReferenceArtifact`
|
|
1702
|
+
(Prompt 1), `isValidReferenceRuntimeBindingDeclarations` (Prompt 5),
|
|
1703
|
+
`evaluateReferenceCandidateFidelity` (Prompt 6), `projectBoundedAgentContext`
|
|
1704
|
+
(Prompt 7, itself now fidelity-aware), `compareObservations` (v0.4),
|
|
1705
|
+
`evaluateFrontendContract` (v0.5). None of their internal logic is
|
|
1706
|
+
inspected, duplicated, or reimplemented by this module - only their
|
|
1707
|
+
top-level results are read.
|
|
1708
|
+
|
|
1709
|
+
**Approved-reference/approved-baseline requirement.** `prepareReferenceCorrection`
|
|
1710
|
+
and `reviewReferenceCorrectionAttempt` both fail closed (`{ok: false}`) if
|
|
1711
|
+
`reference` is not in the `'approved'` lifecycle state (Prompt 1's own
|
|
1712
|
+
`isApprovedExternalReferenceArtifact` guard) - an imported-but-unapproved
|
|
1713
|
+
reference is never treated as an authoritative target design. Neither
|
|
1714
|
+
function ever calls `approveExternalReference`/`approveAndPersistBaseline`
|
|
1715
|
+
itself; approval remains the caller's own separate, explicit action.
|
|
1716
|
+
|
|
1717
|
+
**Pre-change candidate = approved baseline observation**, for the canonical
|
|
1718
|
+
proof: `prepareReferenceCorrection`'s `currentObservation` and
|
|
1719
|
+
`baselineObservation` are the same value, so the initial reference fidelity
|
|
1720
|
+
can genuinely `FAIL` (measuring the gap between the current, already-
|
|
1721
|
+
approved implementation and the desired new design) while the baseline
|
|
1722
|
+
itself stays fully valid and approved. A caller's own architecture may
|
|
1723
|
+
supply a distinct `currentObservation` only when justified - the workflow
|
|
1724
|
+
does not require them to be identical, only that `currentObservation` and
|
|
1725
|
+
`candidateObservation` are always independently validated
|
|
1726
|
+
`ObservationArtifact`s.
|
|
1727
|
+
|
|
1728
|
+
**Preparation (phase A)**: validates the common preconditions (approved
|
|
1729
|
+
reference, matching baseline/contract coherence, valid binding
|
|
1730
|
+
declarations), evaluates reference fidelity via Prompt 6 against
|
|
1731
|
+
`currentObservation`, and - only when that evaluation actually produced a
|
|
1732
|
+
result (`state !== 'not-evaluated'`) - projects it into a bounded context
|
|
1733
|
+
via Prompt 7/v0.6 and returns the `ReferenceCorrectionHandoff`. A
|
|
1734
|
+
`not-evaluated` fidelity (inadequate reference, or reference/candidate
|
|
1735
|
+
incompatible state) is reported as `status: 'blocked-not-evaluated'` -
|
|
1736
|
+
carrying the full Prompt 6 result for inspection, but never a fabricated
|
|
1737
|
+
handoff pretending evidence is adequate. An ambiguous or unavailable
|
|
1738
|
+
required binding does **not** block preparation outright - it still
|
|
1739
|
+
produces a `handoff-ready` result, with the ambiguity/unavailability
|
|
1740
|
+
visible directly in that requirement's own `unavailable`/`binding-
|
|
1741
|
+
unavailable` mismatch (Prompt 6's own honest per-requirement reporting,
|
|
1742
|
+
unchanged), so the external actor sees exactly why that specific
|
|
1743
|
+
requirement cannot yet be assessed.
|
|
1744
|
+
|
|
1745
|
+
**The handoff** (`ReferenceCorrectionHandoff`) carries `reviewRequestId`,
|
|
1746
|
+
`referenceId`/`referenceRequestId`, `baselineObservationId`,
|
|
1747
|
+
`currentObservationId`, the full Prompt 7 `boundedContext` (already
|
|
1748
|
+
containing bounded fidelity mismatches, protected/preserved context,
|
|
1749
|
+
adequacy/omission/truncation, and - when the caller supplied it - runtime/
|
|
1750
|
+
static correlation), and a fixed, four-line `verificationPlan` explaining
|
|
1751
|
+
in plain language what will be re-checked after the edit (fresh Chromium
|
|
1752
|
+
capture, reference re-evaluation, v0.4/v0.5 re-evaluation, and the exact
|
|
1753
|
+
overall-PASS rule) - never reduced to "make it look like the screenshot".
|
|
1754
|
+
No raw reference image bytes, no full `ObservationArtifact`, and no source
|
|
1755
|
+
excerpt are ever included.
|
|
1756
|
+
|
|
1757
|
+
**Handoff persistence: none.** The handoff is a plain, JSON-serializable,
|
|
1758
|
+
in-memory value returned directly to the caller - no new writer/reader, no
|
|
1759
|
+
new artifact family. A caller that needs the handoff to cross a process/
|
|
1760
|
+
session boundary (e.g. to hand it to an external coding-agent process) is
|
|
1761
|
+
free to serialize it with its own mechanism; observer product code does not
|
|
1762
|
+
own a persisted handoff artifact. This was a deliberate "smallest possible"
|
|
1763
|
+
choice: the handoff's only genuinely new identity is `reviewRequestId`
|
|
1764
|
+
(already deterministic and recomputable from stable inputs - see below), so
|
|
1765
|
+
nothing about it requires observer-managed persistence to remain
|
|
1766
|
+
traceable.
|
|
1767
|
+
|
|
1768
|
+
**Review identity** (`buildReferenceCorrectionReviewIdentity`): a pure
|
|
1769
|
+
function of `{referenceRequestId, baselineObservationId,
|
|
1770
|
+
baselineContractId, baselineContractClauses, changeContractId,
|
|
1771
|
+
changeContractClauses, bindingDeclarations}` only - never a timestamp,
|
|
1772
|
+
never an operational file path. Deliberately hashes each contract's own
|
|
1773
|
+
authored `clauses` content, not merely its `baselineId`/`contractId` label:
|
|
1774
|
+
unlike this repository's content-derived identities elsewhere (e.g.
|
|
1775
|
+
`ObservationArtifact.observationId`), a `PersistentBaselineContract`'s
|
|
1776
|
+
`baselineId` and a `PerChangeContract`'s `contractId` are plain, caller-
|
|
1777
|
+
authored strings (`approveAndPersistBaseline` persists `contract.baselineId`
|
|
1778
|
+
verbatim, never recomputing it from `clauses`) - so two structurally valid
|
|
1779
|
+
contracts could in principle share an id while authoring different clauses.
|
|
1780
|
+
Hashing clause content directly closes that gap (caught during this
|
|
1781
|
+
prompt's own independent-judge review before being reported PASS - see the
|
|
1782
|
+
report's Tooling incidents section). `reviewReferenceCorrectionAttempt`
|
|
1783
|
+
recomputes this same hash from its own inputs and rejects the call
|
|
1784
|
+
(`{ok: false}`) if the caller-supplied `reviewRequestId` does not match -
|
|
1785
|
+
this is the mechanism that makes "no hidden baseline change" an enforced
|
|
1786
|
+
invariant rather than a documented intention: an attempt claiming to belong
|
|
1787
|
+
to a review while actually supplying a different baseline observation,
|
|
1788
|
+
baseline contract (id or clause content), per-change contract (id or clause
|
|
1789
|
+
content), reference, or binding set can never silently succeed.
|
|
1790
|
+
|
|
1791
|
+
**Attempt identity** (`buildReferenceCorrectionAttemptIdentity`): a pure,
|
|
1792
|
+
deterministic function of `{reviewRequestId, candidateObservationId}` only
|
|
1793
|
+
- deliberately never a fresh random nonce. Every candidate observation
|
|
1794
|
+
already carries its own fresh, collision-resistant instance identity (v0.1's
|
|
1795
|
+
`buildObservationIdentity`), so hashing it together with the review it was
|
|
1796
|
+
captured for gives an attempt id that is both reproducible (the same
|
|
1797
|
+
review+candidate pair always yields the same `attemptId`) and guaranteed
|
|
1798
|
+
distinct per real capture.
|
|
1799
|
+
|
|
1800
|
+
**Attempt history**: caller-managed, not observer-persisted. Because both
|
|
1801
|
+
workflow functions are pure (no internal mutable state, no side effects),
|
|
1802
|
+
an already-returned `ReferenceCorrectionAttemptResult` can never be
|
|
1803
|
+
overwritten by a later call - a caller that keeps every attempt result it
|
|
1804
|
+
receives (in memory, in its own log, or in its own storage) has a complete,
|
|
1805
|
+
immutable, traceable history for free, linked via each attempt's own
|
|
1806
|
+
`reviewRequestId` (shared across all attempts of one review),
|
|
1807
|
+
`priorAttemptId` (an optional, purely informational link to the immediately
|
|
1808
|
+
preceding attempt, carried through unchanged - never consulted by the
|
|
1809
|
+
evaluation logic itself), and `attemptId`.
|
|
1810
|
+
|
|
1811
|
+
**Baseline-across-attempts rule**: enforced structurally, not merely
|
|
1812
|
+
documented. Every call to `reviewReferenceCorrectionAttempt` requires the
|
|
1813
|
+
caller to re-supply `baselineObservation`/`baselineContract` in full, and
|
|
1814
|
+
`compareObservations`/`evaluateFrontendContract` are always invoked with
|
|
1815
|
+
that same baseline against the fresh `candidateObservation` - there is no
|
|
1816
|
+
code path anywhere in this module that compares one candidate against a
|
|
1817
|
+
prior candidate instead. Combined with the `reviewRequestId` coherence
|
|
1818
|
+
check above, a caller cannot silently swap in a different baseline between
|
|
1819
|
+
attempts of the same review without the call being rejected.
|
|
1820
|
+
|
|
1821
|
+
**Overall result composition.** `ReferenceCorrectionOverallState =
|
|
1822
|
+
'not-evaluated' | 'pass' | 'fail'`:
|
|
1823
|
+
|
|
1824
|
+
- `fidelity.state === 'not-evaluated'` -> overall `'not-evaluated'` - Prompt
|
|
1825
|
+
6's own explicit blocked state is preserved exactly, never collapsed into
|
|
1826
|
+
an ordinary `'fail'`.
|
|
1827
|
+
- otherwise, `fidelity.state === 'pass' && contractEvaluation.overallVerdict === 'PASS'`
|
|
1828
|
+
-> overall `'pass'`; anything else -> overall `'fail'`.
|
|
1829
|
+
|
|
1830
|
+
A structurally-incomparable baseline/candidate pair is *not* given its own
|
|
1831
|
+
third overall bucket - v0.5's own `evaluateFrontendContract` already
|
|
1832
|
+
returns `'FAIL'` (never `'PASS'`) for that case, per its own established,
|
|
1833
|
+
unmodified precedent, and this workflow reuses that decision rather than
|
|
1834
|
+
re-litigating it. `approvalEligible` is a plain, read-only boolean
|
|
1835
|
+
(`true` iff `overallState === 'pass'`) - it is never itself an approval
|
|
1836
|
+
action; the caller must still invoke the existing explicit
|
|
1837
|
+
`approveAndPersistBaseline`/`approveExternalReference` owners separately,
|
|
1838
|
+
and neither is ever called from within this module.
|
|
1839
|
+
|
|
1840
|
+
**Correction iteration**: `reviewReferenceCorrectionAttempt` is called once
|
|
1841
|
+
per candidate; the caller decides whether and when to call it again after
|
|
1842
|
+
another external edit. There is no loop, no polling, no automatic retry,
|
|
1843
|
+
and no mechanism in this module that itself waits for or drives an external
|
|
1844
|
+
implementation step - the production boundary between "prepare a handoff"
|
|
1845
|
+
and "review a candidate" is the explicit seam a human or an external
|
|
1846
|
+
process controls.
|
|
1847
|
+
|
|
1848
|
+
**Source-editing boundary**: absolute. Neither this module nor anything it
|
|
1849
|
+
calls opens, reads, parses, or writes any target source file; both public
|
|
1850
|
+
operations accept only already-captured `ObservationArtifact`s and already-
|
|
1851
|
+
approved contract/reference artifacts. Real-Chromium candidate capture is
|
|
1852
|
+
always the caller's own responsibility, through the existing, unmodified
|
|
1853
|
+
observation pipeline (`runBrowserCapture`/`buildObservationArtifact`, the
|
|
1854
|
+
same functions `application/observationPersistence.ts#observe` already
|
|
1855
|
+
uses) - Prompt 8 adds no second browser adapter, screenshot engine, target
|
|
1856
|
+
resolver, or evidence-capture path.
|