@dailephd/my-frontend-observer 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +107 -7
  3. package/dist/application/projectWorkflowService.d.ts +18 -1
  4. package/dist/application/projectWorkflowService.js +40 -2
  5. package/dist/application/projectWorkflowService.js.map +1 -1
  6. package/dist/application/visualAnnotationContractPromotionService.d.ts +41 -0
  7. package/dist/application/visualAnnotationContractPromotionService.js +143 -0
  8. package/dist/application/visualAnnotationContractPromotionService.js.map +1 -0
  9. package/dist/application/visualAnnotationPersistenceService.d.ts +34 -0
  10. package/dist/application/visualAnnotationPersistenceService.js +68 -0
  11. package/dist/application/visualAnnotationPersistenceService.js.map +1 -0
  12. package/dist/application/visualAnnotationReferenceMaterializationService.d.ts +53 -0
  13. package/dist/application/visualAnnotationReferenceMaterializationService.js +194 -0
  14. package/dist/application/visualAnnotationReferenceMaterializationService.js.map +1 -0
  15. package/dist/artifacts/visualAnnotationArtifactReader.d.ts +19 -0
  16. package/dist/artifacts/visualAnnotationArtifactReader.js +66 -0
  17. package/dist/artifacts/visualAnnotationArtifactReader.js.map +1 -0
  18. package/dist/artifacts/visualAnnotationArtifactWriter.d.ts +42 -0
  19. package/dist/artifacts/visualAnnotationArtifactWriter.js +85 -0
  20. package/dist/artifacts/visualAnnotationArtifactWriter.js.map +1 -0
  21. package/dist/cli.js +464 -454
  22. package/dist/cli.js.map +1 -1
  23. package/dist/domain/visualAnnotation.d.ts +217 -0
  24. package/dist/domain/visualAnnotation.js +584 -0
  25. package/dist/domain/visualAnnotation.js.map +1 -0
  26. package/dist/domain/visualAnnotationIdentity.d.ts +17 -0
  27. package/dist/domain/visualAnnotationIdentity.js +47 -0
  28. package/dist/domain/visualAnnotationIdentity.js.map +1 -0
  29. package/dist/index.d.ts +11 -0
  30. package/dist/index.js +6 -0
  31. package/dist/index.js.map +1 -1
  32. package/dist/projectWorkflow/projectPaths.d.ts +9 -0
  33. package/dist/projectWorkflow/projectPaths.js +21 -0
  34. package/dist/projectWorkflow/projectPaths.js.map +1 -1
  35. package/dist/viewer/assets/{index-CN_yb9Uf.css → index-BN41MI7m.css} +1 -1
  36. package/dist/viewer/assets/index-CkKXnlrI.js +9 -0
  37. package/dist/viewer/index.html +2 -2
  38. package/dist/viewer/sw.js +1 -1
  39. package/dist/viewerServer/annotationAuthoring.d.ts +64 -0
  40. package/dist/viewerServer/annotationAuthoring.js +230 -0
  41. package/dist/viewerServer/annotationAuthoring.js.map +1 -0
  42. package/dist/viewerServer/annotationContractPromotion.d.ts +51 -0
  43. package/dist/viewerServer/annotationContractPromotion.js +105 -0
  44. package/dist/viewerServer/annotationContractPromotion.js.map +1 -0
  45. package/dist/viewerServer/annotationReferenceMaterialization.d.ts +40 -0
  46. package/dist/viewerServer/annotationReferenceMaterialization.js +91 -0
  47. package/dist/viewerServer/annotationReferenceMaterialization.js.map +1 -0
  48. package/dist/viewerServer/authoringSecurity.d.ts +59 -0
  49. package/dist/viewerServer/authoringSecurity.js +112 -0
  50. package/dist/viewerServer/authoringSecurity.js.map +1 -0
  51. package/dist/viewerServer/evidence/annotationView.d.ts +28 -0
  52. package/dist/viewerServer/evidence/annotationView.js +43 -0
  53. package/dist/viewerServer/evidence/annotationView.js.map +1 -0
  54. package/dist/viewerServer/evidence/classify.d.ts +3 -1
  55. package/dist/viewerServer/evidence/classify.js +12 -0
  56. package/dist/viewerServer/evidence/classify.js.map +1 -1
  57. package/dist/viewerServer/evidence/discovery.d.ts +2 -0
  58. package/dist/viewerServer/evidence/discovery.js +6 -0
  59. package/dist/viewerServer/evidence/discovery.js.map +1 -1
  60. package/dist/viewerServer/evidence/handles.js +1 -0
  61. package/dist/viewerServer/evidence/handles.js.map +1 -1
  62. package/dist/viewerServer/evidence/index.d.ts +29 -0
  63. package/dist/viewerServer/evidence/index.js +43 -1
  64. package/dist/viewerServer/evidence/index.js.map +1 -1
  65. package/dist/viewerServer/evidence/mediaResolver.d.ts +1 -1
  66. package/dist/viewerServer/evidence/mediaResolver.js +28 -2
  67. package/dist/viewerServer/evidence/mediaResolver.js.map +1 -1
  68. package/dist/viewerServer/evidence/projection.d.ts +5 -1
  69. package/dist/viewerServer/evidence/projection.js +19 -0
  70. package/dist/viewerServer/evidence/projection.js.map +1 -1
  71. package/dist/viewerServer/httpServer.d.ts +13 -2
  72. package/dist/viewerServer/httpServer.js +278 -4
  73. package/dist/viewerServer/httpServer.js.map +1 -1
  74. package/dist/viewerServer/viewerService.d.ts +8 -0
  75. package/dist/viewerServer/viewerService.js +38 -2
  76. package/dist/viewerServer/viewerService.js.map +1 -1
  77. package/docs/ARCHITECTURE.md +108 -21
  78. package/docs/CI_CD.md +57 -1
  79. package/docs/COMMANDS.md +44 -4
  80. package/docs/CONTRACTS.md +78 -8
  81. package/docs/CURRENT_STATE.md +157 -35
  82. package/docs/DEVELOPMENT.md +8 -3
  83. package/docs/PROJECT_DESCRIPTION.md +4 -1
  84. package/docs/PROJECT_MILESTONES.md +4 -0
  85. package/docs/PROJECT_OVERVIEW.md +41 -17
  86. package/docs/QUICKSTART.md +52 -39
  87. package/docs/RELEASE.md +16 -11
  88. package/docs/ROADMAP.md +406 -66
  89. package/docs/SECURITY.md +71 -14
  90. package/docs/WORKFLOWS.md +151 -23
  91. package/docs/plans/v0.9-implementation-plan.md +1529 -0
  92. package/docs/reports/v0.9-architecture-retrieval.md +567 -0
  93. package/docs/reports/v0.9-batch1-visual-annotation-foundation.md +351 -0
  94. package/docs/reports/v0.9-batch2-viewer-annotation-authoring-boundary.md +438 -0
  95. package/docs/reports/v0.9-batch3-runtime-screenshot-annotation-authoring.md +412 -0
  96. package/docs/reports/v0.9-batch4-external-reference-annotation-authoring.md +452 -0
  97. package/docs/reports/v0.9-batch5-runtime-intent-contract-promotion.md +535 -0
  98. package/docs/reports/v0.9-batch6-reference-materialization.md +514 -0
  99. package/docs/reports/v0.9-batch7-integrated-acceptance.md +644 -0
  100. package/docs/reports/v0.9-demo-foundation.md +589 -0
  101. package/docs/reports/v0.9-final-pre-release-readiness.md +209 -0
  102. package/docs/reports/v0.9-final-readiness-corrections.md +530 -0
  103. package/docs/reports/v0.9-pre-release-readiness.md +170 -0
  104. package/docs/reports/v0.9-tutorial-end-to-end-acceptance.md +980 -0
  105. package/docs/reports/v0.9-tutorial-integration.md +731 -0
  106. package/package.json +2 -2
  107. package/dist/viewer/assets/index-D98S1_2d.js +0 -9
package/dist/cli.js CHANGED
@@ -25,11 +25,11 @@ const defaultIO = {
25
25
  process.stderr.write(text);
26
26
  },
27
27
  };
28
- const TOP_LEVEL_HELP = `my-frontend-observer - local-first browser runtime evidence producer
29
-
30
- Usage:
31
- my-frontend-observer <command> [options]
32
-
28
+ const TOP_LEVEL_HELP = `my-frontend-observer - local-first browser runtime evidence producer
29
+
30
+ Usage:
31
+ my-frontend-observer <command> [options]
32
+
33
33
  Common workflow:
34
34
  init Initialize project-local Observer configuration and state.
35
35
  capture <alias> Capture one project-configured observation under a human alias.
@@ -38,36 +38,36 @@ Common workflow:
38
38
 
39
39
  Advanced:
40
40
  observe Capture one bounded browser observation and persist
41
- it as a portable artifact.
42
- compare Compare two persisted observations and write a
43
- structured comparison artifact.
44
- approve-baseline Explicitly approve and persist one already-authored
45
- persistent baseline contract against the observation
46
- it claims to approve.
47
- save-change-contract Validate and persist one already-authored per-change
48
- contract so it can later be evaluated.
49
- evaluate-contract Evaluate a candidate change against an approved
50
- baseline, a per-change contract, and existing
51
- before/after/comparison evidence, and persist the
52
- result.
53
- import-reference Validate and persist one local external design-
54
- reference image as a new, unapproved
55
- external-reference artifact.
56
- approve-reference Explicitly approve one already-imported
57
- external-reference artifact, persisting a new
58
- approved artifact instance.
59
- evaluate-reference-fidelity Evaluate whether a candidate observation
60
- satisfies an external reference's selected design
61
- requirements, gated by reference adequacy,
62
- reference/candidate compatibility, and explicit
63
- region-to-target bindings. Prints a structured
64
- result; persists nothing.
65
-
66
- Options:
67
- --help Show this help.
68
- --version Print the package version.
69
-
70
- Run "my-frontend-observer <command> --help" for command-specific options.
41
+ it as a portable artifact.
42
+ compare Compare two persisted observations and write a
43
+ structured comparison artifact.
44
+ approve-baseline Explicitly approve and persist one already-authored
45
+ persistent baseline contract against the observation
46
+ it claims to approve.
47
+ save-change-contract Validate and persist one already-authored per-change
48
+ contract so it can later be evaluated.
49
+ evaluate-contract Evaluate a candidate change against an approved
50
+ baseline, a per-change contract, and existing
51
+ before/after/comparison evidence, and persist the
52
+ result.
53
+ import-reference Validate and persist one local external design-
54
+ reference image as a new, unapproved
55
+ external-reference artifact.
56
+ approve-reference Explicitly approve one already-imported
57
+ external-reference artifact, persisting a new
58
+ approved artifact instance.
59
+ evaluate-reference-fidelity Evaluate whether a candidate observation
60
+ satisfies an external reference's selected design
61
+ requirements, gated by reference adequacy,
62
+ reference/candidate compatibility, and explicit
63
+ region-to-target bindings. Prints a structured
64
+ result; persists nothing.
65
+
66
+ Options:
67
+ --help Show this help.
68
+ --version Print the package version.
69
+
70
+ Run "my-frontend-observer <command> --help" for command-specific options.
71
71
  `;
72
72
  const VIEW_HELP = `Usage:
73
73
  my-frontend-observer view [--root <evidence-root>] [--bindings-file <json-file>] [--context-file <json-file>] [options]
@@ -78,71 +78,77 @@ Project or standalone input:
78
78
  used and no initialized project or alias catalog is required.
79
79
  Without --root, view requires an initialized project and
80
80
  discovers its managed evidence root and alias metadata.
81
-
82
- Options:
83
- --port <n> TCP port to bind, in [0, 65535]. Defaults to ${DEFAULT_VIEWER_PORT}.
84
- An explicit alternate port is a different web origin than
85
- the default - an installed PWA is not portable across
86
- origins. If the requested port is already in use, this
87
- command fails with an actionable error; it never silently
88
- falls back to a different port.
89
- --bindings-file <json-file> Local JSON file of the form
90
- { "bindings": [ { "referenceRegion": "...", "runtimeTarget": "..." } ] }
91
- (the exact same operational wrapper format as
92
- \`evaluate-reference-fidelity --bindings-file\`, sharing its
93
- parser). Explicit, session-only viewer input: read once at
94
- startup, never persisted, never written into any Observer
95
- artifact, and never exposed as a path to the browser. Its
96
- declarations become available for on-demand binding/
97
- fidelity evaluation once a reference and candidate are
98
- explicitly selected in the viewer. Reference-specific
99
- validity (region existence, etc.) is checked when a
100
- reference is actually selected, not at startup - only the
101
- file's own readability/JSON/wrapper shape is validated at
102
- startup. Omit to run with no binding declarations (the
103
- viewer remains fully usable; cross-selection stays
104
- disabled).
105
- --context-file <json-file> Local JSON file containing exactly one
106
- BoundedAgentContextArtifact value directly (no wrapper
107
- object) - e.g. { "artifactKind":
108
- "my-frontend-observer/bounded-agent-context",
109
- "schemaVersion": "1.0.0", ... }. Explicit, session-only
110
- viewer input: read once at startup, validated through the
111
- existing canonical isValidBoundedAgentContextArtifact,
112
- held only in server memory, never persisted, never written
113
- into any Observer artifact, and never exposed as a path to
114
- the browser. Bounded agent context remains programmatic-
115
- only as an Observer-produced contract - this command does
116
- not add a way to generate, save, or write one; the viewer
117
- never rebuilds it (no projectBoundedAgentContext call) and
118
- never derives runtime/static correlation (no
119
- deriveRuntimeStaticCorrelations/attachRuntimeStaticCorrelations
120
- call) - it only displays the exact context it was given.
121
- A recognized artifactKind with a schemaVersion other than
122
- the currently supported one starts the viewer showing an
123
- honest "unsupported version" context state rather than
124
- failing. An unreadable file, invalid JSON, wrong
125
- artifactKind, or a structurally invalid current-schema
126
- artifact fails startup clearly. Omit to run with no
127
- bounded context supplied (the viewer remains fully usable;
128
- the context panel says none was supplied). May be combined
129
- with --bindings-file.
130
- --no-open Do not attempt to open the system default browser after
131
- the server starts. Browser auto-open is a best-effort
132
- convenience only: its failure is never fatal and never
133
- affects server startup success.
134
- --help Show this help.
135
-
136
- Starts one Node HTTP server bound only to 127.0.0.1, serving the built
137
- React + TypeScript + Vite viewer application (and its PWA manifest/service
138
- worker) plus one minimal read-only status endpoint. The server never writes
139
- to the supplied evidence root, never exposes it as a generic static
140
- directory, and never launches a browser observation. The process keeps
141
- running (serving the viewer) until interrupted. On success, prints the
142
- viewer URL and exits only when the server stops. On invalid syntax, a
143
- missing/non-directory --root, an invalid --port, or a port already in use,
144
- prints structured diagnostics to stderr and exits nonzero without starting
145
- a server.
81
+
82
+ Options:
83
+ --port <n> TCP port to bind, in [0, 65535]. Defaults to ${DEFAULT_VIEWER_PORT}.
84
+ An explicit alternate port is a different web origin than
85
+ the default - an installed PWA is not portable across
86
+ origins. If the requested port is already in use, this
87
+ command fails with an actionable error; it never silently
88
+ falls back to a different port.
89
+ --bindings-file <json-file> Local JSON file of the form
90
+ { "bindings": [ { "referenceRegion": "...", "runtimeTarget": "..." } ] }
91
+ (the exact same operational wrapper format as
92
+ \`evaluate-reference-fidelity --bindings-file\`, sharing its
93
+ parser). Explicit, session-only viewer input: read once at
94
+ startup, never persisted, never written into any Observer
95
+ artifact, and never exposed as a path to the browser. Its
96
+ declarations become available for on-demand binding/
97
+ fidelity evaluation once a reference and candidate are
98
+ explicitly selected in the viewer. Reference-specific
99
+ validity (region existence, etc.) is checked when a
100
+ reference is actually selected, not at startup - only the
101
+ file's own readability/JSON/wrapper shape is validated at
102
+ startup. Omit to run with no binding declarations (the
103
+ viewer remains fully usable; cross-selection stays
104
+ disabled).
105
+ --context-file <json-file> Local JSON file containing exactly one
106
+ BoundedAgentContextArtifact value directly (no wrapper
107
+ object) - e.g. { "artifactKind":
108
+ "my-frontend-observer/bounded-agent-context",
109
+ "schemaVersion": "1.0.0", ... }. Explicit, session-only
110
+ viewer input: read once at startup, validated through the
111
+ existing canonical isValidBoundedAgentContextArtifact,
112
+ held only in server memory, never persisted, never written
113
+ into any Observer artifact, and never exposed as a path to
114
+ the browser. Bounded agent context remains programmatic-
115
+ only as an Observer-produced contract - this command does
116
+ not add a way to generate, save, or write one; the viewer
117
+ never rebuilds it (no projectBoundedAgentContext call) and
118
+ never derives runtime/static correlation (no
119
+ deriveRuntimeStaticCorrelations/attachRuntimeStaticCorrelations
120
+ call) - it only displays the exact context it was given.
121
+ A recognized artifactKind with a schemaVersion other than
122
+ the currently supported one starts the viewer showing an
123
+ honest "unsupported version" context state rather than
124
+ failing. An unreadable file, invalid JSON, wrong
125
+ artifactKind, or a structurally invalid current-schema
126
+ artifact fails startup clearly. Omit to run with no
127
+ bounded context supplied (the viewer remains fully usable;
128
+ the context panel says none was supplied). May be combined
129
+ with --bindings-file.
130
+ --no-open Do not attempt to open the system default browser after
131
+ the server starts. Browser auto-open is a best-effort
132
+ convenience only: its failure is never fatal and never
133
+ affects server startup success.
134
+ --help Show this help.
135
+
136
+ Starts one Node HTTP server bound only to 127.0.0.1, serving the built
137
+ React + TypeScript + Vite viewer application (and its PWA manifest/service
138
+ worker) plus its local evidence API. With --root the session is read-only
139
+ and never writes to the supplied evidence root. Without --root the
140
+ project-aware session also enables local visual-annotation authoring: the
141
+ viewer page may save immutable annotations, promote selected confirmed
142
+ runtime intent into a change contract, and materialize selected confirmed
143
+ reference intent into a new imported reference revision, all inside the
144
+ project's managed evidence root. The server never edits target source,
145
+ never approves baselines or references, never exposes the evidence root as
146
+ a generic static directory, and never launches a browser observation. The
147
+ process keeps running (serving the viewer) until interrupted. On success,
148
+ prints the viewer URL and exits only when the server stops. On invalid
149
+ syntax, a missing/non-directory --root, an invalid --port, or a port
150
+ already in use, prints structured diagnostics to stderr and exits nonzero
151
+ without starting a server.
146
152
  `;
147
153
  const INIT_HELP = `Usage:
148
154
  my-frontend-observer init --url <loopback-url> [options]
@@ -178,367 +184,367 @@ Options:
178
184
 
179
185
  Exit codes: PASS 0; FAIL 1; REVIEW_REQUIRED 2; BLOCKED 3.
180
186
  `;
181
- const OBSERVE_HELP = `Usage:
182
- my-frontend-observer observe --url <loopback-url> [options]
183
-
184
- Required:
185
- --url <url> Loopback target URL (http/https, localhost/127.x.x.x/::1 only).
186
-
187
- Options:
188
- --viewport <WIDTHxHEIGHT> Viewport size, e.g. 1280x720.
189
- --target <id=selector> An explicit CSS-shorthand observation target.
190
- Repeatable. Parsed on the first "=" only, so
191
- selectors containing "=" (e.g.
192
- button[data-state="active"]) are preserved
193
- intact. Cannot be combined with --targets-file.
194
- --targets-file <json-file> Loads structured semantic observation targets
195
- from a local JSON file: { "targets": [ { "name":
196
- "...", "locators": [ { "kind": "role"|"id"|
197
- "data-attribute"|"semantic-element"|"css"|"text",
198
- ... } ] } ] }. Locator order within a target is
199
- the fallback order. Relative paths resolve from
200
- the current working directory; the file path
201
- itself is never persisted into the artifact or
202
- included in the observation's request identity.
203
- Cannot be combined with --target.
204
- --scroll-scenario-file <json-file>
205
- Loads one bounded runtime scroll scenario from a
206
- local JSON file: { "action": { "kind":
207
- "window-scroll-by"|"target-scroll-by", ...,
208
- "deltaX": <int>, "deltaY": <int> } }.
209
- "target-scroll-by" additionally requires a
210
- "target" naming a configured stable target.
211
- Exactly zero or one scenario per observation.
212
- Relative paths resolve from the current working
213
- directory; the file path itself is never
214
- persisted into the artifact or included in the
215
- observation's request identity. May be combined
216
- with either --target or --targets-file.
217
- --state-file <json-file> Loads explicit, caller-declared frontend state
218
- identity from a local JSON file: { "theme":
219
- "...", "applicationState": "...",
220
- "authenticatedState": "authenticated"|
221
- "unauthenticated" } (each field independently
222
- optional; at least one required). Never inferred
223
- by the observer from screenshot pixels, CSS, DOM,
224
- or URLs - this is caller-declared metadata only,
225
- used solely for later comparability/compatibility
226
- evaluation. Relative paths resolve from the
227
- current working directory; the file path itself
228
- is never persisted into the artifact or included
229
- in the observation's request identity.
230
- --output <directory> Portable, relative output location for the
231
- observation artifact.
232
- --timeout <ms> Overall request timeout in milliseconds.
233
- --help Show this help.
234
-
235
- On success, prints a concise result and exits 0. On invalid syntax, invalid
236
- request, unsafe/failed navigation, or a failed artifact write, prints
237
- structured diagnostics to stderr and exits nonzero. No progress output is
238
- printed during a normal capture.
187
+ const OBSERVE_HELP = `Usage:
188
+ my-frontend-observer observe --url <loopback-url> [options]
189
+
190
+ Required:
191
+ --url <url> Loopback target URL (http/https, localhost/127.x.x.x/::1 only).
192
+
193
+ Options:
194
+ --viewport <WIDTHxHEIGHT> Viewport size, e.g. 1280x720.
195
+ --target <id=selector> An explicit CSS-shorthand observation target.
196
+ Repeatable. Parsed on the first "=" only, so
197
+ selectors containing "=" (e.g.
198
+ button[data-state="active"]) are preserved
199
+ intact. Cannot be combined with --targets-file.
200
+ --targets-file <json-file> Loads structured semantic observation targets
201
+ from a local JSON file: { "targets": [ { "name":
202
+ "...", "locators": [ { "kind": "role"|"id"|
203
+ "data-attribute"|"semantic-element"|"css"|"text",
204
+ ... } ] } ] }. Locator order within a target is
205
+ the fallback order. Relative paths resolve from
206
+ the current working directory; the file path
207
+ itself is never persisted into the artifact or
208
+ included in the observation's request identity.
209
+ Cannot be combined with --target.
210
+ --scroll-scenario-file <json-file>
211
+ Loads one bounded runtime scroll scenario from a
212
+ local JSON file: { "action": { "kind":
213
+ "window-scroll-by"|"target-scroll-by", ...,
214
+ "deltaX": <int>, "deltaY": <int> } }.
215
+ "target-scroll-by" additionally requires a
216
+ "target" naming a configured stable target.
217
+ Exactly zero or one scenario per observation.
218
+ Relative paths resolve from the current working
219
+ directory; the file path itself is never
220
+ persisted into the artifact or included in the
221
+ observation's request identity. May be combined
222
+ with either --target or --targets-file.
223
+ --state-file <json-file> Loads explicit, caller-declared frontend state
224
+ identity from a local JSON file: { "theme":
225
+ "...", "applicationState": "...",
226
+ "authenticatedState": "authenticated"|
227
+ "unauthenticated" } (each field independently
228
+ optional; at least one required). Never inferred
229
+ by the observer from screenshot pixels, CSS, DOM,
230
+ or URLs - this is caller-declared metadata only,
231
+ used solely for later comparability/compatibility
232
+ evaluation. Relative paths resolve from the
233
+ current working directory; the file path itself
234
+ is never persisted into the artifact or included
235
+ in the observation's request identity.
236
+ --output <directory> Portable, relative output location for the
237
+ observation artifact.
238
+ --timeout <ms> Overall request timeout in milliseconds.
239
+ --help Show this help.
240
+
241
+ On success, prints a concise result and exits 0. On invalid syntax, invalid
242
+ request, unsafe/failed navigation, or a failed artifact write, prints
243
+ structured diagnostics to stderr and exits nonzero. No progress output is
244
+ printed during a normal capture.
239
245
  `;
240
- const COMPARE_HELP = `Usage:
241
- my-frontend-observer compare --before <observation-artifact-root> --after <observation-artifact-root> --output <directory> [options]
242
-
243
- Required:
244
- --before <path> Root directory of the "before" persisted
245
- observation artifact (the directory containing
246
- its manifest.json).
247
- --after <path> Root directory of the "after" persisted
248
- observation artifact.
249
- --output <directory> Portable, relative output location for the
250
- comparison artifact.
251
-
252
- Options:
253
- --config-file <json-file> Loads a comparison configuration from a local
254
- JSON file: { "geometryTolerancePx": <0-10>,
255
- "expectedDependencies": [ { "cause": { "target":
256
- "...", "property": "x"|"y"|"width"|"height",
257
- "direction": "increase"|"decrease"|"change"|
258
- "unchanged" }, "effect": { ... same shape ... },
259
- "source": "explicit-config" } ] }. Relative
260
- paths resolve from the current working
261
- directory; the file path itself is never
262
- persisted into the artifact or included in the
263
- comparison request identity. Without
264
- --config-file, geometryTolerancePx defaults to
265
- 0.5 with no declared dependencies.
266
- --help Show this help.
267
-
268
- Comparison reads two already-persisted observation artifacts and derives
269
- evidence purely from their existing content - it never launches a browser
270
- and never re-observes either target. On success, prints a concise result
271
- and exits 0, including when the two observations are found to be
272
- "incomparable" (that is itself a successful comparison outcome, not a
273
- failure). On invalid syntax, an unreadable or structurally invalid source
274
- artifact, invalid configuration, or a failed artifact write, prints
275
- structured diagnostics to stderr and exits nonzero. No progress output is
276
- printed during a normal comparison.
246
+ const COMPARE_HELP = `Usage:
247
+ my-frontend-observer compare --before <observation-artifact-root> --after <observation-artifact-root> --output <directory> [options]
248
+
249
+ Required:
250
+ --before <path> Root directory of the "before" persisted
251
+ observation artifact (the directory containing
252
+ its manifest.json).
253
+ --after <path> Root directory of the "after" persisted
254
+ observation artifact.
255
+ --output <directory> Portable, relative output location for the
256
+ comparison artifact.
257
+
258
+ Options:
259
+ --config-file <json-file> Loads a comparison configuration from a local
260
+ JSON file: { "geometryTolerancePx": <0-10>,
261
+ "expectedDependencies": [ { "cause": { "target":
262
+ "...", "property": "x"|"y"|"width"|"height",
263
+ "direction": "increase"|"decrease"|"change"|
264
+ "unchanged" }, "effect": { ... same shape ... },
265
+ "source": "explicit-config" } ] }. Relative
266
+ paths resolve from the current working
267
+ directory; the file path itself is never
268
+ persisted into the artifact or included in the
269
+ comparison request identity. Without
270
+ --config-file, geometryTolerancePx defaults to
271
+ 0.5 with no declared dependencies.
272
+ --help Show this help.
273
+
274
+ Comparison reads two already-persisted observation artifacts and derives
275
+ evidence purely from their existing content - it never launches a browser
276
+ and never re-observes either target. On success, prints a concise result
277
+ and exits 0, including when the two observations are found to be
278
+ "incomparable" (that is itself a successful comparison outcome, not a
279
+ failure). On invalid syntax, an unreadable or structurally invalid source
280
+ artifact, invalid configuration, or a failed artifact write, prints
281
+ structured diagnostics to stderr and exits nonzero. No progress output is
282
+ printed during a normal comparison.
277
283
  `;
278
- const APPROVE_BASELINE_HELP = `Usage:
279
- my-frontend-observer approve-baseline --observation <observation-artifact-root> --contract-file <json-file> --output <directory>
280
-
281
- Required:
282
- --observation <path> Root directory of the persisted observation
283
- artifact (the directory containing its
284
- manifest.json) that this baseline claims to
285
- approve.
286
- --contract-file <json-file> Local JSON file containing one already-authored
287
- persistent baseline contract (the raw contract
288
- value, no wrapper field). Relative paths resolve
289
- from the current working directory; the file
290
- path itself is never persisted or included in
291
- any identity.
292
- --output <directory> Portable, relative output location for the
293
- baseline artifact.
294
-
295
- Options:
296
- --help Show this help.
297
-
298
- This command is the only explicit baseline-approval act in the observer -
299
- approval is never inferred from a successful comparison or evaluation. The
300
- supplied contract's source-observation reference must match the supplied
301
- observation artifact's stable identity; a mismatched or unrelated observation
302
- is rejected. Any \`supersedesBaselineId\` already authored in the contract is
303
- preserved exactly - this command never discovers, infers, or deletes a prior
304
- baseline. Remains local and non-mutating: it never launches a browser and
305
- never modifies the source observation or any existing baseline artifact. On
306
- success, prints a concise result and exits 0. On invalid syntax, an
307
- unreadable/malformed contract file, a non-baseline contract, a
308
- structurally invalid contract, a source-observation mismatch, an existing
309
- artifact collision, or a persistence failure, prints structured diagnostics
310
- to stderr and exits nonzero.
284
+ const APPROVE_BASELINE_HELP = `Usage:
285
+ my-frontend-observer approve-baseline --observation <observation-artifact-root> --contract-file <json-file> --output <directory>
286
+
287
+ Required:
288
+ --observation <path> Root directory of the persisted observation
289
+ artifact (the directory containing its
290
+ manifest.json) that this baseline claims to
291
+ approve.
292
+ --contract-file <json-file> Local JSON file containing one already-authored
293
+ persistent baseline contract (the raw contract
294
+ value, no wrapper field). Relative paths resolve
295
+ from the current working directory; the file
296
+ path itself is never persisted or included in
297
+ any identity.
298
+ --output <directory> Portable, relative output location for the
299
+ baseline artifact.
300
+
301
+ Options:
302
+ --help Show this help.
303
+
304
+ This command is the only explicit baseline-approval act in the observer -
305
+ approval is never inferred from a successful comparison or evaluation. The
306
+ supplied contract's source-observation reference must match the supplied
307
+ observation artifact's stable identity; a mismatched or unrelated observation
308
+ is rejected. Any \`supersedesBaselineId\` already authored in the contract is
309
+ preserved exactly - this command never discovers, infers, or deletes a prior
310
+ baseline. Remains local and non-mutating: it never launches a browser and
311
+ never modifies the source observation or any existing baseline artifact. On
312
+ success, prints a concise result and exits 0. On invalid syntax, an
313
+ unreadable/malformed contract file, a non-baseline contract, a
314
+ structurally invalid contract, a source-observation mismatch, an existing
315
+ artifact collision, or a persistence failure, prints structured diagnostics
316
+ to stderr and exits nonzero.
311
317
  `;
312
- const SAVE_CHANGE_CONTRACT_HELP = `Usage:
313
- my-frontend-observer save-change-contract --contract-file <json-file> --output <directory>
314
-
315
- Required:
316
- --contract-file <json-file> Local JSON file containing one already-authored
317
- per-change contract (the raw contract value, no
318
- wrapper field). Relative paths resolve from the
319
- current working directory; the file path itself
320
- is never persisted or included in any identity.
321
- --output <directory> Portable, relative output location for the
322
- change-contract artifact.
323
-
324
- Options:
325
- --help Show this help.
326
-
327
- This command validates and persists a per-change contract only - it does not
328
- approve anything. Any \`supersedesBaselineClauseIds\` already authored on a
329
- clause is preserved exactly; resolving those references against a particular
330
- baseline remains \`evaluate-contract\`'s responsibility, not this command's.
331
- Remains local and non-mutating. On success, prints a concise result and
332
- exits 0. On invalid syntax, an unreadable/malformed contract file, a
333
- non-change contract (e.g. a persistent baseline contract), a structurally
334
- invalid contract (including an authored \`unexpected\` category, which is
335
- never a valid authored scope), an existing artifact collision, or a
336
- persistence failure, prints structured diagnostics to stderr and exits
337
- nonzero.
318
+ const SAVE_CHANGE_CONTRACT_HELP = `Usage:
319
+ my-frontend-observer save-change-contract --contract-file <json-file> --output <directory>
320
+
321
+ Required:
322
+ --contract-file <json-file> Local JSON file containing one already-authored
323
+ per-change contract (the raw contract value, no
324
+ wrapper field). Relative paths resolve from the
325
+ current working directory; the file path itself
326
+ is never persisted or included in any identity.
327
+ --output <directory> Portable, relative output location for the
328
+ change-contract artifact.
329
+
330
+ Options:
331
+ --help Show this help.
332
+
333
+ This command validates and persists a per-change contract only - it does not
334
+ approve anything. Any \`supersedesBaselineClauseIds\` already authored on a
335
+ clause is preserved exactly; resolving those references against a particular
336
+ baseline remains \`evaluate-contract\`'s responsibility, not this command's.
337
+ Remains local and non-mutating. On success, prints a concise result and
338
+ exits 0. On invalid syntax, an unreadable/malformed contract file, a
339
+ non-change contract (e.g. a persistent baseline contract), a structurally
340
+ invalid contract (including an authored \`unexpected\` category, which is
341
+ never a valid authored scope), an existing artifact collision, or a
342
+ persistence failure, prints structured diagnostics to stderr and exits
343
+ nonzero.
338
344
  `;
339
- const EVALUATE_CONTRACT_HELP = `Usage:
340
- my-frontend-observer evaluate-contract --before <observation-artifact-root> --after <observation-artifact-root> --comparison <comparison-artifact-root> --baseline <baseline-contract-artifact-root> --change <per-change-contract-artifact-root> --output <directory> [--enforce]
341
-
342
- Required:
343
- --before <path> Root directory of the "before" persisted observation
344
- artifact.
345
- --after <path> Root directory of the "after" persisted observation
346
- artifact.
347
- --comparison <path> Root directory of the already-persisted comparison
348
- artifact for that before/after pair.
349
- --baseline <path> Root directory of the already-approved persistent
350
- baseline contract artifact.
351
- --change <path> Root directory of the already-persisted per-change
352
- contract artifact.
353
- --output <directory> Portable, relative output location for the
354
- evaluation artifact.
355
-
356
- Options:
357
- --enforce Make a FAIL verdict produce a nonzero process exit status. A
358
- FAIL evaluation is always persisted and printed identically
359
- with or without this flag - it changes only the process exit
360
- code, never evaluation identity, contents, or persistence.
361
- --help Show this help.
362
-
363
- This command never launches a browser, never re-resolves targets, and never
364
- recomputes comparison or relationship evidence - it reads the already-
365
- persisted before/after observations and comparison exactly as given and
366
- evaluates the supplied baseline/change contracts against them exactly once.
367
- A FAIL verdict (a found regression or unsatisfied contract clause) is a
368
- successful, persisted evaluation outcome, not an execution error; without
369
- --enforce it exits 0 like PASS. On success (evaluation constructed and
370
- persisted, verdict PASS, or verdict FAIL without --enforce), prints a
371
- concise result and exits 0. With --enforce and verdict FAIL, prints the same
372
- result and exits nonzero. On invalid syntax, an unreadable/malformed/
373
- incoherent source artifact, or a persistence failure (evaluation could not
374
- even be constructed), prints structured diagnostics to stderr, persists
375
- nothing, and exits nonzero.
345
+ const EVALUATE_CONTRACT_HELP = `Usage:
346
+ my-frontend-observer evaluate-contract --before <observation-artifact-root> --after <observation-artifact-root> --comparison <comparison-artifact-root> --baseline <baseline-contract-artifact-root> --change <per-change-contract-artifact-root> --output <directory> [--enforce]
347
+
348
+ Required:
349
+ --before <path> Root directory of the "before" persisted observation
350
+ artifact.
351
+ --after <path> Root directory of the "after" persisted observation
352
+ artifact.
353
+ --comparison <path> Root directory of the already-persisted comparison
354
+ artifact for that before/after pair.
355
+ --baseline <path> Root directory of the already-approved persistent
356
+ baseline contract artifact.
357
+ --change <path> Root directory of the already-persisted per-change
358
+ contract artifact.
359
+ --output <directory> Portable, relative output location for the
360
+ evaluation artifact.
361
+
362
+ Options:
363
+ --enforce Make a FAIL verdict produce a nonzero process exit status. A
364
+ FAIL evaluation is always persisted and printed identically
365
+ with or without this flag - it changes only the process exit
366
+ code, never evaluation identity, contents, or persistence.
367
+ --help Show this help.
368
+
369
+ This command never launches a browser, never re-resolves targets, and never
370
+ recomputes comparison or relationship evidence - it reads the already-
371
+ persisted before/after observations and comparison exactly as given and
372
+ evaluates the supplied baseline/change contracts against them exactly once.
373
+ A FAIL verdict (a found regression or unsatisfied contract clause) is a
374
+ successful, persisted evaluation outcome, not an execution error; without
375
+ --enforce it exits 0 like PASS. On success (evaluation constructed and
376
+ persisted, verdict PASS, or verdict FAIL without --enforce), prints a
377
+ concise result and exits 0. With --enforce and verdict FAIL, prints the same
378
+ result and exits nonzero. On invalid syntax, an unreadable/malformed/
379
+ incoherent source artifact, or a persistence failure (evaluation could not
380
+ even be constructed), prints structured diagnostics to stderr, persists
381
+ nothing, and exits nonzero.
376
382
  `;
377
- const IMPORT_REFERENCE_HELP = `Usage:
378
- my-frontend-observer import-reference <image-file> --output <directory> [options]
379
-
380
- Required:
381
- <image-file> Local path to a PNG, JPEG, or WebP external design-
382
- reference image.
383
- --output <directory> Portable, relative output location for the
384
- external-reference artifact.
385
-
386
- Options:
387
- --label <text> Optional human-readable label, stored as pure
388
- provenance - never part of the reference's logical
389
- identity.
390
- --supersedes <path> Root directory of a prior external-reference
391
- artifact (imported or approved) that this import
392
- explicitly supersedes. The prior artifact is never
393
- modified.
394
- --regions-file <json-file> Local JSON file of the form { "regions": [...] }
395
- declaring explicit, meaningful reference-image
396
- regions (id + a {x, y, width, height} rectangle in
397
- reference-image pixels, origin at the image's
398
- top-left corner). Optional - a reference imported
399
- without this flag behaves exactly as in v0.7 Prompt
400
- 1. Region content participates in the reference's
401
- logical identity; the file path itself never does.
402
- --requirements-file <json-file> Local JSON file of the form
403
- { "requirements": [...] } declaring explicit,
404
- user-selected design requirements over the regions
405
- above - what actually matters for later candidate
406
- evaluation, never inferred merely because a region
407
- property/relationship exists. Each requirement has
408
- a "category" (requested | expected-dependent |
409
- protected | preserved - "unexpected" is never
410
- authorable), a "subject" (a region property, a
411
- region-to-region relationship, or a derived
412
- two-region measurement), and - for property/
413
- measurement subjects - a "tolerance" (exact |
414
- absolute-reference-px | percent; relationship
415
- subjects must omit tolerance). Requires --regions-
416
- file (or an already-present region set) supplying
417
- every region a requirement refers to. Optional -
418
- a reference imported without this flag behaves
419
- exactly as in v0.7 Prompt 1/2. Requirement content
420
- participates in the reference's logical identity.
421
- --applicability-file <json-file> Local JSON file declaring the runtime
422
- frontend state this reference is intended to
423
- represent: { "viewport": { "width", "height" },
424
- "theme": "...", "applicationState": "...",
425
- "authenticatedState": "authenticated"|
426
- "unauthenticated" } (each field independently
427
- optional; at least one required). "viewport" here
428
- is the CSS-pixel runtime viewport the design
429
- represents - distinct from the reference image's
430
- own pixel dimensions, which are never assumed
431
- equal. Never inferred from the image - caller-
432
- declared metadata only, used for later reference/
433
- candidate compatibility evaluation (see
434
- docs/CONTRACTS.md "v0.7 Prompt 4"). Optional - a
435
- reference imported without this flag behaves
436
- exactly as in v0.7 Prompt 1/2/3. Applicability
437
- content participates in the reference's logical
438
- identity.
439
- --help Show this help.
440
-
441
- Detects the image format from its header bytes only (never from the file
442
- extension), reads its pixel dimensions from the same bounded header bytes
443
- (never decoding pixel data), and persists a new external-reference artifact
444
- in the "imported" lifecycle state - importing never approves it. On success,
445
- prints a concise result (including the accepted region/requirement counts,
446
- the resulting reference-side requirement adequacy: adequate, partial, or
447
- inadequate, and whether applicability was declared) and exits 0. On an
448
- unreadable file, an unsupported or undetectable format, invalid/out-of-bound
449
- dimensions, an over-limit file size, an unresolvable --supersedes target, an
450
- invalid region (missing/duplicate/malformed id, non-finite/negative/zero
451
- geometry, a region extending outside the image, or more than the bounded
452
- maximum region count), an invalid requirement (unsupported category/
453
- property/measurement/relationship, a tolerance that is missing/inapplicable/
454
- out of bounds, a reference to an unknown region id, a duplicate requirement
455
- subject, or more than the bounded maximum requirement count), or invalid
456
- applicability (an out-of-bound viewport, an invalid state label, an
457
- unsupported authenticatedState value, or an empty applicability object),
458
- prints structured diagnostics to stderr and exits nonzero.
383
+ const IMPORT_REFERENCE_HELP = `Usage:
384
+ my-frontend-observer import-reference <image-file> --output <directory> [options]
385
+
386
+ Required:
387
+ <image-file> Local path to a PNG, JPEG, or WebP external design-
388
+ reference image.
389
+ --output <directory> Portable, relative output location for the
390
+ external-reference artifact.
391
+
392
+ Options:
393
+ --label <text> Optional human-readable label, stored as pure
394
+ provenance - never part of the reference's logical
395
+ identity.
396
+ --supersedes <path> Root directory of a prior external-reference
397
+ artifact (imported or approved) that this import
398
+ explicitly supersedes. The prior artifact is never
399
+ modified.
400
+ --regions-file <json-file> Local JSON file of the form { "regions": [...] }
401
+ declaring explicit, meaningful reference-image
402
+ regions (id + a {x, y, width, height} rectangle in
403
+ reference-image pixels, origin at the image's
404
+ top-left corner). Optional - a reference imported
405
+ without this flag behaves exactly as in v0.7 Prompt
406
+ 1. Region content participates in the reference's
407
+ logical identity; the file path itself never does.
408
+ --requirements-file <json-file> Local JSON file of the form
409
+ { "requirements": [...] } declaring explicit,
410
+ user-selected design requirements over the regions
411
+ above - what actually matters for later candidate
412
+ evaluation, never inferred merely because a region
413
+ property/relationship exists. Each requirement has
414
+ a "category" (requested | expected-dependent |
415
+ protected | preserved - "unexpected" is never
416
+ authorable), a "subject" (a region property, a
417
+ region-to-region relationship, or a derived
418
+ two-region measurement), and - for property/
419
+ measurement subjects - a "tolerance" (exact |
420
+ absolute-reference-px | percent; relationship
421
+ subjects must omit tolerance). Requires --regions-
422
+ file (or an already-present region set) supplying
423
+ every region a requirement refers to. Optional -
424
+ a reference imported without this flag behaves
425
+ exactly as in v0.7 Prompt 1/2. Requirement content
426
+ participates in the reference's logical identity.
427
+ --applicability-file <json-file> Local JSON file declaring the runtime
428
+ frontend state this reference is intended to
429
+ represent: { "viewport": { "width", "height" },
430
+ "theme": "...", "applicationState": "...",
431
+ "authenticatedState": "authenticated"|
432
+ "unauthenticated" } (each field independently
433
+ optional; at least one required). "viewport" here
434
+ is the CSS-pixel runtime viewport the design
435
+ represents - distinct from the reference image's
436
+ own pixel dimensions, which are never assumed
437
+ equal. Never inferred from the image - caller-
438
+ declared metadata only, used for later reference/
439
+ candidate compatibility evaluation (see
440
+ docs/CONTRACTS.md "v0.7 Prompt 4"). Optional - a
441
+ reference imported without this flag behaves
442
+ exactly as in v0.7 Prompt 1/2/3. Applicability
443
+ content participates in the reference's logical
444
+ identity.
445
+ --help Show this help.
446
+
447
+ Detects the image format from its header bytes only (never from the file
448
+ extension), reads its pixel dimensions from the same bounded header bytes
449
+ (never decoding pixel data), and persists a new external-reference artifact
450
+ in the "imported" lifecycle state - importing never approves it. On success,
451
+ prints a concise result (including the accepted region/requirement counts,
452
+ the resulting reference-side requirement adequacy: adequate, partial, or
453
+ inadequate, and whether applicability was declared) and exits 0. On an
454
+ unreadable file, an unsupported or undetectable format, invalid/out-of-bound
455
+ dimensions, an over-limit file size, an unresolvable --supersedes target, an
456
+ invalid region (missing/duplicate/malformed id, non-finite/negative/zero
457
+ geometry, a region extending outside the image, or more than the bounded
458
+ maximum region count), an invalid requirement (unsupported category/
459
+ property/measurement/relationship, a tolerance that is missing/inapplicable/
460
+ out of bounds, a reference to an unknown region id, a duplicate requirement
461
+ subject, or more than the bounded maximum requirement count), or invalid
462
+ applicability (an out-of-bound viewport, an invalid state label, an
463
+ unsupported authenticatedState value, or an empty applicability object),
464
+ prints structured diagnostics to stderr and exits nonzero.
459
465
  `;
460
- const APPROVE_REFERENCE_HELP = `Usage:
461
- my-frontend-observer approve-reference --reference <external-reference-artifact-root> --output <directory> [options]
462
-
463
- Required:
464
- --reference <path> Root directory of the already-imported
465
- external-reference artifact (the directory
466
- containing its manifest.json) to approve.
467
- --output <directory> Portable, relative output location for the newly
468
- persisted approved artifact.
469
-
470
- Options:
471
- --supersedes <path> Root directory of a prior external-reference
472
- artifact (imported or approved) that this approval
473
- explicitly supersedes. The prior artifact is never
474
- modified.
475
- --help Show this help.
476
-
477
- This is the only explicit reference-approval act in the observer - approval
478
- is never inferred from a successful import or from any later fidelity
479
- evaluation. Approving persists a brand-new artifact instance (a fresh
480
- referenceId sharing the imported artifact's referenceRequestId) that carries
481
- a reference back to the imported artifact's image rather than a second copy
482
- of its bytes; the imported artifact's own manifest is never modified. Any
483
- regions, requirements, and applicability already declared on the imported
484
- artifact are carried forward unchanged (not re-validated against new input,
485
- not re-derived) - approval never adds, removes, or edits regions,
486
- requirements, or applicability. Only a reference currently in the "imported"
487
- lifecycle state can be approved. On success, prints a concise result
488
- (including the carried-forward region/requirement counts, reference-side
489
- requirement adequacy, and whether applicability was declared) and exits 0.
490
- On an unreadable/malformed --reference target, a target that is not in the
491
- "imported" state, an unresolvable --supersedes target, or a persistence
492
- failure, prints structured diagnostics to stderr and exits nonzero.
466
+ const APPROVE_REFERENCE_HELP = `Usage:
467
+ my-frontend-observer approve-reference --reference <external-reference-artifact-root> --output <directory> [options]
468
+
469
+ Required:
470
+ --reference <path> Root directory of the already-imported
471
+ external-reference artifact (the directory
472
+ containing its manifest.json) to approve.
473
+ --output <directory> Portable, relative output location for the newly
474
+ persisted approved artifact.
475
+
476
+ Options:
477
+ --supersedes <path> Root directory of a prior external-reference
478
+ artifact (imported or approved) that this approval
479
+ explicitly supersedes. The prior artifact is never
480
+ modified.
481
+ --help Show this help.
482
+
483
+ This is the only explicit reference-approval act in the observer - approval
484
+ is never inferred from a successful import or from any later fidelity
485
+ evaluation. Approving persists a brand-new artifact instance (a fresh
486
+ referenceId sharing the imported artifact's referenceRequestId) that carries
487
+ a reference back to the imported artifact's image rather than a second copy
488
+ of its bytes; the imported artifact's own manifest is never modified. Any
489
+ regions, requirements, and applicability already declared on the imported
490
+ artifact are carried forward unchanged (not re-validated against new input,
491
+ not re-derived) - approval never adds, removes, or edits regions,
492
+ requirements, or applicability. Only a reference currently in the "imported"
493
+ lifecycle state can be approved. On success, prints a concise result
494
+ (including the carried-forward region/requirement counts, reference-side
495
+ requirement adequacy, and whether applicability was declared) and exits 0.
496
+ On an unreadable/malformed --reference target, a target that is not in the
497
+ "imported" state, an unresolvable --supersedes target, or a persistence
498
+ failure, prints structured diagnostics to stderr and exits nonzero.
493
499
  `;
494
- const EVALUATE_REFERENCE_FIDELITY_HELP = `Usage:
495
- my-frontend-observer evaluate-reference-fidelity --reference <external-reference-artifact-root> --candidate <observation-artifact-root> [options]
496
-
497
- Required:
498
- --reference <path> Root directory of an already-imported or already-
499
- approved external-reference artifact (the directory
500
- containing its manifest.json).
501
- --candidate <path> Root directory of the already-persisted candidate
502
- observation artifact to evaluate against it.
503
-
504
- Options:
505
- --bindings-file <json-file> Local JSON file of the form
506
- { "bindings": [ { "referenceRegion": "...",
507
- "runtimeTarget": "..." } ] } declaring which stable
508
- observer runtime target (a configured target name -
509
- see "observe" --target/--targets-file) explicitly
510
- corresponds to each reference region a selected
511
- requirement depends on. Never inferred from
512
- geometry, matching names, or source code - a
513
- binding exists only because this file declares it.
514
- Optional - omitting it (or supplying an empty
515
- "bindings" array) evaluates with no bindings at
516
- all, so every requirement whose subject depends on
517
- a reference region becomes "unavailable".
518
- --enforce Make a FAIL fidelity result produce a nonzero process exit
519
- status. A FAIL result is always printed identically with or
520
- without this flag - it changes only the process exit code,
521
- never the evaluation's content. Has no effect on a
522
- "not-evaluated" result (a reference-adequacy or compatibility
523
- blocker is never treated as a design mismatch).
524
- --help Show this help.
525
-
526
- This command never launches a browser, never re-resolves targets, and
527
- never recomputes reference regions/requirements/adequacy, compatibility, or
528
- bindings - it reads the already-persisted reference and candidate exactly
529
- as given, evaluates the supplied binding declarations, and evaluates every
530
- one of the reference's selected requirements exactly once. It persists
531
- nothing: the result exists only for this invocation. On success, prints a
532
- concise result (reference-side adequacy, compatibility state, the overall
533
- fidelity state - "not-evaluated"/"pass"/"fail" - and a pass/fail/unavailable
534
- requirement breakdown) and exits 0, unless --enforce is given and the
535
- fidelity state is "fail", in which case it exits nonzero. A "not-evaluated"
536
- result (reference adequacy inadequate, or reference/candidate
537
- incompatible) is a successful, structured evaluation outcome, never an
538
- execution error - it always exits 0. On invalid syntax, an unreadable/
539
- malformed --reference or --candidate target, a malformed --bindings-file,
540
- or an invalid/out-of-bound binding declaration, prints structured
541
- diagnostics to stderr and exits nonzero.
500
+ const EVALUATE_REFERENCE_FIDELITY_HELP = `Usage:
501
+ my-frontend-observer evaluate-reference-fidelity --reference <external-reference-artifact-root> --candidate <observation-artifact-root> [options]
502
+
503
+ Required:
504
+ --reference <path> Root directory of an already-imported or already-
505
+ approved external-reference artifact (the directory
506
+ containing its manifest.json).
507
+ --candidate <path> Root directory of the already-persisted candidate
508
+ observation artifact to evaluate against it.
509
+
510
+ Options:
511
+ --bindings-file <json-file> Local JSON file of the form
512
+ { "bindings": [ { "referenceRegion": "...",
513
+ "runtimeTarget": "..." } ] } declaring which stable
514
+ observer runtime target (a configured target name -
515
+ see "observe" --target/--targets-file) explicitly
516
+ corresponds to each reference region a selected
517
+ requirement depends on. Never inferred from
518
+ geometry, matching names, or source code - a
519
+ binding exists only because this file declares it.
520
+ Optional - omitting it (or supplying an empty
521
+ "bindings" array) evaluates with no bindings at
522
+ all, so every requirement whose subject depends on
523
+ a reference region becomes "unavailable".
524
+ --enforce Make a FAIL fidelity result produce a nonzero process exit
525
+ status. A FAIL result is always printed identically with or
526
+ without this flag - it changes only the process exit code,
527
+ never the evaluation's content. Has no effect on a
528
+ "not-evaluated" result (a reference-adequacy or compatibility
529
+ blocker is never treated as a design mismatch).
530
+ --help Show this help.
531
+
532
+ This command never launches a browser, never re-resolves targets, and
533
+ never recomputes reference regions/requirements/adequacy, compatibility, or
534
+ bindings - it reads the already-persisted reference and candidate exactly
535
+ as given, evaluates the supplied binding declarations, and evaluates every
536
+ one of the reference's selected requirements exactly once. It persists
537
+ nothing: the result exists only for this invocation. On success, prints a
538
+ concise result (reference-side adequacy, compatibility state, the overall
539
+ fidelity state - "not-evaluated"/"pass"/"fail" - and a pass/fail/unavailable
540
+ requirement breakdown) and exits 0, unless --enforce is given and the
541
+ fidelity state is "fail", in which case it exits nonzero. A "not-evaluated"
542
+ result (reference adequacy inadequate, or reference/candidate
543
+ incompatible) is a successful, structured evaluation outcome, never an
544
+ execution error - it always exits 0. On invalid syntax, an unreadable/
545
+ malformed --reference or --candidate target, a malformed --bindings-file,
546
+ or an invalid/out-of-bound binding declaration, prints structured
547
+ diagnostics to stderr and exits nonzero.
542
548
  `;
543
549
  function parseViewport(raw) {
544
550
  const match = /^(\d+)x(\d+)$/.exec(raw);
@@ -2290,6 +2296,8 @@ async function runViewCommand(argv, io) {
2290
2296
  }
2291
2297
  let root;
2292
2298
  let aliasMetadata;
2299
+ // v0.9 Batch 2: only the normal project-aware form enables annotation authoring; explicit --root stays read-only.
2300
+ let authoringProjectRoot;
2293
2301
  if (parsedArgs.root !== undefined) {
2294
2302
  root = parsedArgs.root;
2295
2303
  }
@@ -2306,6 +2314,7 @@ async function runViewCommand(argv, io) {
2306
2314
  }
2307
2315
  root = projectState.root;
2308
2316
  aliasMetadata = { observationAliasesByRelativeDir: projectState.aliases };
2317
+ authoringProjectRoot = projectState.projectRoot;
2309
2318
  }
2310
2319
  const result = await startViewer({
2311
2320
  root,
@@ -2313,6 +2322,7 @@ async function runViewCommand(argv, io) {
2313
2322
  bindingDeclarations,
2314
2323
  context: contextState,
2315
2324
  ...(aliasMetadata === undefined ? {} : { aliasMetadata }),
2325
+ ...(authoringProjectRoot === undefined ? {} : { authoringProjectRoot }),
2316
2326
  });
2317
2327
  if (!result.ok) {
2318
2328
  for (const diagnostic of result.diagnostics)