@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.
Files changed (316) hide show
  1. package/CHANGELOG.md +397 -0
  2. package/LICENSE +21 -0
  3. package/README.md +255 -0
  4. package/dist/application/browserCaptureService.d.ts +11 -0
  5. package/dist/application/browserCaptureService.js +12 -0
  6. package/dist/application/browserCaptureService.js.map +1 -0
  7. package/dist/application/comparisonService.d.ts +56 -0
  8. package/dist/application/comparisonService.js +77 -0
  9. package/dist/application/comparisonService.js.map +1 -0
  10. package/dist/application/externalReferencePersistenceService.d.ts +75 -0
  11. package/dist/application/externalReferencePersistenceService.js +182 -0
  12. package/dist/application/externalReferencePersistenceService.js.map +1 -0
  13. package/dist/application/frontendContractEvaluationService.d.ts +49 -0
  14. package/dist/application/frontendContractEvaluationService.js +112 -0
  15. package/dist/application/frontendContractEvaluationService.js.map +1 -0
  16. package/dist/application/frontendContractPersistenceService.d.ts +56 -0
  17. package/dist/application/frontendContractPersistenceService.js +91 -0
  18. package/dist/application/frontendContractPersistenceService.js.map +1 -0
  19. package/dist/application/observationPersistence.d.ts +59 -0
  20. package/dist/application/observationPersistence.js +79 -0
  21. package/dist/application/observationPersistence.js.map +1 -0
  22. package/dist/application/projectCheckService.d.ts +3 -0
  23. package/dist/application/projectCheckService.js +169 -0
  24. package/dist/application/projectCheckService.js.map +1 -0
  25. package/dist/application/projectWorkflowService.d.ts +46 -0
  26. package/dist/application/projectWorkflowService.js +92 -0
  27. package/dist/application/projectWorkflowService.js.map +1 -0
  28. package/dist/application/referenceFidelityEvaluationService.d.ts +28 -0
  29. package/dist/application/referenceFidelityEvaluationService.js +45 -0
  30. package/dist/application/referenceFidelityEvaluationService.js.map +1 -0
  31. package/dist/artifacts/artifactReader.d.ts +19 -0
  32. package/dist/artifacts/artifactReader.js +36 -0
  33. package/dist/artifacts/artifactReader.js.map +1 -0
  34. package/dist/artifacts/artifactWriter.d.ts +25 -0
  35. package/dist/artifacts/artifactWriter.js +68 -0
  36. package/dist/artifacts/artifactWriter.js.map +1 -0
  37. package/dist/artifacts/comparisonArtifactReader.d.ts +18 -0
  38. package/dist/artifacts/comparisonArtifactReader.js +35 -0
  39. package/dist/artifacts/comparisonArtifactReader.js.map +1 -0
  40. package/dist/artifacts/comparisonArtifactWriter.d.ts +41 -0
  41. package/dist/artifacts/comparisonArtifactWriter.js +67 -0
  42. package/dist/artifacts/comparisonArtifactWriter.js.map +1 -0
  43. package/dist/artifacts/externalReferenceArtifactReader.d.ts +18 -0
  44. package/dist/artifacts/externalReferenceArtifactReader.js +35 -0
  45. package/dist/artifacts/externalReferenceArtifactReader.js.map +1 -0
  46. package/dist/artifacts/externalReferenceArtifactWriter.d.ts +44 -0
  47. package/dist/artifacts/externalReferenceArtifactWriter.js +77 -0
  48. package/dist/artifacts/externalReferenceArtifactWriter.js.map +1 -0
  49. package/dist/artifacts/frontendContractArtifactReader.d.ts +24 -0
  50. package/dist/artifacts/frontendContractArtifactReader.js +47 -0
  51. package/dist/artifacts/frontendContractArtifactReader.js.map +1 -0
  52. package/dist/artifacts/frontendContractArtifactWriter.d.ts +34 -0
  53. package/dist/artifacts/frontendContractArtifactWriter.js +70 -0
  54. package/dist/artifacts/frontendContractArtifactWriter.js.map +1 -0
  55. package/dist/artifacts/frontendContractEvaluationArtifactReader.d.ts +17 -0
  56. package/dist/artifacts/frontendContractEvaluationArtifactReader.js +34 -0
  57. package/dist/artifacts/frontendContractEvaluationArtifactReader.js.map +1 -0
  58. package/dist/artifacts/frontendContractEvaluationArtifactWriter.d.ts +32 -0
  59. package/dist/artifacts/frontendContractEvaluationArtifactWriter.js +58 -0
  60. package/dist/artifacts/frontendContractEvaluationArtifactWriter.js.map +1 -0
  61. package/dist/artifacts/types.d.ts +17 -0
  62. package/dist/artifacts/types.js +2 -0
  63. package/dist/artifacts/types.js.map +1 -0
  64. package/dist/browser/chromiumAdapter.d.ts +21 -0
  65. package/dist/browser/chromiumAdapter.js +204 -0
  66. package/dist/browser/chromiumAdapter.js.map +1 -0
  67. package/dist/browser/evidenceCapture.d.ts +62 -0
  68. package/dist/browser/evidenceCapture.js +500 -0
  69. package/dist/browser/evidenceCapture.js.map +1 -0
  70. package/dist/browser/scrollCapture.d.ts +32 -0
  71. package/dist/browser/scrollCapture.js +163 -0
  72. package/dist/browser/scrollCapture.js.map +1 -0
  73. package/dist/browser/types.d.ts +24 -0
  74. package/dist/browser/types.js +2 -0
  75. package/dist/browser/types.js.map +1 -0
  76. package/dist/cli.d.ts +8 -0
  77. package/dist/cli.js +2411 -0
  78. package/dist/cli.js.map +1 -0
  79. package/dist/domain/boundedAgentContext.d.ts +226 -0
  80. package/dist/domain/boundedAgentContext.js +355 -0
  81. package/dist/domain/boundedAgentContext.js.map +1 -0
  82. package/dist/domain/boundedAgentContextCorrelation.d.ts +74 -0
  83. package/dist/domain/boundedAgentContextCorrelation.js +441 -0
  84. package/dist/domain/boundedAgentContextCorrelation.js.map +1 -0
  85. package/dist/domain/boundedAgentContextIdentity.d.ts +26 -0
  86. package/dist/domain/boundedAgentContextIdentity.js +69 -0
  87. package/dist/domain/boundedAgentContextIdentity.js.map +1 -0
  88. package/dist/domain/boundedAgentContextProjection.d.ts +71 -0
  89. package/dist/domain/boundedAgentContextProjection.js +477 -0
  90. package/dist/domain/boundedAgentContextProjection.js.map +1 -0
  91. package/dist/domain/comparison.d.ts +220 -0
  92. package/dist/domain/comparison.js +350 -0
  93. package/dist/domain/comparison.js.map +1 -0
  94. package/dist/domain/comparisonEngine.d.ts +76 -0
  95. package/dist/domain/comparisonEngine.js +734 -0
  96. package/dist/domain/comparisonEngine.js.map +1 -0
  97. package/dist/domain/comparisonIdentity.d.ts +13 -0
  98. package/dist/domain/comparisonIdentity.js +46 -0
  99. package/dist/domain/comparisonIdentity.js.map +1 -0
  100. package/dist/domain/completion.d.ts +30 -0
  101. package/dist/domain/completion.js +22 -0
  102. package/dist/domain/completion.js.map +1 -0
  103. package/dist/domain/diagnostics.d.ts +17 -0
  104. package/dist/domain/diagnostics.js +77 -0
  105. package/dist/domain/diagnostics.js.map +1 -0
  106. package/dist/domain/evidence.d.ts +29 -0
  107. package/dist/domain/evidence.js +59 -0
  108. package/dist/domain/evidence.js.map +1 -0
  109. package/dist/domain/explicitState.d.ts +40 -0
  110. package/dist/domain/explicitState.js +54 -0
  111. package/dist/domain/explicitState.js.map +1 -0
  112. package/dist/domain/externalReference.d.ts +158 -0
  113. package/dist/domain/externalReference.js +167 -0
  114. package/dist/domain/externalReference.js.map +1 -0
  115. package/dist/domain/externalReferenceApplicability.d.ts +43 -0
  116. package/dist/domain/externalReferenceApplicability.js +52 -0
  117. package/dist/domain/externalReferenceApplicability.js.map +1 -0
  118. package/dist/domain/externalReferenceCompatibility.d.ts +40 -0
  119. package/dist/domain/externalReferenceCompatibility.js +48 -0
  120. package/dist/domain/externalReferenceCompatibility.js.map +1 -0
  121. package/dist/domain/externalReferenceFidelity.d.ts +171 -0
  122. package/dist/domain/externalReferenceFidelity.js +419 -0
  123. package/dist/domain/externalReferenceFidelity.js.map +1 -0
  124. package/dist/domain/externalReferenceIdentity.d.ts +38 -0
  125. package/dist/domain/externalReferenceIdentity.js +70 -0
  126. package/dist/domain/externalReferenceIdentity.js.map +1 -0
  127. package/dist/domain/externalReferenceImage.d.ts +35 -0
  128. package/dist/domain/externalReferenceImage.js +160 -0
  129. package/dist/domain/externalReferenceImage.js.map +1 -0
  130. package/dist/domain/externalReferenceRegionRelationships.d.ts +63 -0
  131. package/dist/domain/externalReferenceRegionRelationships.js +98 -0
  132. package/dist/domain/externalReferenceRegionRelationships.js.map +1 -0
  133. package/dist/domain/externalReferenceRegions.d.ts +65 -0
  134. package/dist/domain/externalReferenceRegions.js +105 -0
  135. package/dist/domain/externalReferenceRegions.js.map +1 -0
  136. package/dist/domain/externalReferenceRequirementIdentity.d.ts +12 -0
  137. package/dist/domain/externalReferenceRequirementIdentity.js +35 -0
  138. package/dist/domain/externalReferenceRequirementIdentity.js.map +1 -0
  139. package/dist/domain/externalReferenceRequirements.d.ts +215 -0
  140. package/dist/domain/externalReferenceRequirements.js +401 -0
  141. package/dist/domain/externalReferenceRequirements.js.map +1 -0
  142. package/dist/domain/externalReferenceRuntimeBinding.d.ts +146 -0
  143. package/dist/domain/externalReferenceRuntimeBinding.js +183 -0
  144. package/dist/domain/externalReferenceRuntimeBinding.js.map +1 -0
  145. package/dist/domain/frontendContractEvaluation.d.ts +57 -0
  146. package/dist/domain/frontendContractEvaluation.js +454 -0
  147. package/dist/domain/frontendContractEvaluation.js.map +1 -0
  148. package/dist/domain/frontendContractEvaluationArtifact.d.ts +65 -0
  149. package/dist/domain/frontendContractEvaluationArtifact.js +108 -0
  150. package/dist/domain/frontendContractEvaluationArtifact.js.map +1 -0
  151. package/dist/domain/frontendContractIdentity.d.ts +39 -0
  152. package/dist/domain/frontendContractIdentity.js +70 -0
  153. package/dist/domain/frontendContractIdentity.js.map +1 -0
  154. package/dist/domain/frontendContracts.d.ts +188 -0
  155. package/dist/domain/frontendContracts.js +260 -0
  156. package/dist/domain/frontendContracts.js.map +1 -0
  157. package/dist/domain/identity.d.ts +21 -0
  158. package/dist/domain/identity.js +47 -0
  159. package/dist/domain/identity.js.map +1 -0
  160. package/dist/domain/referenceCorrectionIdentity.d.ts +40 -0
  161. package/dist/domain/referenceCorrectionIdentity.js +77 -0
  162. package/dist/domain/referenceCorrectionIdentity.js.map +1 -0
  163. package/dist/domain/referenceCorrectionWorkflow.d.ts +160 -0
  164. package/dist/domain/referenceCorrectionWorkflow.js +165 -0
  165. package/dist/domain/referenceCorrectionWorkflow.js.map +1 -0
  166. package/dist/domain/referenceFidelityProjection.d.ts +65 -0
  167. package/dist/domain/referenceFidelityProjection.js +135 -0
  168. package/dist/domain/referenceFidelityProjection.js.map +1 -0
  169. package/dist/domain/relationships.d.ts +210 -0
  170. package/dist/domain/relationships.js +352 -0
  171. package/dist/domain/relationships.js.map +1 -0
  172. package/dist/domain/schema.d.ts +269 -0
  173. package/dist/domain/schema.js +442 -0
  174. package/dist/domain/schema.js.map +1 -0
  175. package/dist/domain/scrollEvidence.d.ts +51 -0
  176. package/dist/domain/scrollEvidence.js +134 -0
  177. package/dist/domain/scrollEvidence.js.map +1 -0
  178. package/dist/index.d.ts +114 -0
  179. package/dist/index.js +64 -0
  180. package/dist/index.js.map +1 -0
  181. package/dist/projectWorkflow/aliasCatalog.d.ts +21 -0
  182. package/dist/projectWorkflow/aliasCatalog.js +67 -0
  183. package/dist/projectWorkflow/aliasCatalog.js.map +1 -0
  184. package/dist/projectWorkflow/checkAcceptance.d.ts +25 -0
  185. package/dist/projectWorkflow/checkAcceptance.js +55 -0
  186. package/dist/projectWorkflow/checkAcceptance.js.map +1 -0
  187. package/dist/projectWorkflow/checkResult.d.ts +85 -0
  188. package/dist/projectWorkflow/checkResult.js +101 -0
  189. package/dist/projectWorkflow/checkResult.js.map +1 -0
  190. package/dist/projectWorkflow/projectConfig.d.ts +43 -0
  191. package/dist/projectWorkflow/projectConfig.js +84 -0
  192. package/dist/projectWorkflow/projectConfig.js.map +1 -0
  193. package/dist/projectWorkflow/projectDiscovery.d.ts +9 -0
  194. package/dist/projectWorkflow/projectDiscovery.js +20 -0
  195. package/dist/projectWorkflow/projectDiscovery.js.map +1 -0
  196. package/dist/projectWorkflow/projectPaths.d.ts +8 -0
  197. package/dist/projectWorkflow/projectPaths.js +23 -0
  198. package/dist/projectWorkflow/projectPaths.js.map +1 -0
  199. package/dist/request/paths.d.ts +14 -0
  200. package/dist/request/paths.js +33 -0
  201. package/dist/request/paths.js.map +1 -0
  202. package/dist/request/request.d.ts +111 -0
  203. package/dist/request/request.js +464 -0
  204. package/dist/request/request.js.map +1 -0
  205. package/dist/safety/policy.d.ts +14 -0
  206. package/dist/safety/policy.js +81 -0
  207. package/dist/safety/policy.js.map +1 -0
  208. package/dist/viewer/assets/index-CN_yb9Uf.css +1 -0
  209. package/dist/viewer/assets/index-D98S1_2d.js +9 -0
  210. package/dist/viewer/icons/icon-192.png +0 -0
  211. package/dist/viewer/icons/icon-512.png +0 -0
  212. package/dist/viewer/index.html +15 -0
  213. package/dist/viewer/manifest.webmanifest +1 -0
  214. package/dist/viewer/registerSW.js +1 -0
  215. package/dist/viewer/sw.js +1 -0
  216. package/dist/viewer/workbox-9c191d2f.js +1 -0
  217. package/dist/viewerServer/context.d.ts +48 -0
  218. package/dist/viewerServer/context.js +60 -0
  219. package/dist/viewerServer/context.js.map +1 -0
  220. package/dist/viewerServer/evidence/classify.d.ts +59 -0
  221. package/dist/viewerServer/evidence/classify.js +124 -0
  222. package/dist/viewerServer/evidence/classify.js.map +1 -0
  223. package/dist/viewerServer/evidence/comparisonView.d.ts +28 -0
  224. package/dist/viewerServer/evidence/comparisonView.js +43 -0
  225. package/dist/viewerServer/evidence/comparisonView.js.map +1 -0
  226. package/dist/viewerServer/evidence/contextSourceView.d.ts +25 -0
  227. package/dist/viewerServer/evidence/contextSourceView.js +20 -0
  228. package/dist/viewerServer/evidence/contextSourceView.js.map +1 -0
  229. package/dist/viewerServer/evidence/discovery.d.ts +31 -0
  230. package/dist/viewerServer/evidence/discovery.js +78 -0
  231. package/dist/viewerServer/evidence/discovery.js.map +1 -0
  232. package/dist/viewerServer/evidence/evaluationView.d.ts +32 -0
  233. package/dist/viewerServer/evidence/evaluationView.js +50 -0
  234. package/dist/viewerServer/evidence/evaluationView.js.map +1 -0
  235. package/dist/viewerServer/evidence/handles.d.ts +21 -0
  236. package/dist/viewerServer/evidence/handles.js +43 -0
  237. package/dist/viewerServer/evidence/handles.js.map +1 -0
  238. package/dist/viewerServer/evidence/index.d.ts +41 -0
  239. package/dist/viewerServer/evidence/index.js +82 -0
  240. package/dist/viewerServer/evidence/index.js.map +1 -0
  241. package/dist/viewerServer/evidence/limits.d.ts +27 -0
  242. package/dist/viewerServer/evidence/limits.js +28 -0
  243. package/dist/viewerServer/evidence/limits.js.map +1 -0
  244. package/dist/viewerServer/evidence/linkedEvidence.d.ts +43 -0
  245. package/dist/viewerServer/evidence/linkedEvidence.js +151 -0
  246. package/dist/viewerServer/evidence/linkedEvidence.js.map +1 -0
  247. package/dist/viewerServer/evidence/mediaResolver.d.ts +16 -0
  248. package/dist/viewerServer/evidence/mediaResolver.js +85 -0
  249. package/dist/viewerServer/evidence/mediaResolver.js.map +1 -0
  250. package/dist/viewerServer/evidence/observationView.d.ts +29 -0
  251. package/dist/viewerServer/evidence/observationView.js +46 -0
  252. package/dist/viewerServer/evidence/observationView.js.map +1 -0
  253. package/dist/viewerServer/evidence/pathSafety.d.ts +11 -0
  254. package/dist/viewerServer/evidence/pathSafety.js +31 -0
  255. package/dist/viewerServer/evidence/pathSafety.js.map +1 -0
  256. package/dist/viewerServer/evidence/projection.d.ts +55 -0
  257. package/dist/viewerServer/evidence/projection.js +152 -0
  258. package/dist/viewerServer/evidence/projection.js.map +1 -0
  259. package/dist/viewerServer/evidence/referenceView.d.ts +133 -0
  260. package/dist/viewerServer/evidence/referenceView.js +169 -0
  261. package/dist/viewerServer/evidence/referenceView.js.map +1 -0
  262. package/dist/viewerServer/httpServer.d.ts +27 -0
  263. package/dist/viewerServer/httpServer.js +381 -0
  264. package/dist/viewerServer/httpServer.js.map +1 -0
  265. package/dist/viewerServer/openBrowser.d.ts +7 -0
  266. package/dist/viewerServer/openBrowser.js +32 -0
  267. package/dist/viewerServer/openBrowser.js.map +1 -0
  268. package/dist/viewerServer/port.d.ts +16 -0
  269. package/dist/viewerServer/port.js +19 -0
  270. package/dist/viewerServer/port.js.map +1 -0
  271. package/dist/viewerServer/viewerService.d.ts +62 -0
  272. package/dist/viewerServer/viewerService.js +88 -0
  273. package/dist/viewerServer/viewerService.js.map +1 -0
  274. package/docs/ARCHITECTURE.md +1286 -0
  275. package/docs/CI_CD.md +250 -0
  276. package/docs/COMMANDS.md +972 -0
  277. package/docs/CONTRACTS.md +1856 -0
  278. package/docs/CURRENT_STATE.md +1049 -0
  279. package/docs/DEVELOPMENT.md +202 -0
  280. package/docs/DOCUMENTATION_PRESERVATION_POLICY.md +50 -0
  281. package/docs/PROJECT_DESCRIPTION.md +2221 -0
  282. package/docs/PROJECT_MILESTONES.md +2526 -0
  283. package/docs/PROJECT_OVERVIEW.md +150 -0
  284. package/docs/QUICKSTART.md +83 -0
  285. package/docs/RELEASE.md +27 -0
  286. package/docs/ROADMAP.md +641 -0
  287. package/docs/SECURITY.md +218 -0
  288. package/docs/WORKFLOWS.md +642 -0
  289. package/docs/plans/v0.8-implementation-plan.md +655 -0
  290. package/docs/plans/v0.8.1-cli-usability-patch-plan.md +505 -0
  291. package/docs/reports/v0.7-bounded-fidelity-context-prompt7.md +243 -0
  292. package/docs/reports/v0.7-implementation-completeness-documentation-reconciliation.md +497 -0
  293. package/docs/reports/v0.7-pre-release-readiness.md +337 -0
  294. package/docs/reports/v0.7-reference-binding-prompt5.md +223 -0
  295. package/docs/reports/v0.7-reference-compatibility-prompt4.md +234 -0
  296. package/docs/reports/v0.7-reference-correction-workflow-prompt8.md +222 -0
  297. package/docs/reports/v0.7-reference-fidelity-prompt6.md +216 -0
  298. package/docs/reports/v0.7-reference-foundation-prompt1.md +151 -0
  299. package/docs/reports/v0.7-reference-regions-prompt2.md +195 -0
  300. package/docs/reports/v0.7-reference-requirements-prompt3.md +217 -0
  301. package/docs/reports/v0.7-release-prep.md +423 -0
  302. package/docs/reports/v0.8-binding-fidelity-interaction-batch6.md +279 -0
  303. package/docs/reports/v0.8-bounded-context-correlation-batch7.md +233 -0
  304. package/docs/reports/v0.8-comparison-contract-inspection-batch4.md +279 -0
  305. package/docs/reports/v0.8-evidence-index-readers-batch2.md +247 -0
  306. package/docs/reports/v0.8-implementation-completeness-documentation-reconciliation.md +741 -0
  307. package/docs/reports/v0.8-integrated-viewer-acceptance-batch8.md +128 -0
  308. package/docs/reports/v0.8-observation-svg-inspection-batch3.md +223 -0
  309. package/docs/reports/v0.8-prerelease-readiness-cross-platform-security-code-rot.md +687 -0
  310. package/docs/reports/v0.8-reference-candidate-inspection-batch5.md +232 -0
  311. package/docs/reports/v0.8-viewer-runtime-pwa-batch1.md +278 -0
  312. package/docs/reports/v0.8.1-check-orchestration-prompt2.md +69 -0
  313. package/docs/reports/v0.8.1-implementation-completeness-documentation-reconciliation.md +114 -0
  314. package/docs/reports/v0.8.1-prerelease-readiness-cross-platform-security-code-rot.md +170 -0
  315. package/docs/reports/v0.8.1-project-workflow-foundation-prompt1.md +66 -0
  316. 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.