@dailephd/my-frontend-observer 0.8.1 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/README.md +109 -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 +78 -1
  79. package/docs/COMMANDS.md +44 -4
  80. package/docs/CONTRACTS.md +78 -8
  81. package/docs/CURRENT_STATE.md +224 -35
  82. package/docs/DEVELOPMENT.md +38 -3
  83. package/docs/PROJECT_DESCRIPTION.md +4 -1
  84. package/docs/PROJECT_MILESTONES.md +32 -0
  85. package/docs/PROJECT_OVERVIEW.md +58 -17
  86. package/docs/QUICKSTART.md +52 -39
  87. package/docs/RELEASE.md +17 -11
  88. package/docs/ROADMAP.md +458 -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/plans/v0.9.1-implementation-plan.md +468 -0
  93. package/docs/reports/v0.9-architecture-retrieval.md +567 -0
  94. package/docs/reports/v0.9-batch1-visual-annotation-foundation.md +351 -0
  95. package/docs/reports/v0.9-batch2-viewer-annotation-authoring-boundary.md +438 -0
  96. package/docs/reports/v0.9-batch3-runtime-screenshot-annotation-authoring.md +412 -0
  97. package/docs/reports/v0.9-batch4-external-reference-annotation-authoring.md +452 -0
  98. package/docs/reports/v0.9-batch5-runtime-intent-contract-promotion.md +535 -0
  99. package/docs/reports/v0.9-batch6-reference-materialization.md +514 -0
  100. package/docs/reports/v0.9-batch7-integrated-acceptance.md +644 -0
  101. package/docs/reports/v0.9-demo-foundation.md +589 -0
  102. package/docs/reports/v0.9-final-pre-release-readiness.md +209 -0
  103. package/docs/reports/v0.9-final-readiness-corrections.md +530 -0
  104. package/docs/reports/v0.9-pre-release-readiness.md +170 -0
  105. package/docs/reports/v0.9-tutorial-end-to-end-acceptance.md +980 -0
  106. package/docs/reports/v0.9-tutorial-integration.md +731 -0
  107. package/docs/reports/v0.9.1-batch1-pwa-hard-gate-isolation.md +359 -0
  108. package/docs/reports/v0.9.1-batch2-hard-gate-validation-integration.md +262 -0
  109. package/docs/reports/v0.9.1-pre-release-readiness.md +206 -0
  110. package/package.json +3 -2
  111. package/dist/viewer/assets/index-D98S1_2d.js +0 -9
@@ -0,0 +1,530 @@
1
+ # v0.9 Final Readiness Corrections
2
+
3
+ ## 1. VERDICT
4
+
5
+ The failed final-readiness workflow run `35527166495` was decomposed into three
6
+ independent defects and corrected. A fourth and a fifth defect, both in the same
7
+ readiness verifier and both in the same family as the second, were found while
8
+ proving the correction locally and are also fixed.
9
+
10
+ No core Observer product regression was established by that run. Final readiness
11
+ exposed:
12
+
13
+ - an Observer tutorial pacing fixture defect,
14
+ - an Observer readiness verifier bug,
15
+ - and a generic my-dev-kit-lab native-select portability gap.
16
+
17
+ The product's own semantics, artifacts and commands were not changed.
18
+
19
+ ## 2. Repository identity
20
+
21
+ - Repository: `C:\Users\daile\Projects\my-frontend-observer`
22
+ - Branch: `validation/v0.9-final-readiness`
23
+ - Entry HEAD: `116bf5f45513768d4dfc552545cc02e13265f02f`
24
+ - Package: `@dailephd/my-frontend-observer`
25
+ - Package version: `0.8.1` (unchanged)
26
+ - v0.9: unreleased
27
+ - `master`: not pushed
28
+ - `docs/reports/v0.9-architecture-retrieval.md`: `S` skip-worktree preserved
29
+
30
+ ## 3. Failed workflow identity
31
+
32
+ - Run: `35527166495`
33
+ - URL: `https://github.com/dailephd/my-frontend-observer/actions/runs/35527166495`
34
+ - This run is recorded as history. It is not reused as passing evidence for the
35
+ final candidate.
36
+
37
+ ## 4. Failure decomposition
38
+
39
+ The earlier compressed summary attributed the whole failure to tutorial
40
+ portability. That was incomplete. The run contained three independent failures.
41
+
42
+ 1. Candidate/package job: failed in `npm test` because one scenario pause was
43
+ too short. `02-runtime-intent-contract.json/category-requested` declared
44
+ `pauseAfterMs` of `10900` where the frozen readability rule required
45
+ `11900`. Because the candidate job failed, the exact candidate tarball was
46
+ never built, so the package matrix was `NOT_REACHED`.
47
+ 2. Windows tutorial job: the readiness verifier crashed with
48
+ `Cannot read properties of undefined (reading 'contractClass')` because
49
+ contract-reader success results were extracted through the wrong property.
50
+ 3. Ubuntu tutorial job: the same readiness verifier crash.
51
+ 4. macOS tutorial job: scenario 2 failed while using native-select keyboard
52
+ emulation through click, `ArrowDown` and `Enter` sequences.
53
+
54
+ ## 5. my-dev-kit-lab v0.4.9 release identity
55
+
56
+ - Package: `@dailephd/my-dev-kit-lab`
57
+ - Version pinned exactly: `0.4.9`
58
+ - Release commit: `3aca0e2f5969b0a8b414bcd361dec20ba70b8dc6`
59
+ - `npx --yes @dailephd/my-dev-kit-lab@0.4.9 --version` reported `0.4.9`
60
+ - Scenario, target-contract, run-result and manifest schema versions all remain
61
+ `1.0.0`. No Observer scenario schema was migrated.
62
+ - Lab Playwright remains `1.60.0`, so the workflow's exact lab-browser install
63
+ commands were left unchanged. Observer's own `playwright` dependency remains
64
+ `^1.62.1` and was not changed to match the lab.
65
+ - Observer did not add `@dailephd/my-dev-kit-lab` to `dependencies` or
66
+ `devDependencies`. It remains an external tool invoked through `npx`.
67
+
68
+ ## 6. select-option contract
69
+
70
+ The released action used is:
71
+
72
+ ```json
73
+ {
74
+ "type": "select-option",
75
+ "locator": { "kind": "role", "role": "combobox", "name": "Intent category" },
76
+ "value": "requested"
77
+ }
78
+ ```
79
+
80
+ It maps to Playwright's `Locator.selectOption({ value })` and verifies that
81
+ exactly the requested value was selected. v0.4.9 supports `value` only. The
82
+ closed validator rejects `label`, `index`, `values` and multi-select arrays, so
83
+ none of those are used.
84
+
85
+ ## 7. Native-select scenario migration inventory
86
+
87
+ Option values were read from the owning viewer source, not guessed from labels:
88
+
89
+ - `viewer/src/annotation/runtimeIntent.ts` for the runtime intent vocabularies.
90
+ - `viewer/src/annotation/referenceIntent.ts` for the reference requirement
91
+ vocabularies.
92
+ - `viewer/src/components/RuntimeAnnotationPanel.tsx` for the relationship select,
93
+ whose option values are `pairwise:<index>`.
94
+ - `viewer/src/components/ReferenceAnnotationPanel.tsx` for the requirement
95
+ selects, whose relationship option values are the relationship index.
96
+ - `examples/v09-demo/references/v09-reference-regions.json` for region ids in
97
+ import order: `header, hero, sidebar, content, cta, asset, footer`.
98
+
99
+ Each migration below replaced a contiguous group of workaround steps with one
100
+ semantic selection step. `01-annotation-basics.json` contains no native select
101
+ interaction and was not modified.
102
+
103
+ ### 02-runtime-intent-contract
104
+
105
+ - Runtime intent operation: press "m", press Enter, press Enter, now `select-option` value `move` in step `operation-move`
106
+ - Move direction: click, press ArrowDown, press Enter, now `select-option` value `right` in step `direction-right`
107
+ - Intent category: click, press ArrowDown, press Enter, now `select-option` value `requested` in step `category-requested`
108
+ - Runtime intent operation: press ArrowDown x3, now `select-option` value `resize` in step `operation-resize`
109
+ - Resize direction: press ArrowDown, now `select-option` value `wider` in step `resize-wider`
110
+ - Intent category: press ArrowDown x2, now `select-option` value `expected-dependent` in step `category-expected`
111
+ - Intent expected-dependent mode: press ArrowDown, now `select-option` value `required` in step `mode-required`
112
+ - Runtime intent operation: press ArrowDown x5, now `select-option` value `preserve` in step `operation-preserve`
113
+ - Intent category: press ArrowDown x3, now `select-option` value `protected` in step `category-protected`
114
+ - Preserved property: press ArrowDown x2, now `select-option` value `y` in step `preserve-property`
115
+ - Contract tolerance: press ArrowDown x2, now `select-option` value `absolute-px` in step `tolerance`
116
+ - Canonical relationship to associate: press ArrowDown, now `select-option` value `pairwise:0` in step `relationship-choose`
117
+ - Runtime intent operation: press ArrowDown x5, now `select-option` value `preserve` in step `rel-operation`
118
+ - Intent category: press ArrowDown x4, now `select-option` value `preserved` in step `rel-category`
119
+ - Runtime intent operation: press ArrowDown x4, now `select-option` value `remove` in step `remove-op`
120
+ - Intent category: press ArrowDown, now `select-option` value `requested` in step `remove-category`
121
+ - Runtime intent operation: press ArrowDown, now `select-option` value `inspect` in step `inspect-op`
122
+
123
+ ### 03-reference-authoring
124
+
125
+ - Requirement category: press ArrowDown x3, now `select-option` value `protected` in step `property-category`
126
+ - Requirement subject: press ArrowDown, now `select-option` value `region-property` in step `property-subject`
127
+ - Requirement region: press ArrowDown x2, now `select-option` value `hero` in step `property-region`
128
+ - Region property: press ArrowDown x3, now `select-option` value `width` in step `property-prop`
129
+ - Tolerance: press ArrowDown x2, now `select-option` value `absolute-reference-px` in step `property-tolerance`
130
+ - Requirement subject: press ArrowDown, now `select-option` value `region-relationship` in step `relationship-subject`
131
+ - Requirement relationship: press ArrowDown, now `select-option` value `0` in step `relationship-pick`
132
+ - Requirement category: press ArrowDown, now `select-option` value `preserved` in step `measurement-category`
133
+ - Requirement subject: press ArrowDown, now `select-option` value `region-measurement` in step `measurement-subject`
134
+ - Subject region: press ArrowDown x2, now `select-option` value `hero` in step `measurement-subject-region`
135
+ - Related region: press ArrowDown x5, now `select-option` value `cta` in step `measurement-related`
136
+ - Measurement: press ArrowDown, now `select-option` value `vertical-gap` in step `measurement-kind`
137
+
138
+ ### 04-reference-materialization
139
+
140
+ - Requirement category: press ArrowDown x3, now `select-option` value `protected` in step `req-a-cat`
141
+ - Requirement subject: press ArrowDown, now `select-option` value `region-property` in step `req-a-subject`
142
+ - Requirement region: press ArrowDown x2, now `select-option` value `hero` in step `req-a-region`
143
+ - Region property: press ArrowDown x3, now `select-option` value `width` in step `req-a-prop`
144
+ - Tolerance: press ArrowDown x2, now `select-option` value `absolute-reference-px` in step `req-a-tol`
145
+ - Requirement category: press ArrowDown, now `select-option` value `preserved` in step `req-b-cat`
146
+ - Requirement region: press ArrowDown x5, now `select-option` value `footer` in step `req-b-region`
147
+ - Region property: press ArrowDown, now `select-option` value `height` in step `req-b-prop`
148
+
149
+ Counts:
150
+
151
+ - `SELECT_OPTION_MIGRATION_COUNT`: 37
152
+ - Collapsed workaround steps: 85 (83 `press`, 2 `click`)
153
+ - `NATIVE_SELECT_KEYBOARD_WORKAROUNDS_BEFORE`: 83
154
+ - `NATIVE_SELECT_KEYBOARD_WORKAROUNDS_REMAINING`: 0
155
+ - Scenario step totals: 258 before, 210 after
156
+
157
+ ## 8. Keyboard-workaround removal
158
+
159
+ Narration that described obsolete automation mechanics was removed, for example
160
+ "The first press lands on inspect", "stepped from the top of the list", and the
161
+ single-word stepping lines such as "Move.", "Resize.", "Remove.". Conceptual
162
+ explanation was preserved, including why a category or tolerance is chosen and
163
+ why `unexpected` cannot be authored.
164
+
165
+ `press` remains a legal tutorial action and is still permitted. Only `press`
166
+ acting as a surrogate for `<select>` choice was removed. After migration the
167
+ four scenarios contain no `press` action at all, because every remaining one had
168
+ been a combobox surrogate.
169
+
170
+ Two scenario steps that opened a select with `click` before stepping it were
171
+ part of the same workaround and were collapsed into the semantic selection.
172
+
173
+ No OS-conditional scenario steps were introduced. One canonical scenario set
174
+ remains.
175
+
176
+ Presentation state was preserved rather than dropped while collapsing:
177
+
178
+ - the `direction-right` callout was retained,
179
+ - the `move-form-ready` screenshot, which was attached to the last step of a
180
+ collapsed group, was carried onto the single replacement step.
181
+
182
+ ## 9. Pacing correction
183
+
184
+ The frozen rule is unchanged:
185
+
186
+ ```
187
+ readingMs = max(1200, ceil((narration.length / 15) * 10) * 100)
188
+ ```
189
+
190
+ The rule was not weakened, no test was deleted, and no CI special case was
191
+ added. After all narration edits, every step in all four scenarios was
192
+ re-paced with `pauseAfterMs = max(existing, readingMs)`, which raises any step
193
+ that no longer covers its narration while preserving one deliberately longer
194
+ dwell in `01-annotation-basics.json`.
195
+
196
+ - `CATEGORY_REQUESTED_PACING_FIXED`: true (`10900` to `11900`)
197
+ - `ALL_STEPS_MEET_READING_TIME_RULE`: true, 210 of 210 steps
198
+ - Only the pacing of edited steps changed; `01-annotation-basics.json` is
199
+ byte-identical to its committed version.
200
+
201
+ ## 10. Readiness contract-reader root cause
202
+
203
+ `scripts/run-v09-tutorial-readiness.mjs` used one helper for every canonical
204
+ reader:
205
+
206
+ ```js
207
+ async function readArtifact(reader, file) {
208
+ const result = await reader(file);
209
+ if (!result?.ok) fail(...);
210
+ return result.artifact;
211
+ }
212
+ ```
213
+
214
+ That is correct for `readVisualAnnotationArtifact` and
215
+ `readExternalReferenceArtifact`, whose success result is `{ ok: true, artifact }`.
216
+
217
+ It is wrong for the canonical frontend-contract readers. Their success results
218
+ are:
219
+
220
+ - `readPersistentBaselineContract` returns `{ ok: true, contract }`
221
+ - `readPerChangeContract` returns `{ ok: true, contract }`
222
+
223
+ Neither returns `artifact`. The script therefore pushed `undefined` for every
224
+ valid contract, and the later `item.contractClass` evaluation threw
225
+ `Cannot read properties of undefined (reading 'contractClass')`. This is why the
226
+ Windows and Ubuntu jobs failed after otherwise running the tutorials.
227
+
228
+ Reproduced against the three real contract manifests written by a local
229
+ scenario 2 run:
230
+
231
+ All three returned `undefined` from the old helper, and evaluating
232
+ `contractClass` on that value threw
233
+ `Cannot read properties of undefined (reading 'contractClass')`. The fixed
234
+ reader returned a defined contract for each:
235
+
236
+ 1. the per-change contract promoted by the tutorial: `change`
237
+ 2. `tutorial-baseline/v09-tutorial-baseline`: `baseline`
238
+ 3. `tutorial-initial-change/v09-tutorial-initial-change`: `change`
239
+
240
+ - `CONTRACTCLASS_CRASH_REPRODUCIBLE_BEFORE_FIX`: true
241
+ - `CONTRACTCLASS_CRASH_CLOSED`: true
242
+
243
+ ## 11. Contract-reader correction
244
+
245
+ A dedicated canonical contract reader was added. No optional-chaining
246
+ workaround was used, no raw JSON was parsed to choose a reader, and no third
247
+ contract validator was introduced:
248
+
249
+ ```js
250
+ async function readContract(file) {
251
+ const baseline = await readPersistentBaselineContract(file);
252
+ if (baseline.ok) return baseline.contract;
253
+ const change = await readPerChangeContract(file);
254
+ if (change.ok) return change.contract;
255
+ fail(`canonical contract readers rejected ${file}: baseline=${baseline.reason}; change=${change.reason}`);
256
+ }
257
+ ```
258
+
259
+ Trying each canonical validator is what decides the contract class, so the raw
260
+ `contractClass` dispatch was removed. `jsonFile()` became unused and was
261
+ deleted, along with the now-unused `readFile` import. A later-unused `sha256()`
262
+ helper was also removed. `BASELINE_READER_RESULT_PROPERTY` and
263
+ `CHANGE_READER_RESULT_PROPERTY` are both `contract`.
264
+
265
+ ## 12. Two further readiness verifier defects found while proving the fix
266
+
267
+ Once the `contractClass` crash was closed, the local run reached scenario 3 and
268
+ scenario 4 evidence checks for the first time. Both were broken in the same
269
+ family as the contract-reader defect: the script read canonical artifacts
270
+ through a shape that does not exist.
271
+
272
+ ### 12.1 Reference lifecycle compared as a string
273
+
274
+ The script used `item.lifecycle === 'approved'` and
275
+ `item.lifecycle === 'imported'`. The canonical contract in
276
+ `src/domain/externalReference.ts` is:
277
+
278
+ ```ts
279
+ export interface ImportedExternalReferenceArtifact extends ExternalReferenceArtifactBase {
280
+ lifecycle: { state: 'imported' };
281
+ image: ExternalReferenceImageReference;
282
+ }
283
+ export interface ApprovedExternalReferenceArtifact extends ExternalReferenceArtifactBase {
284
+ lifecycle: { state: 'approved'; approvedAt: string };
285
+ sourceReference: ExternalReferenceSourceReference;
286
+ }
287
+ ```
288
+
289
+ `lifecycle` is an object, so both comparisons were always false and scenario 3
290
+ could never pass. Verified against real evidence: both references reported
291
+ `oldStringCompareWorks: false`.
292
+
293
+ This was never reached in run `35527166495` because Windows and Ubuntu crashed
294
+ in scenario 2 and macOS failed in scenario 2.
295
+
296
+ Corrected to use the canonical exported type guards
297
+ `isApprovedExternalReferenceArtifact` and `isImportedExternalReferenceArtifact`
298
+ rather than a locally re-derived check.
299
+
300
+ ### 12.2 The materialized image digest comparison never executed
301
+
302
+ The scenario 4 check was:
303
+
304
+ ```js
305
+ const imagePath = (reference) => reference.image?.path ? path.join(...) : undefined;
306
+ const sourceImage = imagePath(approved);
307
+ const revisionImage = imagePath(imported[0]);
308
+ if (sourceImage && revisionImage && sha256(...) !== sha256(...)) fail(...);
309
+ ```
310
+
311
+ An approved artifact has no top-level `image`; it carries
312
+ `sourceReference.image`. `sourceImage` was therefore always `undefined`, and the
313
+ `sourceImage && revisionImage` guard made the comparison a silent no-op. The
314
+ check reported success while proving nothing, which is exactly the
315
+ absent-evidence-as-PASS failure the evidence-honesty rules forbid. Verified
316
+ against real evidence: the approved artifact reported `hasTopLevelImage: false`.
317
+
318
+ Corrected to compare the canonical identity-bearing digests directly, with no
319
+ file re-hashing and no filesystem path construction:
320
+
321
+ - source: `approved.sourceReference.image.sha256`
322
+ - materialized: the successor's `image.sha256`
323
+
324
+ A missing digest on either side now fails explicitly instead of being skipped.
325
+
326
+ ### 12.3 Imported-reference count assumption
327
+
328
+ The scenario 4 check also required exactly one imported reference. Real
329
+ evidence shows a materialization target holds three references: the original
330
+ source import, the approval, and the materialized successor. So
331
+ `imported.length !== 1` would have failed even after the lifecycle fix.
332
+
333
+ The successor is now identified by the property that actually distinguishes it,
334
+ namely that it carries requirements, while the reference the approval came from
335
+ carries none. Observed: `importedCount: 2`, `materializedCount: 1`,
336
+ `digestsMatch: true`.
337
+
338
+ ### 12.4 Diagnosability
339
+
340
+ The scenario 3 and scenario 4 checks previously collapsed several distinct
341
+ conditions into one generic message such as
342
+ `reference authoring invariants failed`. Each condition now fails with its own
343
+ message naming the actual cause and the observed counts.
344
+
345
+ ## 13. Local four-tutorial proof
346
+
347
+ `npm run build` was run first, because `scripts/run-v09-tutorial-readiness.mjs`
348
+ imports `../dist/index.js`.
349
+
350
+ Before running the tutorials, all four scenarios were validated against the
351
+ released lab:
352
+
353
+ ```
354
+ npx --yes @dailephd/my-dev-kit-lab@0.4.9 tutorial validate --scenario <file> --target-contract <generated> --json
355
+ ```
356
+
357
+ - `01-annotation-basics`: `status: valid`, `errors: []`
358
+ - `02-runtime-intent-contract`: `status: valid`, `errors: []`
359
+ - `03-reference-authoring`: `status: valid`, `errors: []`
360
+ - `04-reference-materialization`: `status: valid`, `errors: []`
361
+
362
+ This also proves the installed v0.4.9 validator accepts the exact released
363
+ `select-option` shape, because every migrated step uses it.
364
+
365
+ Static workaround audit across all four scenario files:
366
+
367
+ - `COMBOBOX_PRESS_ACTION_COUNT`: 0
368
+ - `select-option` actions: 37
369
+ - Steps satisfying the reading-time rule: 210 of 210
370
+
371
+ Full run, into a clean scratch output outside tracked repository files:
372
+
373
+ ```
374
+ node scripts/run-v09-tutorial-readiness.mjs .my-dev-kit-workflow/adhoc/v09-final-readiness/tutorials-run
375
+ ```
376
+
377
+ Result: exit 0, with every required condition met.
378
+
379
+ - `observer-v09-annotation-basics`: `passed`, `cleanupErrors: []`
380
+ - `observer-v09-runtime-contract`: `passed`, `cleanupErrors: []`
381
+ - `observer-v09-reference-authoring`: `passed`, `cleanupErrors: []`
382
+ - `observer-v09-reference-materialization`: `passed`, `cleanupErrors: []`
383
+ - `demoSourceImmutable`: true
384
+ - `repositoryUnchanged`: true
385
+ - Tracked demo digest identical before and after: 19 files,
386
+ `3a65f1c822faaddd958d4f4f20ddedcbf415bd4d34de67bf75a34ddc35c32bef`
387
+
388
+ One earlier local attempt failed on the final
389
+ `repository status changed during tutorial readiness` guard. That was caused by
390
+ this report being written while the run was in progress, not by the product or
391
+ the scenarios. The guard behaved correctly. The run above was performed with a
392
+ quiescent worktree.
393
+
394
+ ## 14. Canonical Observer evidence proof
395
+
396
+ Every scenario's evidence was read back through the canonical readers.
397
+
398
+ Scenario 2, the contract verifier gate, reached and passed canonical contract
399
+ verification with no `contractClass` crash:
400
+
401
+ - 3 contract manifests read through the canonical readers
402
+ - promoted per-change contract found with exactly 2 clauses
403
+ - no `inspect` clause present
404
+ - no `remove` clause present
405
+ - baseline contract `v09-tutorial-baseline` preserved
406
+
407
+ Scenario 4, the reference verifier gate:
408
+
409
+ - approved source reference exists, carrying 0 requirements
410
+ - 2 imported references exist: the original source import and the successor
411
+ - exactly 1 materialized successor, carrying 1 requirement
412
+ - image digest unchanged and now actually compared:
413
+ `4bdb94db1096532dd886b7b8bd9ea4018f44ff0e3debb24e92cbf6132123538d`
414
+ on both the approved source reference and the materialized successor
415
+
416
+ Scenario 1: 2 annotations with a valid supersession chain and all five mark
417
+ kinds. Scenario 3: one external-reference annotation with exactly 7 items and an
418
+ approved reference carrying 0 requirements.
419
+
420
+ ## 15. Full local regression
421
+
422
+ - `npm run typecheck`: PASS
423
+ - `npm run lint`: PASS
424
+ - `npm test`: PASS, 1453 tests in 89 files
425
+ - `npm run test:browser`: PASS, 275 tests in 29 files
426
+ - `npm run test:security`: PASS, 167 tests in 16 files plus 77 tests in 3 files
427
+ - `npm run build`: PASS
428
+ - `npm run check:docs`: PASS, 17 required files
429
+ - `npm pack --dry-run`: PASS, 366 files
430
+
431
+ `tests/unit/v09TutorialScenarios.test.ts` passes, including pacing. The
432
+ candidate-job failure is closed.
433
+
434
+ ## 16. Packed smoke results
435
+
436
+ One local candidate tarball was built into the approved workflow root and used
437
+ for all three smokes. Process-local `TEMP`/`TMP` were redirected into that root
438
+ so no consumer install or browser profile was created elsewhere. No user or
439
+ machine environment variable was changed.
440
+
441
+ - Tarball: `dailephd-my-frontend-observer-0.8.1.tgz`
442
+ - SHA-256: `8f8bcd5aab0607df35c9a56fcfa9ece9f286c115910c6e4b50b2384adf4469ad`
443
+ - File count: 366
444
+
445
+ Results:
446
+
447
+ - Packed observation smoke: PASS, `"pass": true`, real Chromium
448
+ `153.0.8010.12`, `targetsFilePathLeaked: false`
449
+ - Packed viewer smoke: PASS
450
+ - Packed v0.9 annotation smoke: PASS
451
+
452
+ This local tarball is local proof only. It is not the final candidate. The
453
+ authoritative exact candidate is the one the workflow builds.
454
+
455
+ ## 17. Changed files
456
+
457
+ - `examples/v09-demo/tutorials/02-runtime-intent-contract.json`
458
+ - `examples/v09-demo/tutorials/03-reference-authoring.json`
459
+ - `examples/v09-demo/tutorials/04-reference-materialization.json`
460
+ - `examples/v09-demo/README.md`
461
+ - `examples/v09-demo/scripts/generate-tutorial-target.mjs`
462
+ - `scripts/run-v09-tutorial-readiness.mjs`
463
+ - `tests/unit/v09TutorialScenarios.test.ts`
464
+ - `tests/unit/v09TutorialTargetContract.test.ts`
465
+ - `docs/WORKFLOWS.md`
466
+ - `docs/CURRENT_STATE.md`
467
+ - `docs/PROJECT_OVERVIEW.md`
468
+ - `README.md`
469
+ - `docs/reports/v0.9-final-readiness-corrections.md` (this report)
470
+
471
+ `examples/v09-demo/tutorials/01-annotation-basics.json` was deliberately not
472
+ modified.
473
+
474
+ `.github/workflows/pre-release-readiness.yml` was inspected and needed no
475
+ change. It never pinned the lab version; it delegates to
476
+ `scripts/run-v09-tutorial-readiness.mjs`, where the pin was updated. Its exact
477
+ lab Chromium installation already uses `playwright@1.60.0`, which v0.4.9 still
478
+ declares.
479
+
480
+ ## 18. Test changes
481
+
482
+ - `select-option` was added to the released lab action allowlist. All previously
483
+ released actions were kept.
484
+ - A native-select portability regression test was added. It fails if any
485
+ scenario uses `action.type = "press"` against a locator with `kind = "role"`
486
+ and `role = "combobox"`, which freezes the rule that a native `<select>`
487
+ semantic choice must use `select-option`. It does not ban `press` generally.
488
+ - The same test asserts the v0.4.9 value-only contract, so a scenario cannot
489
+ acquire `label`, `index` or `values`.
490
+ - The guard was checked against a synthetic pre-migration step to confirm it is
491
+ not vacuous: it fires on a combobox `press`, and does not fire on a
492
+ `select-option` or on a legitimate non-combobox `press`.
493
+
494
+ ## 19. Scope audit
495
+
496
+ - `MY_DEV_KIT_LAB_VERSION`: `0.4.9`, pinned exactly
497
+ - `NATIVE_SELECT_KEYBOARD_WORKAROUNDS_REMAINING`: 0
498
+ - `select-option` used semantically, one canonical scenario set
499
+ - Pacing test passes and was not weakened
500
+ - Contract verifier uses the canonical contract readers correctly
501
+ - No `.artifact` extraction from contract-reader success results
502
+ - No product `<select>` redesign, no custom dropdown, no tutorial-only control
503
+ - No new Observer dependency; `@dailephd/my-dev-kit-lab` is not a dependency
504
+ - Demo still excluded from the npm package: 0 of 365 packaged files match
505
+ `v09-demo`
506
+ - Package version remains `0.8.1`; v0.9 remains unreleased; `master` not pushed
507
+ - No Observer product semantics, artifact schema or command surface changed
508
+
509
+ ## 20. Remote readiness continuation
510
+
511
+ Run `35527166495` cannot contribute passing evidence to the final candidate.
512
+ The scenario files changed, the lab version changed, the readiness verifier
513
+ changed and the candidate bytes changed, so rerunning only the failed jobs
514
+ against the stale commit would prove nothing.
515
+
516
+ A new complete workflow execution is required on the correction commit,
517
+ covering:
518
+
519
+ 1. the candidate/static job, which must now get past `npm test` and build the
520
+ exact candidate tarball
521
+ 2. Windows, Linux and macOS exact-package smokes, which run 35527166495 never
522
+ reached
523
+ 3. Windows, Linux and macOS Observer tutorial readiness
524
+
525
+ `RUN_A` is the first complete pass of the full workflow on one exact
526
+ post-correction commit. Only after `RUN_A` exists may
527
+ `docs/reports/v0.9-final-pre-release-readiness.md` be written. Because
528
+ `docs/reports/` is inside the npm package, committing that report changes the
529
+ candidate bytes, so a second full workflow run `RUN_B` is required on the exact
530
+ report-containing commit.