@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.
- package/CHANGELOG.md +57 -0
- package/README.md +107 -7
- package/dist/application/projectWorkflowService.d.ts +18 -1
- package/dist/application/projectWorkflowService.js +40 -2
- package/dist/application/projectWorkflowService.js.map +1 -1
- package/dist/application/visualAnnotationContractPromotionService.d.ts +41 -0
- package/dist/application/visualAnnotationContractPromotionService.js +143 -0
- package/dist/application/visualAnnotationContractPromotionService.js.map +1 -0
- package/dist/application/visualAnnotationPersistenceService.d.ts +34 -0
- package/dist/application/visualAnnotationPersistenceService.js +68 -0
- package/dist/application/visualAnnotationPersistenceService.js.map +1 -0
- package/dist/application/visualAnnotationReferenceMaterializationService.d.ts +53 -0
- package/dist/application/visualAnnotationReferenceMaterializationService.js +194 -0
- package/dist/application/visualAnnotationReferenceMaterializationService.js.map +1 -0
- package/dist/artifacts/visualAnnotationArtifactReader.d.ts +19 -0
- package/dist/artifacts/visualAnnotationArtifactReader.js +66 -0
- package/dist/artifacts/visualAnnotationArtifactReader.js.map +1 -0
- package/dist/artifacts/visualAnnotationArtifactWriter.d.ts +42 -0
- package/dist/artifacts/visualAnnotationArtifactWriter.js +85 -0
- package/dist/artifacts/visualAnnotationArtifactWriter.js.map +1 -0
- package/dist/cli.js +464 -454
- package/dist/cli.js.map +1 -1
- package/dist/domain/visualAnnotation.d.ts +217 -0
- package/dist/domain/visualAnnotation.js +584 -0
- package/dist/domain/visualAnnotation.js.map +1 -0
- package/dist/domain/visualAnnotationIdentity.d.ts +17 -0
- package/dist/domain/visualAnnotationIdentity.js +47 -0
- package/dist/domain/visualAnnotationIdentity.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/projectWorkflow/projectPaths.d.ts +9 -0
- package/dist/projectWorkflow/projectPaths.js +21 -0
- package/dist/projectWorkflow/projectPaths.js.map +1 -1
- package/dist/viewer/assets/{index-CN_yb9Uf.css → index-BN41MI7m.css} +1 -1
- package/dist/viewer/assets/index-CkKXnlrI.js +9 -0
- package/dist/viewer/index.html +2 -2
- package/dist/viewer/sw.js +1 -1
- package/dist/viewerServer/annotationAuthoring.d.ts +64 -0
- package/dist/viewerServer/annotationAuthoring.js +230 -0
- package/dist/viewerServer/annotationAuthoring.js.map +1 -0
- package/dist/viewerServer/annotationContractPromotion.d.ts +51 -0
- package/dist/viewerServer/annotationContractPromotion.js +105 -0
- package/dist/viewerServer/annotationContractPromotion.js.map +1 -0
- package/dist/viewerServer/annotationReferenceMaterialization.d.ts +40 -0
- package/dist/viewerServer/annotationReferenceMaterialization.js +91 -0
- package/dist/viewerServer/annotationReferenceMaterialization.js.map +1 -0
- package/dist/viewerServer/authoringSecurity.d.ts +59 -0
- package/dist/viewerServer/authoringSecurity.js +112 -0
- package/dist/viewerServer/authoringSecurity.js.map +1 -0
- package/dist/viewerServer/evidence/annotationView.d.ts +28 -0
- package/dist/viewerServer/evidence/annotationView.js +43 -0
- package/dist/viewerServer/evidence/annotationView.js.map +1 -0
- package/dist/viewerServer/evidence/classify.d.ts +3 -1
- package/dist/viewerServer/evidence/classify.js +12 -0
- package/dist/viewerServer/evidence/classify.js.map +1 -1
- package/dist/viewerServer/evidence/discovery.d.ts +2 -0
- package/dist/viewerServer/evidence/discovery.js +6 -0
- package/dist/viewerServer/evidence/discovery.js.map +1 -1
- package/dist/viewerServer/evidence/handles.js +1 -0
- package/dist/viewerServer/evidence/handles.js.map +1 -1
- package/dist/viewerServer/evidence/index.d.ts +29 -0
- package/dist/viewerServer/evidence/index.js +43 -1
- package/dist/viewerServer/evidence/index.js.map +1 -1
- package/dist/viewerServer/evidence/mediaResolver.d.ts +1 -1
- package/dist/viewerServer/evidence/mediaResolver.js +28 -2
- package/dist/viewerServer/evidence/mediaResolver.js.map +1 -1
- package/dist/viewerServer/evidence/projection.d.ts +5 -1
- package/dist/viewerServer/evidence/projection.js +19 -0
- package/dist/viewerServer/evidence/projection.js.map +1 -1
- package/dist/viewerServer/httpServer.d.ts +13 -2
- package/dist/viewerServer/httpServer.js +278 -4
- package/dist/viewerServer/httpServer.js.map +1 -1
- package/dist/viewerServer/viewerService.d.ts +8 -0
- package/dist/viewerServer/viewerService.js +38 -2
- package/dist/viewerServer/viewerService.js.map +1 -1
- package/docs/ARCHITECTURE.md +108 -21
- package/docs/CI_CD.md +57 -1
- package/docs/COMMANDS.md +44 -4
- package/docs/CONTRACTS.md +78 -8
- package/docs/CURRENT_STATE.md +157 -35
- package/docs/DEVELOPMENT.md +8 -3
- package/docs/PROJECT_DESCRIPTION.md +4 -1
- package/docs/PROJECT_MILESTONES.md +4 -0
- package/docs/PROJECT_OVERVIEW.md +41 -17
- package/docs/QUICKSTART.md +52 -39
- package/docs/RELEASE.md +16 -11
- package/docs/ROADMAP.md +406 -66
- package/docs/SECURITY.md +71 -14
- package/docs/WORKFLOWS.md +151 -23
- package/docs/plans/v0.9-implementation-plan.md +1529 -0
- package/docs/reports/v0.9-architecture-retrieval.md +567 -0
- package/docs/reports/v0.9-batch1-visual-annotation-foundation.md +351 -0
- package/docs/reports/v0.9-batch2-viewer-annotation-authoring-boundary.md +438 -0
- package/docs/reports/v0.9-batch3-runtime-screenshot-annotation-authoring.md +412 -0
- package/docs/reports/v0.9-batch4-external-reference-annotation-authoring.md +452 -0
- package/docs/reports/v0.9-batch5-runtime-intent-contract-promotion.md +535 -0
- package/docs/reports/v0.9-batch6-reference-materialization.md +514 -0
- package/docs/reports/v0.9-batch7-integrated-acceptance.md +644 -0
- package/docs/reports/v0.9-demo-foundation.md +589 -0
- package/docs/reports/v0.9-final-pre-release-readiness.md +209 -0
- package/docs/reports/v0.9-final-readiness-corrections.md +530 -0
- package/docs/reports/v0.9-pre-release-readiness.md +170 -0
- package/docs/reports/v0.9-tutorial-end-to-end-acceptance.md +980 -0
- package/docs/reports/v0.9-tutorial-integration.md +731 -0
- package/package.json +2 -2
- package/dist/viewer/assets/index-D98S1_2d.js +0 -9
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
# v0.9 Batch 3 Report: Runtime Screenshot Annotation Authoring
|
|
2
|
+
|
|
3
|
+
## 1. VERDICT
|
|
4
|
+
|
|
5
|
+
PASS_V0_9_BATCH3_RUNTIME_SCREENSHOT_ANNOTATION_AUTHORING
|
|
6
|
+
|
|
7
|
+
## 2. Repository identity
|
|
8
|
+
|
|
9
|
+
1. Repository: `C:\Users\daile\Projects\my-frontend-observer`
|
|
10
|
+
2. Branch: `master`
|
|
11
|
+
3. Starting HEAD: `3776c356d1c7c8848c633d0a3c0fab46501b9a04`
|
|
12
|
+
4. Ending HEAD before the final commit: `3776c356d1c7c8848c633d0a3c0fab46501b9a04`
|
|
13
|
+
5. Package version: `0.8.1` (unchanged in `package.json` and `package-lock.json`)
|
|
14
|
+
|
|
15
|
+
## 3. Prompt 2 entry gate
|
|
16
|
+
|
|
17
|
+
1. Prompt 2 verdict: `PASS_V0_9_BATCH2_VIEWER_ANNOTATION_AUTHORING_BOUNDARY`
|
|
18
|
+
2. Prompt 2 commit: `3776c356d1c7c8848c633d0a3c0fab46501b9a04`
|
|
19
|
+
3. Viewer protocol: `1.1.0`
|
|
20
|
+
4. Annotation save route: `POST /api/annotations`
|
|
21
|
+
5. Project-aware authoring: enabled
|
|
22
|
+
6. Standalone viewer: read-only
|
|
23
|
+
7. Preflight found a clean tracked worktree on `master` at the required HEAD.
|
|
24
|
+
8. `git ls-files -v docs/reports/v0.9-architecture-retrieval.md` printed `S`
|
|
25
|
+
at entry and again before commit. The `skip-worktree` state was preserved.
|
|
26
|
+
The file was not restored, overwritten, or committed. No pull, rebase,
|
|
27
|
+
reset, or clean was run.
|
|
28
|
+
9. No Prompt 2 API defect was found. The UI uses the Prompt 2 API unchanged.
|
|
29
|
+
|
|
30
|
+
## 4. Authoring-session consumption
|
|
31
|
+
|
|
32
|
+
1. `viewer/src/hooks/useAuthoringSession.ts` fetches
|
|
33
|
+
`GET /api/authoring/session` with `cache: 'no-store'` when the observation
|
|
34
|
+
workspace mounts.
|
|
35
|
+
2. States: `loading`, `read-only`, `available` (with the token), and `error`.
|
|
36
|
+
3. The token lives only in React state. It is never written to localStorage,
|
|
37
|
+
sessionStorage, IndexedDB, a URL, a log, a service-worker message, or the
|
|
38
|
+
visible UI. The only place it leaves memory is the save request header.
|
|
39
|
+
A source search of the new viewer files found no storage APIs and no
|
|
40
|
+
`console` calls.
|
|
41
|
+
4. A read-only viewer is a normal state:
|
|
42
|
+
1. The toolbar shows only Select and Pan plus a "Read-only viewer" notice.
|
|
43
|
+
2. The panel can still list and display saved annotations for inspection.
|
|
44
|
+
3. No drawing, editing, or save controls are shown.
|
|
45
|
+
|
|
46
|
+
## 5. Runtime annotation discovery
|
|
47
|
+
|
|
48
|
+
`viewer/src/hooks/useRuntimeAnnotations.ts` uses only existing APIs.
|
|
49
|
+
|
|
50
|
+
1. `GET /api/index` gives the candidates: `family === 'visual-annotation'`,
|
|
51
|
+
`supportState === 'supported'`, and
|
|
52
|
+
`relatedIds.sourceObservationId === observationId`. At most 100 candidates
|
|
53
|
+
are checked, and truncation is reported.
|
|
54
|
+
2. `GET /api/annotations/:handle/view` confirms each candidate. The pure
|
|
55
|
+
helper `annotationViewBelongsToObservation` requires `source.status`
|
|
56
|
+
`available`, `source.family` `observation`, and `source.handle` equal to
|
|
57
|
+
the selected observation handle.
|
|
58
|
+
3. Aliases, relative directories, and labels are never used to join an
|
|
59
|
+
annotation to an observation.
|
|
60
|
+
|
|
61
|
+
The panel's saved list shows, for each annotation:
|
|
62
|
+
|
|
63
|
+
1. `annotationId`
|
|
64
|
+
2. item count
|
|
65
|
+
3. confirmed item count
|
|
66
|
+
4. whether it is a revision
|
|
67
|
+
|
|
68
|
+
No paths are shown. Selecting a saved annotation copies its items into the
|
|
69
|
+
in-memory draft. It sets that annotation as the parent handle and keeps every
|
|
70
|
+
`annotationItemId`. The persisted artifact is never mutated.
|
|
71
|
+
|
|
72
|
+
## 6. Shared SVG source-coordinate transform
|
|
73
|
+
|
|
74
|
+
1. New `viewer/src/svg/sourceCoordinates.ts` provides
|
|
75
|
+
`screenPointToSvgSource(svg, clientX, clientY)`. It uses
|
|
76
|
+
`getScreenCTM()`, `createSVGPoint()`, `ctm.inverse()`, and
|
|
77
|
+
`matrixTransform()`.
|
|
78
|
+
2. `viewer/src/hooks/useZoomPan.ts` now calls this helper. Its private
|
|
79
|
+
duplicate was removed.
|
|
80
|
+
3. Annotation drawing and moving (`useAnnotationPointerInteraction.ts`) call
|
|
81
|
+
the same helper.
|
|
82
|
+
4. A search of `viewer/src` finds exactly one `getScreenCTM()` or
|
|
83
|
+
`matrixTransform` implementation. No bounding-rectangle ratios,
|
|
84
|
+
devicePixelRatio, CSS ratios, or manual aspect-ratio math are used.
|
|
85
|
+
5. The source frame is `artifact.requestConfig.viewport` (runtime CSS pixels).
|
|
86
|
+
Coordinates are never rounded, clamped, or scaled. A pointer position
|
|
87
|
+
outside `0..width` by `0..height` is ignored. A gesture keeps its last
|
|
88
|
+
valid in-frame position.
|
|
89
|
+
|
|
90
|
+
## 7. Interaction modes
|
|
91
|
+
|
|
92
|
+
1. `AnnotationInteractionMode` is the closed set `select`, `pan`, `point`,
|
|
93
|
+
`rectangle`, `line`, `arrow`, `note`. It is defined in the new pure module
|
|
94
|
+
`viewer/src/annotation/annotationGeometry.ts`.
|
|
95
|
+
2. `useAnnotationPointerInteraction` attaches exactly one handler set to the
|
|
96
|
+
SVG at a time:
|
|
97
|
+
1. `pan`: the existing `useZoomPan` pointer handlers only. Target and mark
|
|
98
|
+
pointer input is disabled.
|
|
99
|
+
2. Drawing modes: drawing handlers only. Target rectangles get
|
|
100
|
+
`pointer-events: none`, and marks are not selectable, so drawing over a
|
|
101
|
+
target never selects it and never moves marks.
|
|
102
|
+
3. `select`: target selection, mark selection, and mark drag-to-move. The
|
|
103
|
+
background neither pans nor draws.
|
|
104
|
+
3. A drag therefore can never mean both pan and draw.
|
|
105
|
+
|
|
106
|
+
## 8. Annotation layer
|
|
107
|
+
|
|
108
|
+
1. New `viewer/src/components/AnnotationLayer.tsx` renders one `<g>` inside the
|
|
109
|
+
existing `TargetOverlaySvg`. It comes after the screenshot, relationships,
|
|
110
|
+
and targets, so annotations render on top. There is no second SVG, no
|
|
111
|
+
canvas, and no positioned DOM layer.
|
|
112
|
+
2. It receives only items, the selected item id, a selectable flag, callbacks,
|
|
113
|
+
and an optional preview mark. It knows nothing about artifacts, paths,
|
|
114
|
+
tokens, the API, or aliases, so Prompt 4 can reuse it.
|
|
115
|
+
3. It renders the five mark kinds with fixed classes. Note text is plain React
|
|
116
|
+
text: no `dangerouslySetInnerHTML` and no authored SVG.
|
|
117
|
+
4. Each mark has `data-annotation-item-id`, `data-annotation-kind`, and a
|
|
118
|
+
bounded `aria-label`.
|
|
119
|
+
5. `TargetOverlaySvg` gains three optional props: `annotationLayer`,
|
|
120
|
+
`targetsInteractive` (default `true`), and `interactionClassName`.
|
|
121
|
+
Screenshot, targets, labels, relationship connectors, target selection, and
|
|
122
|
+
viewBox behavior are otherwise unchanged. Comparison and reference
|
|
123
|
+
workspaces pass none of the new props.
|
|
124
|
+
|
|
125
|
+
## 9. Draft lifecycle
|
|
126
|
+
|
|
127
|
+
`viewer/src/hooks/useRuntimeAnnotationDraft.ts` owns the draft.
|
|
128
|
+
|
|
129
|
+
1. New annotation: no items, no parent, nothing selected, not dirty.
|
|
130
|
+
2. Drawing:
|
|
131
|
+
1. Point and note are created on pointer release at the pointer-down
|
|
132
|
+
source point.
|
|
133
|
+
2. Rectangle, line, and arrow show a dashed preview while dragging and are
|
|
134
|
+
created on release.
|
|
135
|
+
3. Rectangles are direction-normalized. A zero-width or zero-height
|
|
136
|
+
rectangle is not created.
|
|
137
|
+
4. Local choice: a zero-length line or arrow is not created either.
|
|
138
|
+
5. A note is created only when the toolbar note text is 1 to 2000
|
|
139
|
+
characters.
|
|
140
|
+
3. New item: `annotation-item-${crypto.randomUUID()}`, no association,
|
|
141
|
+
`interpretation: { state: 'uninterpreted' }`. It is selected on creation
|
|
142
|
+
and marks the draft dirty.
|
|
143
|
+
4. Move (select mode): drag a mark past a 3-pixel threshold. It translates
|
|
144
|
+
rigidly by the source delta through the shared transform. A translation
|
|
145
|
+
that would leave the frame is not applied. The id, association, and
|
|
146
|
+
interpretation are unchanged. There are no resize handles.
|
|
147
|
+
5. Note edit: a controlled textarea for the selected note changes only
|
|
148
|
+
`mark.text`, bounded at 2000 characters. Saving is disabled while any note
|
|
149
|
+
is empty.
|
|
150
|
+
6. Delete draft item removes the item from memory only. No HTTP DELETE is
|
|
151
|
+
sent.
|
|
152
|
+
7. Cancel changes restores the loaded persisted items, or an empty draft for a
|
|
153
|
+
new annotation. It also clears transient drawing and any save error.
|
|
154
|
+
8. Loaded items keep their persisted interpretation. No interpretation UI
|
|
155
|
+
exists in this batch.
|
|
156
|
+
|
|
157
|
+
## 10. Target association
|
|
158
|
+
|
|
159
|
+
1. Drawing never associates anything.
|
|
160
|
+
2. With a draft item selected and a runtime target selected (from the target
|
|
161
|
+
list or SVG), the explicit "Associate target <name>" button sets
|
|
162
|
+
`{ kind: 'runtime-target', target }`.
|
|
163
|
+
3. "Clear association" removes it.
|
|
164
|
+
4. Annotation selection and target selection are separate state.
|
|
165
|
+
5. The display reads "Target association: <name>" or "No association". This
|
|
166
|
+
wording is deliberately different from the inspector's "Target: <name>".
|
|
167
|
+
|
|
168
|
+
## 11. Relationship association
|
|
169
|
+
|
|
170
|
+
1. Options come only from the server graph already fetched from
|
|
171
|
+
`/api/observations/:handle/relationships`.
|
|
172
|
+
1. Pairwise options keep `relationshipKind`, `subjectTarget`, and
|
|
173
|
+
`relatedTarget`.
|
|
174
|
+
2. Page-level options keep `relationshipKind` only.
|
|
175
|
+
2. The user must choose an option and press "Associate relationship". Lines
|
|
176
|
+
and arrows never imply a relationship.
|
|
177
|
+
3. The display reads "Relationship association: <kind> (<subject> →
|
|
178
|
+
<related>)".
|
|
179
|
+
|
|
180
|
+
## 12. Zoom/pan behavior
|
|
181
|
+
|
|
182
|
+
1. The observation workspace now uses
|
|
183
|
+
`useZoomPan(viewport.width, viewport.height)` with the existing
|
|
184
|
+
`ZoomControls`: Zoom in, Zoom out, Fit, and Reset. Reset equals Fit.
|
|
185
|
+
2. Bounds are unchanged (`ZOOM_MIN = 1`, `ZOOM_MAX = 8`, `ZOOM_STEP = 1.25`).
|
|
186
|
+
3. Zoom and pan change only the SVG viewBox. Target and annotation geometry
|
|
187
|
+
and the screenshot frame are unchanged. Zoom state is never persisted.
|
|
188
|
+
|
|
189
|
+
## 13. Save/reload behavior
|
|
190
|
+
|
|
191
|
+
1. Save runs only when the session is `available`. It POSTs
|
|
192
|
+
`{ sourceHandle, parentAnnotationHandle?, items }` with `content-type` and
|
|
193
|
+
the token header. The browser supplies Origin.
|
|
194
|
+
2. On 201, the UI:
|
|
195
|
+
1. Reads the returned handle.
|
|
196
|
+
2. Re-reads the canonical view.
|
|
197
|
+
3. Replaces the draft items with the persisted items.
|
|
198
|
+
4. Sets the parent to the new handle.
|
|
199
|
+
5. Clears dirty.
|
|
200
|
+
6. Shows "Saved annotation <id>".
|
|
201
|
+
7. Refreshes the saved list.
|
|
202
|
+
3. On failure (403, 404, 409, 413, 422, 500, or network), the UI shows a
|
|
203
|
+
concise message, keeps the draft, and does not retry. It never falls back
|
|
204
|
+
to a new root annotation.
|
|
205
|
+
4. After a browser reload the token is re-acquired and the saved list rebuilt.
|
|
206
|
+
No draft is recovered, because none is stored in the browser.
|
|
207
|
+
|
|
208
|
+
## 14. Revision behavior
|
|
209
|
+
|
|
210
|
+
1. After a save, or after selecting a saved annotation, further edits save as
|
|
211
|
+
a child. The new artifact has a fresh `annotationId` and
|
|
212
|
+
`supersedesAnnotationId` pointing to the parent. It then becomes the loaded
|
|
213
|
+
parent.
|
|
214
|
+
2. The parent's manifest bytes are unchanged (browser-verified).
|
|
215
|
+
3. A stale parent gets 409. The UI shows "Save conflict", keeps the draft,
|
|
216
|
+
sends no second POST, and writes nothing.
|
|
217
|
+
|
|
218
|
+
## 15. Alias identity behavior
|
|
219
|
+
|
|
220
|
+
The browser test:
|
|
221
|
+
|
|
222
|
+
1. Aliases `current` to observation A.
|
|
223
|
+
2. Saves annotation X on A.
|
|
224
|
+
3. Moves `current` to observation B with the canonical `writeAliasCatalog`.
|
|
225
|
+
4. Restarts the viewer from `loadProjectViewerState`.
|
|
226
|
+
|
|
227
|
+
Result: B (shown as `current`) lists no saved annotations. A, selected by its
|
|
228
|
+
canonical id, still lists X, and X's persisted source still names A.
|
|
229
|
+
|
|
230
|
+
## 16. Source-unavailable behavior
|
|
231
|
+
|
|
232
|
+
With a draft mark present, the test moves the source observation directory
|
|
233
|
+
away before Save. The POST returns 404 and the UI shows "…no longer exists".
|
|
234
|
+
The draft and its rendered mark remain, no success message appears, and no
|
|
235
|
+
artifact is written. The directory is restored afterwards.
|
|
236
|
+
|
|
237
|
+
## 17. Accessibility
|
|
238
|
+
|
|
239
|
+
1. Toolbar mode buttons are native buttons with "<Mode> mode" labels and
|
|
240
|
+
`aria-pressed`.
|
|
241
|
+
2. The note input and the selected-note textarea have labels.
|
|
242
|
+
3. In select mode, marks are `role="button"`, `tabIndex=0`, `aria-pressed`,
|
|
243
|
+
with bounded labels such as "Rectangle annotation" or "Note annotation:
|
|
244
|
+
<up to 60 chars>".
|
|
245
|
+
4. Enter and Space select a mark, mirroring target selection.
|
|
246
|
+
5. Save status uses `role="status"`, and failures use `role="alert"`.
|
|
247
|
+
6. Browser-verified: Tab from the last target reaches the first mark, and
|
|
248
|
+
Enter or Space selects it. Target keyboard selection still works and does
|
|
249
|
+
not clear the annotation selection.
|
|
250
|
+
|
|
251
|
+
## 18. Browser tests
|
|
252
|
+
|
|
253
|
+
New `tests/browser/runtimeAnnotationAuthoring.test.ts` (15 real-Chromium
|
|
254
|
+
tests). It reuses the Prompt 2 fixtures in `tests/support/annotationAuthoringFixtures.ts`.
|
|
255
|
+
|
|
256
|
+
1. Availability: the project-aware toolbar has all seven modes and Save. The
|
|
257
|
+
standalone viewer has no drawing or save controls, target inspection still
|
|
258
|
+
works, and no non-GET request is sent.
|
|
259
|
+
2. All five mark kinds are drawn with real pointer input. The persisted kinds,
|
|
260
|
+
coordinates, `uninterpreted` state, id format, and canonical source are
|
|
261
|
+
checked. A note without text is not created.
|
|
262
|
+
3. Exact rectangle: a reverse drag from 250,220 to 100,120 persists 100, 120,
|
|
263
|
+
150, 100. A zero-area drag creates nothing.
|
|
264
|
+
4. Zoomed drawing: after three zoom-ins, a rectangle keeps its source
|
|
265
|
+
coordinates. Fit restores `0 0 800 600`, and target geometry is unchanged.
|
|
266
|
+
5. Panned drawing and exclusivity: a pan drag changes the viewBox and creates
|
|
267
|
+
no item. A rectangle drag creates an item, leaves the viewBox unchanged,
|
|
268
|
+
and persists the intended source coordinates.
|
|
269
|
+
6. DPR: at `deviceScaleFactor: 2` (confirmed `devicePixelRatio === 2`), the
|
|
270
|
+
rectangle and point persist CSS-pixel source values, not doubled ones.
|
|
271
|
+
7. Explicit target association: a rectangle drawn inside `header` stays
|
|
272
|
+
unassociated and does not select the target, until "Associate target
|
|
273
|
+
header". The persisted association is exact.
|
|
274
|
+
8. Explicit relationship association: the relationship is taken from the
|
|
275
|
+
server graph, the arrow stays unassociated until chosen, and the persisted
|
|
276
|
+
`relationshipKind`, `subjectTarget`, and `relatedTarget` are exact.
|
|
277
|
+
9. Draft editing:
|
|
278
|
+
1. Move keeps the id at +40,+30.
|
|
279
|
+
2. Note edit keeps the anchor and id.
|
|
280
|
+
3. Save keeps item ids.
|
|
281
|
+
4. Delete, then Cancel, restores the persisted items.
|
|
282
|
+
5. Cancel on a new draft empties it.
|
|
283
|
+
6. No DELETE request is sent.
|
|
284
|
+
10. Keyboard selection of marks and targets.
|
|
285
|
+
11. Save and reload: the same annotation id, item ids, rectangle geometry,
|
|
286
|
+
association, and source handle come back from canonical evidence.
|
|
287
|
+
12. Revision: a child supersedes the parent, the parent bytes are unchanged,
|
|
288
|
+
item ids are preserved, and the child becomes the loaded parent.
|
|
289
|
+
13. Stale revision: 409, conflict alert, draft kept, exactly one failed POST,
|
|
290
|
+
and nothing written.
|
|
291
|
+
14. Alias replacement does not rebind (section 15).
|
|
292
|
+
15. Source disappearance gives a visible failure with the draft kept
|
|
293
|
+
(section 16).
|
|
294
|
+
|
|
295
|
+
Unit tests: new `tests/unit/viewerAnnotationDraftModel.test.ts` (12 tests) covers:
|
|
296
|
+
|
|
297
|
+
1. Mode set
|
|
298
|
+
2. Frame validity
|
|
299
|
+
3. Drawn-mark normalization and refusal
|
|
300
|
+
4. New-item shape
|
|
301
|
+
5. Translation
|
|
302
|
+
6. The shared transform (fake CTM)
|
|
303
|
+
7. Exact-handle membership
|
|
304
|
+
8. Failure messages
|
|
305
|
+
9. Mark and association labels
|
|
306
|
+
|
|
307
|
+
Stabilization notes (test-only):
|
|
308
|
+
|
|
309
|
+
1. The stale-revision test waits for the post-save saved-list refresh to
|
|
310
|
+
finish before persisting a child from outside the page. Otherwise the
|
|
311
|
+
test's out-of-band writer raced the viewer's own evidence discovery on
|
|
312
|
+
Windows (see section 21).
|
|
313
|
+
2. The alias test leaves the page before closing the first viewer server, so
|
|
314
|
+
in-flight requests do not hold its connections open during `close()`.
|
|
315
|
+
3. After these changes the file passed on repeated runs.
|
|
316
|
+
|
|
317
|
+
## 19. Regression validation
|
|
318
|
+
|
|
319
|
+
Every command below was run on Windows in the repository root.
|
|
320
|
+
|
|
321
|
+
1. `npm run typecheck`: PASS
|
|
322
|
+
2. `npm run lint`: PASS
|
|
323
|
+
3. `npm test`: PASS (77 files, 1298 tests)
|
|
324
|
+
4. `npm run build`: PASS
|
|
325
|
+
5. `npm run test:browser`: PASS (22 files, 202 tests, real Chromium)
|
|
326
|
+
6. `npm run test:security`: PASS (unit: 13 files, 143 tests. Browser: 3 files,
|
|
327
|
+
77 tests)
|
|
328
|
+
7. `npm run check:docs`: PASS
|
|
329
|
+
8. `npm pack --dry-run`: PASS (344 files, 888.8 kB)
|
|
330
|
+
|
|
331
|
+
`tests/browser/observationSvgWorkspace.test.ts` passes unchanged. It covers
|
|
332
|
+
canonical target geometry, unresolved-target honesty, list and SVG selection,
|
|
333
|
+
inspector, and relationships. `projectWorkflowViewer`, `pwaHardening`,
|
|
334
|
+
comparison, reference, binding, fidelity, and context browser suites all
|
|
335
|
+
pass.
|
|
336
|
+
|
|
337
|
+
## 20. Security regression
|
|
338
|
+
|
|
339
|
+
1. No server file changed. Host, Origin, token, body-size, path, symlink,
|
|
340
|
+
method, and PWA no-cache tests all pass in `test:security`.
|
|
341
|
+
2. The viewer sends the token only in the `x-frontend-observer-authoring-token`
|
|
342
|
+
header of same-origin POSTs and stores it nowhere.
|
|
343
|
+
3. The service-worker configuration is unchanged.
|
|
344
|
+
|
|
345
|
+
## 21. Scope audit
|
|
346
|
+
|
|
347
|
+
1. external-reference annotation UI changed: false
|
|
348
|
+
2. contract promotion implemented: false
|
|
349
|
+
3. reference materialization implemented: false
|
|
350
|
+
4. ContractPrimitive vocabulary changed: false
|
|
351
|
+
5. annotation artifact schema changed: false
|
|
352
|
+
6. Prompt 2 security policy weakened: false
|
|
353
|
+
7. project config schema changed: false
|
|
354
|
+
8. alias catalog schema changed: false
|
|
355
|
+
9. package version changed: false
|
|
356
|
+
10. dependency changed: false
|
|
357
|
+
|
|
358
|
+
No file under `src/` changed. `ReferenceWorkspace` and
|
|
359
|
+
`ReferenceRegionOverlaySvg` are untouched. No intent, category, confirmation,
|
|
360
|
+
or contract-primitive UI exists.
|
|
361
|
+
|
|
362
|
+
Remaining risk (pre-existing, not introduced here, protected files not
|
|
363
|
+
modified):
|
|
364
|
+
|
|
365
|
+
1. On Windows, the canonical artifact writers' atomic directory rename can
|
|
366
|
+
fail with `EPERM` if viewer evidence discovery is reading inside the
|
|
367
|
+
writer's `.tmp-<id>` directory at that moment. That happens when a
|
|
368
|
+
separate process or tab refreshes `/api/index` exactly during a save.
|
|
369
|
+
2. Normal single-page use does not trigger it, because the UI refreshes only
|
|
370
|
+
after a 201.
|
|
371
|
+
3. A future batch that owns the writer or discovery could skip `.tmp-*`
|
|
372
|
+
directories during discovery.
|
|
373
|
+
|
|
374
|
+
## 22. Changed files
|
|
375
|
+
|
|
376
|
+
### 22.1 Added
|
|
377
|
+
|
|
378
|
+
1. `viewer/src/types/visualAnnotation.ts`
|
|
379
|
+
2. `viewer/src/svg/sourceCoordinates.ts`
|
|
380
|
+
3. `viewer/src/annotation/annotationGeometry.ts` (pure draft geometry and
|
|
381
|
+
mode model, split out so unit tests can reach it)
|
|
382
|
+
4. `viewer/src/components/AnnotationLayer.tsx`
|
|
383
|
+
5. `viewer/src/components/AnnotationToolbar.tsx`
|
|
384
|
+
6. `viewer/src/components/RuntimeAnnotationPanel.tsx`
|
|
385
|
+
7. `viewer/src/hooks/useAuthoringSession.ts`
|
|
386
|
+
8. `viewer/src/hooks/useRuntimeAnnotations.ts`
|
|
387
|
+
9. `viewer/src/hooks/useRuntimeAnnotationDraft.ts`
|
|
388
|
+
10. `viewer/src/hooks/useAnnotationPointerInteraction.ts` (frame-agnostic
|
|
389
|
+
exclusive pointer binding, reusable by Prompt 4)
|
|
390
|
+
11. `tests/browser/runtimeAnnotationAuthoring.test.ts`
|
|
391
|
+
12. `tests/unit/viewerAnnotationDraftModel.test.ts`
|
|
392
|
+
13. `docs/reports/v0.9-batch3-runtime-screenshot-annotation-authoring.md`
|
|
393
|
+
|
|
394
|
+
### 22.2 Modified
|
|
395
|
+
|
|
396
|
+
1. `viewer/src/components/ObservationWorkspace.tsx`
|
|
397
|
+
2. `viewer/src/components/TargetOverlaySvg.tsx`
|
|
398
|
+
3. `viewer/src/hooks/useZoomPan.ts`
|
|
399
|
+
4. `viewer/src/styles/index.css`
|
|
400
|
+
5. `tests/support/evidenceFixtures.ts`: `writeRichObservationFixture` gains an
|
|
401
|
+
optional `requestId`, because alias catalogs require 64-hex request ids.
|
|
402
|
+
Existing callers are unaffected.
|
|
403
|
+
|
|
404
|
+
### 22.3 Generated paths
|
|
405
|
+
|
|
406
|
+
1. `dist/` was rebuilt. It is ignored.
|
|
407
|
+
2. Tests create and remove temporary directories under the OS temp directory.
|
|
408
|
+
3. Validation logs went to the session scratchpad.
|
|
409
|
+
|
|
410
|
+
## 23. Remaining next step
|
|
411
|
+
|
|
412
|
+
v0.9 Prompt 4 — External-reference annotation and reference-region authoring
|