@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
@@ -0,0 +1,589 @@
1
+ # v0.9 Demo Foundation
2
+
3
+ ## 1. VERDICT
4
+
5
+ `PASS_V0_9_DEMO_FOUNDATION`
6
+
7
+ A deterministic, offline, version-controlled Observer demo now exists under
8
+ `examples/v09-demo/`, together with a safe materialization script, a
9
+ loopback-only demo server, a fixed external reference image, and automated
10
+ unit, real-HTTP and real-Chromium coverage. The full Observer regression suite
11
+ remains green. No product semantics, package version, dependency or release
12
+ state changed.
13
+
14
+ This is demonstration infrastructure only. It is not a v0.9 product feature and
15
+ it does not release anything.
16
+
17
+ ## 2. Repository identity
18
+
19
+ - Repository: `C:\Users\daile\Projects\my-frontend-observer`
20
+ - Branch: `master`
21
+ - Starting HEAD: `9cedc2e79ed626b9bbced5583e2da605dc14a72c`
22
+ - Package: `@dailephd/my-frontend-observer`
23
+ - Package version: `0.8.1` (unchanged)
24
+
25
+ The planner-known implementation-complete commit was
26
+ `a78a058271564661e2f04a835db04071b135258a`. Local `master` was one commit
27
+ ahead of it at `9cedc2e` (`docs: record v0.9 pre-release readiness`), which is
28
+ exactly the later pre-release readiness work the task anticipates. That commit
29
+ adds only `docs/reports/v0.9-pre-release-readiness.md`. The divergence is
30
+ therefore legitimate continuation work, the tracked worktree was clean at
31
+ entry, and `9cedc2e` was adopted as `STARTING_HEAD`. Nothing was reset,
32
+ rebased, pulled or overwritten.
33
+
34
+ `docs/reports/v0.9-architecture-retrieval.md` carried its intentional
35
+ `skip-worktree` flag (`S`) at preflight and still carries it. It was not
36
+ cleared, restored, overwritten or deleted. No `git clean` and no
37
+ `git reset --hard` was run at any point.
38
+
39
+ ## 3. Purpose
40
+
41
+ The Observer product now has a generic tutorial automation consumer,
42
+ `@dailephd/my-dev-kit-lab@0.4.7`, which already owns Playwright loading,
43
+ Chromium launch, managed processes, HTTP readiness, tutorial scenarios, target
44
+ contracts, locators, assertions, cursor and overlay rendering, recording,
45
+ subtitles, tutorial markdown, manifests and packed acceptance.
46
+
47
+ Observer must not reimplement any of that. This stage therefore builds only the
48
+ deterministic demo application that later Observer-specific tutorial scenarios
49
+ will observe, annotate and compare, plus the small amount of infrastructure
50
+ needed to materialize and serve it on loopback.
51
+
52
+ This is Prompt 1 of three. No tutorial scenario was authored here, no tutorial
53
+ video was generated, and no lab target contract was frozen.
54
+
55
+ ## 4. v0.9 entry state
56
+
57
+ v0.9 product implementation was already complete and unreleased at entry. All
58
+ frozen v0.9 semantics were treated as read-only:
59
+
60
+ `VisualAnnotationArtifact`, annotation identity and persistence, runtime intent
61
+ mappings, confirmation semantics, frontend-contract primitives, contract
62
+ promotion, reference requirement semantics, reference materialization,
63
+ reference approval, project acceptance and viewer authoring security are all
64
+ unchanged by this stage.
65
+
66
+ The demo was built around the completed product, not inside it.
67
+
68
+ ## 5. Demo architecture
69
+
70
+ The demo is a small deterministic HTML, CSS and JavaScript application. No
71
+ frontend framework, no second Vite application and no build system was added,
72
+ and no dependency was added or upgraded.
73
+
74
+ The architecture has three separable pieces:
75
+
76
+ 1. An immutable tracked template under `examples/v09-demo/`.
77
+ 2. A materialization script that copies that template into a caller-provided
78
+ disposable target root.
79
+ 3. A loopback-only static server that serves one materialized (or explicitly
80
+ pointed-at) demo application on a caller-selected port.
81
+
82
+ Geometry lives entirely in CSS custom properties keyed on a single
83
+ `data-demo-state` attribute. The server freezes the requested state into that
84
+ attribute before serving the document. One small classic script owns the only
85
+ two differences CSS cannot express honestly: removing an element from the DOM,
86
+ and pointing an image at a different local file.
87
+
88
+ ## 6. Demo component inventory
89
+
90
+ Tracked template:
91
+
92
+ ```text
93
+ examples/v09-demo/README.md
94
+ examples/v09-demo/app/index.html
95
+ examples/v09-demo/app/styles.css
96
+ examples/v09-demo/app/state.js
97
+ examples/v09-demo/app/assets/brand-mark.svg
98
+ examples/v09-demo/app/assets/asset-primary.svg
99
+ examples/v09-demo/app/assets/asset-alternate.svg
100
+ examples/v09-demo/references/v09-reference.png
101
+ examples/v09-demo/references/v09-reference.provenance.json
102
+ examples/v09-demo/scripts/prepare.mjs
103
+ examples/v09-demo/scripts/server.mjs
104
+ examples/v09-demo/scripts/generate-reference.mjs
105
+ ```
106
+
107
+ The application contains a header, a navigation bar, a hero, a sidebar, a main
108
+ content region, two cards, a call-to-action, a footer and a local illustration
109
+ asset area.
110
+
111
+ ## 7. Stable target identity
112
+
113
+ Every region carries a deliberate `data-demo-target` attribute. This vocabulary
114
+ is the demo's identity contract and does not depend on any incidental CSS
115
+ class:
116
+
117
+ ```text
118
+ header
119
+ navigation
120
+ hero
121
+ sidebar
122
+ content
123
+ card-1
124
+ card-2
125
+ cta
126
+ footer
127
+ asset
128
+ ```
129
+
130
+ A bounded set of `data-testid` attributes exists only where automation needs to
131
+ interact with the page rather than measure it:
132
+
133
+ ```text
134
+ demo-state-badge
135
+ demo-cta-button
136
+ demo-asset-image
137
+ demo-nav-<state> (one per demo state)
138
+ ```
139
+
140
+ `data-testid` was not added mechanically to every element. Observer Viewer
141
+ selectors were deliberately not touched: `viewer/src/` is unchanged, and the
142
+ minimum stable Viewer test ids belong to Prompt 2 after real scenario authoring
143
+ shows what is needed.
144
+
145
+ ## 8. State model
146
+
147
+ Nine frozen states, selected deterministically through the URL query parameter
148
+ `?state=<state>`:
149
+
150
+ ```text
151
+ baseline
152
+ move-hero
153
+ wider-sidebar
154
+ changed-spacing
155
+ move-footer
156
+ removed-card
157
+ changed-asset
158
+ multi-change
159
+ reference
160
+ ```
161
+
162
+ Omitting `?state=` renders `baseline`. An unrecognized state fails closed: the
163
+ server answers HTTP 400 with an explicit invalid-state document that names the
164
+ rejected value (HTML-escaped) and lists the supported states. The application
165
+ itself is never rendered for an invalid state, and no unrelated state is ever
166
+ silently substituted.
167
+
168
+ No state name needed to differ from the task's vocabulary.
169
+
170
+ ## 9. Baseline state
171
+
172
+ Baseline geometry is frozen for the canonical tutorial viewport `1440x900`.
173
+ Real Chromium measurements through the canonical observation path:
174
+
175
+ ```text
176
+ header x=0 y=0 w=1440 h=64
177
+ navigation x=0 y=64 w=1440 h=48
178
+ sidebar x=20 y=132 w=260 h=676
179
+ content x=304 y=132 w=1116 h=676
180
+ hero x=325 y=153 w=894 h=180
181
+ card-1 x=325 y=353 w=300 h=170
182
+ card-2 x=645 y=353 w=300 h=170
183
+ cta x=325 y=543 w=1074 h=72
184
+ asset x=325 y=635 w=1074 h=120
185
+ footer x=0 y=828 w=1440 h=72
186
+ ```
187
+
188
+ Determinism measures actually taken: fixed band heights rather than
189
+ font-dependent ones, a local system font stack, no network font, no remote
190
+ asset, no animation, no transition, no timer, no random value, no current time,
191
+ no network-loaded image, and `overflow: hidden` on the document so a scrollbar
192
+ can never appear and change the usable layout width. In every state, real
193
+ Chromium reports `innerWidth = 1440`, `scrollWidth = 1440` and
194
+ `scrollHeight = 900`.
195
+
196
+ ## 10. Change-state semantics
197
+
198
+ Measured against baseline with real Chromium:
199
+
200
+ 1. `move-hero` - hero `x` increases from 325 to 485, exactly +160px. Hero
201
+ width, `y` and height are unchanged, and every other demo target is
202
+ byte-identical to baseline. This is the clean `Move Right` ->
203
+ `property-increases x` example.
204
+ 2. `wider-sidebar` - sidebar `width` increases from 260 to 400, exactly
205
+ +140px. Sidebar `x`, `y` and height are unchanged, and the header and
206
+ footer do not move. This is the clean `Resize Wider` ->
207
+ `property-increases width` example.
208
+ 3. `changed-spacing` - the measured horizontal gap between the sidebar's right
209
+ edge and the content region's left edge grows from 24px to 96px. Neither
210
+ region's own width changes, and no demo target changes its `y`. This is a
211
+ relationship and measurement change, not a region change.
212
+ 4. `move-footer` - footer `y` decreases from 828 to 764, exactly -64px, while
213
+ footer `x`, width and height are unchanged and every other target is
214
+ identical to baseline. The footer lifts off the bottom edge, which is
215
+ deliberately the shape of a real layout regression.
216
+ 5. `removed-card` - `card-2` is genuinely absent from the document. The
217
+ canonical observation reports `selectionStatus = not-found` and no geometry
218
+ evidence, rather than fabricated geometry. `card-1` and every other target
219
+ are identical to baseline. This demonstrates human intent that v0.9 can
220
+ annotate as a `remove` operation but that the current `ContractPrimitive`
221
+ vocabulary cannot promote into a contract clause.
222
+ 6. `changed-asset` - the illustration image `src` changes from
223
+ `assets/asset-primary.svg` to `assets/asset-alternate.svg`. Every demo
224
+ target's geometry, including the asset container's, is identical to
225
+ baseline, so an asset-sensitive difference is never conflated with geometry.
226
+ 7. `multi-change` - five deterministic differences at once: hero `x` 625,
227
+ sidebar width 400, footer `y` 764, `card-2` absent, and the alternate asset.
228
+ This is the state for exercising selected-only contract promotion and
229
+ selected-only reference materialization. It is deliberately not the primary
230
+ simple tutorial state.
231
+
232
+ ## 11. Reference state
233
+
234
+ `reference` represents a desired design rather than an observed change. It
235
+ differs from baseline in four controlled ways, chosen so a reference workflow
236
+ has one of each kind of difference to work with:
237
+
238
+ - region geometry: sidebar width 320 (baseline 260), hero `x` 497 (baseline
239
+ 325);
240
+ - relationship and measurement: the sidebar-to-content gap is 56px (baseline
241
+ 24px);
242
+ - asset-sensitive area: the alternate local illustration;
243
+ - visual treatment: a different accent colour.
244
+
245
+ The page frame stays comparable: header and footer geometry are identical to
246
+ baseline, and every demo region is still present. The reference is a design,
247
+ not a removal.
248
+
249
+ ## 12. Local asset policy
250
+
251
+ Every asset is a local file committed beneath `examples/v09-demo/`. There is no
252
+ CDN, no Google Fonts, no external image URL, no external API and no
253
+ network-dependent icon library. The demo renders correctly offline.
254
+
255
+ This is enforced two ways. A unit test scans every demo application file and
256
+ fails on any `http://` or `https://` reference (excluding the SVG XML
257
+ namespace declaration, which is an identifier and never fetched). A real
258
+ Chromium test records every network request origin while loading all nine
259
+ states and asserts the only origin contacted is the demo's own loopback server.
260
+
261
+ ## 13. Materialization
262
+
263
+ ```powershell
264
+ node examples/v09-demo/scripts/prepare.mjs <targetRoot>
265
+ ```
266
+
267
+ The target root is required and never defaulted. It is resolved absolutely and
268
+ validated before anything is removed. The script refuses:
269
+
270
+ - a missing, empty or whitespace-only argument;
271
+ - a target root containing a null byte;
272
+ - a filesystem or drive root;
273
+ - any path with fewer than two segments below its root;
274
+ - the user's home directory;
275
+ - the repository root;
276
+ - the immutable demo template, or anything inside it;
277
+ - any directory that contains the repository, which a reset would destroy.
278
+
279
+ Reset semantics are "rematerialize the target root": a rerun destroys and
280
+ recreates the target, so it always ends in the same clean state. There is no
281
+ separate reset command, matching the task's contract.
282
+
283
+ A materialized target contains exactly:
284
+
285
+ ```text
286
+ <targetRoot>/app/index.html
287
+ <targetRoot>/app/styles.css
288
+ <targetRoot>/app/state.js
289
+ <targetRoot>/app/assets/asset-alternate.svg
290
+ <targetRoot>/app/assets/asset-primary.svg
291
+ <targetRoot>/app/assets/brand-mark.svg
292
+ <targetRoot>/server.mjs
293
+ ```
294
+
295
+ That is everything needed to serve the demo without reaching back into the
296
+ template or into any generated Observer evidence. The fixed reference PNG is
297
+ deliberately not copied: it is not needed to serve the application, and Prompt
298
+ 2 imports it through canonical Observer commands from its tracked path.
299
+
300
+ ## 14. Source immutability
301
+
302
+ The tracked template is read-only during materialization. A test takes a
303
+ SHA-256 fingerprint of every file under `examples/v09-demo/` before the
304
+ materialization suite runs and compares it after a destructive rematerialization
305
+ has occurred; the fingerprints must match exactly. A further test asserts that
306
+ nothing new appeared beside the template's own directories, and that every file
307
+ written during the run lives under the selected target root.
308
+
309
+ Materialization destroys and recreates only the explicitly selected disposable
310
+ target root, and only after every containment and safety check has passed.
311
+
312
+ No generated `.frontend-observer` evidence exists anywhere in the template, and
313
+ a test enforces that.
314
+
315
+ ## 15. Demo server
316
+
317
+ `examples/v09-demo/scripts/server.mjs` is a small Node HTTP server using only
318
+ Node built-ins. No dependency was added.
319
+
320
+ Behavior:
321
+
322
+ - binds `127.0.0.1` only; the test reads the bound address back from the OS
323
+ rather than trusting the requested constant;
324
+ - serves only the resolved demo application directory;
325
+ - resolves every request path inside that directory and fails closed on `..`
326
+ segments, `.` segments, backslashes, null bytes and percent-encoded forms of
327
+ the same;
328
+ - serves only a small allowlist of static types (`.html`, `.css`, `.js`,
329
+ `.svg`, `.png`, `.json`); anything else is 404 rather than a guessed type;
330
+ - answers only `GET` and `HEAD`; any other method is 405;
331
+ - sends `cache-control: no-store` and `x-content-type-options: nosniff`.
332
+
333
+ Port selection is always the caller's. `4173` appears nowhere in the file, and
334
+ a test asserts that. The command line requires `--port` and validates it as an
335
+ integer in `1..65535`, rejecting a missing value, a non-integer, `0`, a
336
+ negative value and `65536`. The exported `startDemoServer({ port })` additionally
337
+ accepts `port: 0` for in-process automation, where the OS assigns an ephemeral
338
+ port; this mirrors the repository's existing canonical viewer port contract in
339
+ `src/viewerServer/port.ts`, which documents the same allowance for deterministic
340
+ test use. A test proves the server listens on exactly the port the caller
341
+ selected.
342
+
343
+ An optional `--app-root <dir>` lets the tracked template be served directly
344
+ while editing the demo. A materialized target needs no such flag: the server
345
+ resolves `app/` beside itself.
346
+
347
+ No lab `TutorialTargetContractV1` was frozen or committed. That belongs to
348
+ Prompt 2, once the demo, the Observer project bootstrap and the project-aware
349
+ viewer can be composed with dynamic loopback ports.
350
+
351
+ ## 16. Health and readiness
352
+
353
+ ```text
354
+ GET /health
355
+ ```
356
+
357
+ Answers HTTP 200 with a bounded deterministic JSON document:
358
+
359
+ ```json
360
+ {
361
+ "ok": true,
362
+ "demoId": "my-frontend-observer-v09-demo",
363
+ "schemaVersion": "1.0.0",
364
+ "canonicalViewport": { "width": 1440, "height": 900 },
365
+ "defaultState": "baseline",
366
+ "states": ["baseline", "..."]
367
+ }
368
+ ```
369
+
370
+ There is no current time and no per-run identity in it. A test fetches it twice
371
+ and asserts the two responses are byte-identical, and asserts the health
372
+ response never contains application HTML. `schemaVersion` here describes this
373
+ demo's own readiness document; it is not an Observer artifact schema version
374
+ and no Observer schema was touched.
375
+
376
+ ## 17. Reference PNG
377
+
378
+ `examples/v09-demo/references/v09-reference.png` is a fixed 1440x900 PNG
379
+ (55,876 bytes) captured from the demo's `reference` state.
380
+
381
+ It was produced by `examples/v09-demo/scripts/generate-reference.mjs`, which
382
+ serves the `reference` state on loopback and captures it through the
383
+ repository's own canonical Chromium observation path (`captureViewportInternal`
384
+ from `src/browser/chromiumAdapter.ts`, via the compiled `dist/` module). No
385
+ second screenshot engine was added. The script requires a prior `npm run build`
386
+ and fails with an explicit message if the compiled modules are missing.
387
+
388
+ `examples/v09-demo/references/v09-reference.provenance.json` records the source
389
+ demo state, the canonical viewport, the image format, how the image was
390
+ captured, the exact regeneration command and the intended tutorial use. It is a
391
+ plain record beside the asset: it declares no artifact kind and no Observer
392
+ schema version, and a test asserts both fields are absent.
393
+
394
+ The committed PNG is a fixed demo asset. Nothing regenerates it during an
395
+ ordinary test run; regeneration is an explicit manual command so an intentional
396
+ visual change is reviewed deliberately.
397
+
398
+ No generated `.frontend-observer/` reference artifacts were committed. Import
399
+ and approval of this PNG through canonical Observer commands, inside a
400
+ disposable target, belongs to Prompt 2.
401
+
402
+ ## 18. Unit and server tests
403
+
404
+ `tests/unit/v09DemoTemplate.test.ts` (12 tests) - stable target vocabulary
405
+ present exactly once each and closed to the frozen set, baseline state frozen
406
+ into the tracked document, bounded and unique `data-testid` set, semantic
407
+ elements for the major regions, no remote origin in any demo file, every
408
+ referenced asset present locally, no animation/transition/timer/randomness/
409
+ current time anywhere, no committed Observer evidence, and the reference PNG
410
+ validated through `src/domain/externalReferenceImage.ts` (format detected from
411
+ magic bytes, dimensions `1440x900`, size within the canonical bound) plus its
412
+ provenance record.
413
+
414
+ `tests/unit/v09DemoMaterialization.test.ts` (16 tests) - explicit target
415
+ required; filesystem/drive root, root-like target, home directory, repository
416
+ root, template and anything inside it, repository-containing directory and null
417
+ byte all rejected; clean target produced with exactly the expected file list;
418
+ every file copied byte-for-byte; rerun resets deterministically and discards a
419
+ stale directory and a mutated file; template fingerprints unchanged across the
420
+ destructive run; nothing written outside the selected target; no Observer
421
+ evidence in the target; and the materialized `server.mjs` resolves its
422
+ application root from the target root alone.
423
+
424
+ `tests/unit/v09DemoServer.test.ts` (18 tests) - real HTTP throughout. Port
425
+ argument validation, no hardcoded port, path containment unit coverage,
426
+ single-attribute state rewriting, loopback binding read back from the OS,
427
+ deterministic `/health`, health not confused with application HTML, baseline at
428
+ `/`, every one of the nine states rendering with exactly that state frozen into
429
+ the document, unknown state failing closed with 400 and no application markup,
430
+ HTML escaping of a hostile state value, every local asset served with the right
431
+ content type, wire-level traversal rejection including percent-encoded and
432
+ backslash forms, the static allowlist, method rejection, and a server listening
433
+ on exactly the caller-selected port.
434
+
435
+ All demo unit suites passed.
436
+
437
+ ## 19. Real-browser validation
438
+
439
+ `tests/browser/v09Demo.test.ts` (17 tests) runs against real Chromium at
440
+ `1440x900`, using the repository's existing real-Chromium browser-test
441
+ infrastructure.
442
+
443
+ The geometry suite captures all nine states through the canonical observation
444
+ path and asserts: every required demo target present in baseline; baseline
445
+ rendered at exactly the canonical viewport with the footer flush to the bottom;
446
+ the intended direction and magnitude of each state's primary difference; that
447
+ every unrelated region is untouched in `move-hero`, `wider-sidebar`,
448
+ `move-footer`, `removed-card` and `changed-asset`; that `changed-spacing`
449
+ changes a measured gap and no region's `y`; that `card-2` resolves `not-found`
450
+ with no fabricated geometry when removed; that `multi-change` shows several
451
+ differences at once; that `reference` shows the intended controlled design
452
+ differences while staying comparable; that every demo target stays present,
453
+ unique (never `ambiguous`) and matched in every state except the intentionally
454
+ absent card; and that the major regions expose real landmark roles (`banner`,
455
+ `navigation`, `complementary`, `main`, `contentinfo`) in every state.
456
+
457
+ The DOM suite uses a real page to assert what observation evidence deliberately
458
+ does not carry: the asset `src` swap and its absence elsewhere, every
459
+ tutorial-critical test id present and unique per state, the state badge text and
460
+ `aria-current` navigation marking, the invalid-state page rendering without any
461
+ application markup, that no remote origin is ever contacted, and that no state
462
+ ever needs a scrollbar.
463
+
464
+ All 17 real-browser demo tests passed.
465
+
466
+ The accessibility check is a bounded structural and landmark-role check. It is
467
+ not a full accessibility certification and is not claimed as one. No PWA,
468
+ service-worker or OS-installation claim is made by this stage, because none was
469
+ exercised.
470
+
471
+ ## 20. Scope audit
472
+
473
+ All of the following are `false`:
474
+
475
+ ```text
476
+ v0.9 annotation schema changed false
477
+ frontend contract schema changed false
478
+ external-reference schema changed false
479
+ runtime intent semantics changed false
480
+ contract promotion semantics changed false
481
+ reference materialization semantics changed false
482
+ viewer security changed false
483
+ tutorial runtime duplicated false
484
+ Playwright tutorial engine added to Observer false
485
+ WebM recorder added to Observer false
486
+ subtitle generator added to Observer false
487
+ package version changed false
488
+ dependency added false
489
+ v0.9 marked released false
490
+ ```
491
+
492
+ `src/`, `viewer/src/`, `package.json`, `package-lock.json`, `scripts/` and
493
+ `.github/workflows/` are all unchanged.
494
+
495
+ One file outside `examples/` and `tests/` changed: `eslint.config.js` gained a
496
+ scoped block giving `examples/v09-demo/app/**/*.js` the two DOM globals it
497
+ uses. That file is lint configuration, not Observer production source or
498
+ product semantics, and the addition is scoped to the demo application
499
+ directory. ESLint 10 flat config has no per-file environment comment, so a
500
+ config block is the only available mechanism.
501
+
502
+ `examples/v09-demo` was deliberately not added to `package.json#files`. A
503
+ `npm pack --dry-run` confirms zero `examples/` entries in the candidate
504
+ tarball. Whether the demo ships inside `0.9.0` is a Prompt 3 decision, after
505
+ tutorial acceptance.
506
+
507
+ Documentation scope was respected. Only `examples/v09-demo/README.md` and this
508
+ report were written. `README.md`, `docs/QUICKSTART.md`, `docs/WORKFLOWS.md`,
509
+ `docs/CURRENT_STATE.md` and every other release-facing document are unchanged
510
+ and remain owned by Prompt 3.
511
+
512
+ ## 21. Changed files
513
+
514
+ Added:
515
+
516
+ ```text
517
+ examples/v09-demo/README.md
518
+ examples/v09-demo/app/index.html
519
+ examples/v09-demo/app/styles.css
520
+ examples/v09-demo/app/state.js
521
+ examples/v09-demo/app/assets/brand-mark.svg
522
+ examples/v09-demo/app/assets/asset-primary.svg
523
+ examples/v09-demo/app/assets/asset-alternate.svg
524
+ examples/v09-demo/references/v09-reference.png
525
+ examples/v09-demo/references/v09-reference.provenance.json
526
+ examples/v09-demo/scripts/prepare.mjs
527
+ examples/v09-demo/scripts/server.mjs
528
+ examples/v09-demo/scripts/generate-reference.mjs
529
+ tests/unit/v09DemoTemplate.test.ts
530
+ tests/unit/v09DemoMaterialization.test.ts
531
+ tests/unit/v09DemoServer.test.ts
532
+ tests/browser/v09Demo.test.ts
533
+ docs/reports/v0.9-demo-foundation.md
534
+ ```
535
+
536
+ Modified:
537
+
538
+ ```text
539
+ eslint.config.js
540
+ ```
541
+
542
+ Validation actually run, with exact results:
543
+
544
+ ```text
545
+ npm run typecheck PASS
546
+ npm run lint PASS
547
+ npm test PASS (87 files, 1425 tests)
548
+ npm run test:browser PASS (27 files, 252 tests)
549
+ npm run test:security PASS (16 files / 167 tests, then 3 files / 77 tests)
550
+ npm run build PASS
551
+ npm run check:docs PASS (17 required files)
552
+ npm pack --dry-run PASS (0.8.1, 0 examples/ entries; 362 files before this
553
+ report was written, 363 with it)
554
+ ```
555
+
556
+ No step was skipped. No test was weakened, skipped or deleted, and no
557
+ `.skip`, `.only`, `continue-on-error` or `|| true` was introduced.
558
+
559
+ Generated paths during this stage, all inside the repository-local approved
560
+ workflow root and all git-ignored:
561
+
562
+ ```text
563
+ .my-dev-kit-workflow/v0.9/demo-foundation/tests/materialization/ (created and removed by the materialization suite on each run)
564
+ ```
565
+
566
+ Two transient exploratory paths were used during implementation and have been
567
+ removed: `.my-dev-kit-workflow/v0.9/demo-foundation/scratch/` and
568
+ `.my-dev-kit-workflow/v0.9/demo-foundation/smoke-target/`. Nothing substantial
569
+ was generated on `C:\` outside the repository.
570
+
571
+ `git diff --check` reports no whitespace problems. No `.frontend-observer`
572
+ evidence, tutorial video, lab workspace, temporary target, server log,
573
+ `node_modules` or `dist` content is tracked by this change.
574
+
575
+ ## 22. Remaining next step
576
+
577
+ Prompt 2: Observer-specific `my-dev-kit-lab@0.4.7` tutorial integration.
578
+
579
+ Prompt 2 owns dynamic `TutorialTargetContractV1` generation, demo and bootstrap
580
+ process composition, Observer project initialization, baseline capture,
581
+ external reference import and approval of `v09-reference.png`, project-aware
582
+ Observer viewer startup, the minimum stable Viewer test ids, four
583
+ `TutorialScenarioV1` files, scenario validation against
584
+ `my-dev-kit-lab@0.4.7`, and Observer-specific runtime assertions.
585
+
586
+ Known risks and blockers carried forward: none from this stage. The one open
587
+ decision deliberately deferred is whether `examples/v09-demo` should be
588
+ included in `package.json#files` for `0.9.0`, which Prompt 3 decides after
589
+ tutorial acceptance.