@dailephd/my-frontend-observer 0.10.0 → 0.10.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.
- package/CHANGELOG.md +490 -479
- package/LICENSE +21 -21
- package/README.md +375 -365
- package/dist/application/projectCheckService.d.ts +6 -0
- package/dist/application/projectCheckService.js +8 -1
- package/dist/application/projectCheckService.js.map +1 -1
- package/dist/application/projectWorkflowService.d.ts +7 -2
- package/dist/application/projectWorkflowService.js +10 -3
- package/dist/application/projectWorkflowService.js.map +1 -1
- package/dist/cli.js +510 -510
- package/dist/viewer/index.html +13 -13
- package/dist/viewer/sw.js +1 -1
- package/docs/ARCHITECTURE.md +1394 -1385
- package/docs/CI_CD.md +349 -338
- package/docs/COMMANDS.md +1035 -1026
- package/docs/CONTRACTS.md +1971 -1960
- package/docs/CURRENT_STATE.md +1277 -1252
- package/docs/DEVELOPMENT.md +240 -237
- package/docs/DOCUMENTATION_PRESERVATION_POLICY.md +50 -50
- package/docs/PROJECT_DESCRIPTION.md +2248 -2224
- package/docs/PROJECT_MILESTONES.md +2681 -2558
- package/docs/PROJECT_OVERVIEW.md +200 -196
- package/docs/QUICKSTART.md +100 -100
- package/docs/RELEASE.md +37 -36
- package/docs/ROADMAP.md +1105 -1034
- package/docs/SECURITY.md +297 -297
- package/docs/WORKFLOWS.md +806 -796
- package/docs/plans/v0.10-implementation-plan.md +1509 -1509
- package/docs/plans/v0.8-implementation-plan.md +655 -655
- package/docs/plans/v0.8.1-cli-usability-patch-plan.md +505 -505
- package/docs/plans/v0.9-implementation-plan.md +1529 -1529
- package/docs/plans/v0.9.1-implementation-plan.md +468 -468
- package/docs/reports/v0.10-batch1-visual-change-workflow-foundation.md +102 -102
- package/docs/reports/v0.10-batch2-project-composition-check-recording.md +103 -103
- package/docs/reports/v0.10-batch3-viewer-visual-change-workspace.md +93 -93
- package/docs/reports/v0.10-batch4-actual-frontend-entry.md +59 -59
- package/docs/reports/v0.10-batch5-reference-driven-entry.md +238 -238
- package/docs/reports/v0.10-batch6-coding-agent-handoff.md +85 -85
- package/docs/reports/v0.10-batch7-correction-review-acceptance.md +145 -145
- package/docs/reports/v0.10-batch8-integrated-acceptance.md +109 -109
- package/docs/reports/v0.10-implementation-completeness-documentation-reconciliation.md +344 -344
- package/docs/reports/v0.10-pre-release-readiness.md +120 -120
- package/docs/reports/v0.10-release-preparation.md +70 -70
- package/docs/reports/v0.10.1-project-check-baseline-context-implementation.md +86 -0
- package/docs/reports/v0.7-bounded-fidelity-context-prompt7.md +243 -243
- package/docs/reports/v0.7-implementation-completeness-documentation-reconciliation.md +497 -497
- package/docs/reports/v0.7-pre-release-readiness.md +337 -337
- package/docs/reports/v0.7-reference-binding-prompt5.md +223 -223
- package/docs/reports/v0.7-reference-compatibility-prompt4.md +234 -234
- package/docs/reports/v0.7-reference-correction-workflow-prompt8.md +222 -222
- package/docs/reports/v0.7-reference-fidelity-prompt6.md +216 -216
- package/docs/reports/v0.7-reference-foundation-prompt1.md +151 -151
- package/docs/reports/v0.7-reference-regions-prompt2.md +195 -195
- package/docs/reports/v0.7-reference-requirements-prompt3.md +217 -217
- package/docs/reports/v0.7-release-prep.md +423 -423
- package/docs/reports/v0.8-binding-fidelity-interaction-batch6.md +279 -279
- package/docs/reports/v0.8-bounded-context-correlation-batch7.md +233 -233
- package/docs/reports/v0.8-comparison-contract-inspection-batch4.md +279 -279
- package/docs/reports/v0.8-evidence-index-readers-batch2.md +247 -247
- package/docs/reports/v0.8-implementation-completeness-documentation-reconciliation.md +741 -741
- package/docs/reports/v0.8-integrated-viewer-acceptance-batch8.md +128 -128
- package/docs/reports/v0.8-observation-svg-inspection-batch3.md +223 -223
- package/docs/reports/v0.8-prerelease-readiness-cross-platform-security-code-rot.md +687 -687
- package/docs/reports/v0.8-reference-candidate-inspection-batch5.md +232 -232
- package/docs/reports/v0.8-viewer-runtime-pwa-batch1.md +278 -278
- package/docs/reports/v0.8.1-implementation-completeness-documentation-reconciliation.md +114 -114
- package/docs/reports/v0.8.1-prerelease-readiness-cross-platform-security-code-rot.md +170 -170
- package/docs/reports/v0.9-architecture-retrieval.md +14 -37
- package/docs/reports/v0.9-final-pre-release-readiness.md +209 -209
- package/docs/reports/v0.9-final-readiness-corrections.md +530 -530
- package/docs/reports/v0.9-pre-release-readiness.md +169 -169
- package/docs/reports/v0.9.1-batch1-pwa-hard-gate-isolation.md +359 -359
- package/docs/reports/v0.9.1-batch2-hard-gate-validation-integration.md +262 -262
- package/docs/reports/v0.9.1-pre-release-readiness.md +206 -206
- package/package.json +59 -59
|
@@ -1,223 +1,223 @@
|
|
|
1
|
-
# v0.8 Batch 3 — Runtime Observation Inspection and SVG Overlays — Implementation Report
|
|
2
|
-
|
|
3
|
-
## 1. Starting state
|
|
4
|
-
|
|
5
|
-
- Branch: `master`
|
|
6
|
-
- Starting HEAD: `be82055e29ca8d12cee61ae933dc85e6af3f2b9f` ("feat: add v0.8 viewer evidence indexing and readers")
|
|
7
|
-
- `origin/master` after `git fetch`: `a1de8ac01e1367b60021cb04226f56369fa2debb`
|
|
8
|
-
- `git rev-list --left-right --count origin/master...HEAD`: `0 2` — local is exactly Batch 1 + Batch 2 ahead of origin, no divergence.
|
|
9
|
-
- Starting `git status --short`: clean.
|
|
10
|
-
- Package version confirmed `0.7.0` throughout; never bumped.
|
|
11
|
-
|
|
12
|
-
## 2. Predecessor reports inspected
|
|
13
|
-
|
|
14
|
-
Read both local reports in full (not console summaries):
|
|
15
|
-
|
|
16
|
-
- `docs/reports/v0.8-viewer-runtime-pwa-batch1.md` — confirmed host `127.0.0.1`/port `4319`, `src/viewerServer/httpServer.ts`/`viewerService.ts` ownership, PWA `navigateFallbackDenylist: [/^\/api\//]`.
|
|
17
|
-
- `docs/reports/v0.8-evidence-index-readers-batch2.md` — confirmed exact module/API details reused unchanged this batch: `evidence/index.ts#loadArtifactByHandle`/`buildEvidenceIndexMetadata`, `evidence/handles.ts` (`<family-slug>:<percent-encoded relativeDir>`), `evidence/pathSafety.ts#resolveContainedDir` (including its Batch 2 forward-slash-root bugfix - re-used as-is, not re-litigated), `evidence/classify.ts#ClassifiedRecord`/`ViewerSupportState`, `evidence/projection.ts#EvidenceMetadataRecord`/`EvidenceArtifactDetail`, `GET /api/index`/`GET /api/artifacts/<handle>`/`GET /api/media/<handle>/<role>` exact semantics/status codes, `viewer/src/hooks/useEvidenceIndex.ts`/`useArtifactDetail.ts`, `viewer/src/components/EvidenceList.tsx`/`ArtifactPreview.tsx`. No parallel API was created; every Batch 3 addition is additive to this exact boundary.
|
|
18
|
-
|
|
19
|
-
## 3. Frozen planning authority inspected
|
|
20
|
-
|
|
21
|
-
`docs/DOCUMENTATION_PRESERVATION_POLICY.md`, `docs/PROJECT_MILESTONES.md` (Milestone 8), `docs/ROADMAP.md` (v0.8), `docs/plans/v0.8-implementation-plan.md` (Batch 3 section + cross-batch invariants §7) — all previously read in full during Batches 1-2, re-confirmed unchanged. `docs/ARCHITECTURE.md`, `docs/CONTRACTS.md` (media/schema-version constants, previously read in full), `docs/WORKFLOWS.md`, `docs/COMMANDS.md` (`view` section, then edited), `docs/DEVELOPMENT.md`. New this batch: `src/domain/schema.ts` (full `TargetGeometry`/`TargetEvidenceRecord`/`PageEvidence`-shape/`ScrollScenarioEvidence` read), `src/domain/relationships.ts` (`deriveLayoutRelationships` full signature/result type), `src/browser/evidenceCapture.ts` (`capturePageEvidence`/`captureResolvedTargetRecord` - the coordinate-audit source), `src/browser/chromiumAdapter.ts` (screenshot capture call, context creation), `src/artifacts/artifactReader.ts`, `src/viewerServer/httpServer.ts`, `src/viewerServer/evidence/*`, `viewer/src/*`, `tests/unit/relationshipDerivation.test.ts` (unresolved-target fixture pattern reused), `tests/unit/cliFrontendContracts.test.ts` (observation fixture pattern already reused since Batch 2).
|
|
22
|
-
|
|
23
|
-
Confirmed Batch 3's title/scope in `docs/plans/v0.8-implementation-plan.md` match the task exactly; no material difference found.
|
|
24
|
-
|
|
25
|
-
## 4. Prior-path audit (task §6)
|
|
26
|
-
|
|
27
|
-
Inspected `Z:\Users\newuser\Projects\`: exactly one sibling workflow directory exists, `my-frontend-observer.my-dev-kit-workflow` (correctly separated). No malformed duplicate (missing-separator variant) was found at any location - the two candidate paths given in the task text were in fact identical strings, so there was nothing to distinguish. Neither location was moved, deleted, or reused; Batch 3's own work used exclusively `...\my-dev-kit-workflow\v0.8\batch-03`.
|
|
28
|
-
|
|
29
|
-
## 5. WORKFLOW_ROOT and my-dev-kit retrieval
|
|
30
|
-
|
|
31
|
-
`WORKFLOW_ROOT` = `Z:\Users\newuser\Projects\my-frontend-observer.my-dev-kit-workflow\v0.8\batch-03`. Index built successfully:
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
npx @dailephd/my-dev-kit@latest index --root . --src src --src tests --src viewer --out "<WORKFLOW_ROOT>\my-dev-kit-index" --call-graph --json
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
All seven required searches were run (observation geometry ownership, relationship ownership, scroll/visibility/overflow evidence, Batch 2 projection/API ownership, viewer React selection/data loading, screenshot coordinate semantics, plus the general index build). Results consistently pointed at the same files direct inspection then confirmed in detail (`src/domain/schema.ts`, `src/domain/relationships.ts`, `src/browser/evidenceCapture.ts`, `src/browser/chromiumAdapter.ts`, `src/viewerServer/evidence/*`) - no `lookup`/`source`/`slice` follow-up was needed since the searches were unambiguous and direct file reads (§3) established the exact contracts.
|
|
38
|
-
|
|
39
|
-
## 6. Coordinate audit (task §9 - the load-bearing decision)
|
|
40
|
-
|
|
41
|
-
Established from direct source inspection, not assumption:
|
|
42
|
-
|
|
43
|
-
- **Target geometry**: `src/browser/evidenceCapture.ts#captureResolvedTargetRecord` calls `el.getBoundingClientRect()` inside `handle.evaluate(...)`. This is viewport-relative CSS pixels (origin at the current viewport's top-left), captured from the same live page state the screenshot is taken from immediately after (same function/request lifecycle - `docs/ARCHITECTURE.md`'s existing "same live page/readiness state" invariant, unchanged by this batch).
|
|
44
|
-
- **Screenshot**: `src/browser/chromiumAdapter.ts` calls `page.screenshot({type:'png'})` with no `fullPage` option - Playwright's default is `fullPage: false`, capturing exactly the current viewport at the current scroll position.
|
|
45
|
-
- **Device scale factor**: `browser.newContext({viewport: {width, height}})` never sets `deviceScaleFactor` anywhere in this codebase, so Playwright's own default (`1`) applies to every observation this repository can currently produce. Consequence: the screenshot PNG's raw pixel dimensions equal `requestConfig.viewport.width × requestConfig.viewport.height` exactly - **1 CSS pixel = 1 PNG pixel** for every currently-producible observation.
|
|
46
|
-
- **`devicePixelRatio` disposition**: captured as `pageEvidence.devicePixelRatio` (`window.devicePixelRatio`, always `1` under the context above) - kept as **informational observation-level display evidence only**. It is never read by any Batch 3 geometry/coordinate code path (`grep`-verified: the only production reference to `devicePixelRatio` outside `evidenceCapture.ts` itself is the read-only display line in `ObservationInspector.tsx`).
|
|
47
|
-
- **Canonical viewport source**: `requestConfig.viewport` (`{width, height}`), a required, strongly-typed field validated on every `ObservationArtifact` (unlike the loosely-typed `pageEvidence: Record<string, EvidenceField<unknown>>` bag, which a hand-constructed test fixture could in principle omit fields from) - chosen as the authoritative SVG `viewBox` source for exactly this reason.
|
|
48
|
-
|
|
49
|
-
**Conclusion**: no coordinate rewriting is needed or performed. The SVG `viewBox` is set to `0 0 {requestConfig.viewport.width} {requestConfig.viewport.height}` - the exact frame `getBoundingClientRect()` already used - and the screenshot `<image>` fills that same viewBox (`preserveAspectRatio="none"`, safe here specifically because the two frames are pixel-identical per the above). Target rectangles use `geometry.x/y/width/height` completely unchanged. This is also robust against a hypothetical future capture path using a different `deviceScaleFactor`, because the `<image>`/viewBox scaling is browser-native presentation behavior, never a manual multiplication in this codebase - satisfying task §9's explicit preference for a `viewBox`-based presentation mapping over evidence rewriting. No `BLOCKED_V08_BATCH3_COORDINATE_MAPPING_INADEQUATE` condition was found.
|
|
50
|
-
|
|
51
|
-
## 7. Observation-view projection architecture
|
|
52
|
-
|
|
53
|
-
Deliberately **not** a new server-side artifact-detail endpoint. The existing Batch 2 `GET /api/artifacts/<handle>` already returns the full, already-validated `ObservationArtifact` (targetEvidence, pageEvidence, requestConfig, screenshot field, completion, diagnostics, provenance, browser) unchanged - everything the target/observation inspector needs. Batch 3's "projection" is therefore two things:
|
|
54
|
-
|
|
55
|
-
1. **One additive server-side computation** (`src/viewerServer/evidence/observationView.ts#getObservationRelationships`, exposed as `GET /api/observations/<handle>/relationships`) - the only genuinely new derivation this batch performs, and it is a thin, defense-in-depth-wrapped pass-through to the existing canonical `deriveLayoutRelationships` (§8). Ephemeral: computed fresh per request, never persisted, never a new artifact family.
|
|
56
|
-
2. **Client-side presentation reshaping only** (`viewer/src/observation/targetOrder.ts#orderedTargets`) - selects/orders already-fetched fields (configured-target order, `hasGeometry` flag), explicitly not evidence derivation (frozen plan §27 permits "selection; filtering; formatting").
|
|
57
|
-
|
|
58
|
-
Both are ephemeral, in-memory, per-request/per-render, and derived only from already-validated canonical domain values and canonical relationship-engine results - no `viewer.json`, no new artifact family, no mutation of `ObservationArtifact`.
|
|
59
|
-
|
|
60
|
-
## 8. Canonical relationship-engine reuse
|
|
61
|
-
|
|
62
|
-
`src/viewerServer/evidence/observationView.ts` calls `deriveLayoutRelationships` (`src/domain/relationships.ts`) exactly once, unchanged, against the already-classified, already-validated `ObservationArtifact` - the same function `docs/ARCHITECTURE.md`'s v0.4 architecture section documents as "the one canonical pure derivation." No relationship predicate (overlap, relative width, ordering, containment, fit, sequencing, page-width fit) is reimplemented anywhere in `src/viewerServer/` or `viewer/src/`. Proven by an exact-equality test (`tests/unit/observationRelationshipsServer.test.ts` "returns exactly the canonical `deriveLayoutRelationships(...)` result... never a second computation") that independently re-derives the same graph directly from the persisted artifact and asserts `toEqual` against the HTTP response body.
|
|
63
|
-
|
|
64
|
-
## 9. Screenshot loading path
|
|
65
|
-
|
|
66
|
-
Unchanged Batch 2 mechanism: `viewer/src/components/TargetOverlaySvg.tsx`'s `<image href={`/api/media/${handle}/screenshot`}>` - the same handle already used for `/api/artifacts/<handle>`, the same `screenshot` media role Batch 2 already implemented (`mediaResolver.ts`, unmodified). No new media role, no local path, no `file://`, no base64 embedding, no new viewer-owned copy of the bytes. Loaded only when the observation's visual workspace actually renders (i.e., only after a user selects a supported `observation` record) - never eagerly for the whole index.
|
|
67
|
-
|
|
68
|
-
## 10. SVG component/layout
|
|
69
|
-
|
|
70
|
-
`viewer/src/components/TargetOverlaySvg.tsx`: root `<svg viewBox="0 0 {w} {h}">` (§6); one `<image>` filling it; one `<rect>` per target with usable geometry (`role="button"`, keyboard-operable, `data-target-name` for identity); one `<text>` label per rectangle when the labels toggle is on; one `<line>` per pairwise relationship whose both endpoints have geometry (drawn between rectangle centers), only when the relationships toggle is on. `viewer/src/components/{TargetList,ObservationInspector,EvidenceFieldView,ObservationWorkspace}.tsx` provide the surrounding three-pane layout (Targets | Screenshot+SVG | Inspector) nested inside the existing Batch 1 `app-shell__workspace` region - the outer nav/details shell structure is untouched.
|
|
71
|
-
|
|
72
|
-
## 11. Target selection architecture
|
|
73
|
-
|
|
74
|
-
`viewer/src/components/ObservationWorkspace.tsx` holds `selected: string | undefined` (React `useState`, presentation-only, reset via a `useEffect` keyed on `handle` whenever a different observation is selected - task §17's "changing observation resets or safely rebinds selection" requirement). Both the target-list `<button>` and the SVG `<rect>` call the same `onSelect(name)` callback using the target's existing stable `name`; both re-render their own `aria-pressed`/selected-class state from the single shared `selected` value, and the inspector reads the same value - genuine bidirectional synchronization through one source of truth, not two parallel selection states. Proven in real Chromium (`tests/browser/observationSvgWorkspace.test.ts`, two dedicated tests: SVG→list/inspector and list→SVG).
|
|
75
|
-
|
|
76
|
-
## 12. Unresolved-target behavior
|
|
77
|
-
|
|
78
|
-
`orderedTargets()`'s `hasGeometry` is `true` only when `geometry.state` is `'available'` or `'partial'` - never merely because the target is configured. `TargetOverlaySvg` skips rendering entirely for any target without usable geometry (`geometryOf()` returns `undefined`, the `.map` callback returns `null`). No `x=0 y=0 width=0 height=0` fallback exists anywhere in the code. The target remains fully selectable from `TargetList` (shown with its real resolution status, e.g. `not-found (no geometry)`) and its canonical resolution/reason evidence is shown honestly in `ObservationInspector`. Proven with a real fixture (`missingWidget`, `not-found`) at the unit, server, and real-Chromium levels (`tests/unit/observationCoordinateMapping.test.ts`, `observationRelationshipsServer.test.ts`, `tests/browser/observationSvgWorkspace.test.ts`).
|
|
79
|
-
|
|
80
|
-
## 13. Overlay controls
|
|
81
|
-
|
|
82
|
-
Three independently toggleable controls (`ObservationWorkspace`'s `OverlayToggles` state): **geometry** (target rectangles), **labels** (target-name text, disabled/hidden when geometry is off, since a label with no rectangle would be presentation-meaningless), **relationships** (connector lines). Toggling never refetches or alters the underlying artifact/graph - purely a render-time filter over already-fetched data.
|
|
83
|
-
|
|
84
|
-
## 14. Visibility presentation
|
|
85
|
-
|
|
86
|
-
Not a separate overlay category in this batch: `TargetVisibility.visible` (existing canonical evidence, `derived` source) is shown honestly in the target inspector (`EvidenceFieldView` on `record.visibility`) exactly as the artifact states it - never inferred from screenshot pixels or from the mere existence of a rectangle. No new visibility threshold was introduced.
|
|
87
|
-
|
|
88
|
-
## 15. Overflow presentation
|
|
89
|
-
|
|
90
|
-
`record.style` (display/position/overflow-x/overflow-y, existing canonical `computed-browser` evidence) and `record.layout` (scroll/client dimensions, existing canonical `browser` evidence) are shown honestly in the target inspector via `EvidenceFieldView`, unmodified and unreinterpreted. No SVG rectangle-intersection-based overflow inference exists anywhere in this batch.
|
|
91
|
-
|
|
92
|
-
## 16. Scroll presentation
|
|
93
|
-
|
|
94
|
-
`ObservationInspector` distinguishes, using only existing canonical evidence: no scroll scenario configured (`artifact.scrollScenarioEvidence === undefined` - shown as an honest "No scroll scenario was configured for this observation" note) vs. available evidence (`initial`/`final`/`transition`/`scrollOwner`, shown via the existing `ScrollScenarioTransition`/`ScrollOwnerInterpretation` fields, `EvidenceFieldView` honoring the field's own `EvidenceField` state for `scrollOwner`). No browser action is re-run from the viewer; no historical trajectory is animated; no movement arrow is fabricated - the presentation is textual/status-only, exactly matching task §22's "if a faithful spatial overlay is not possible, show scroll evidence in the inspector/status layer."
|
|
95
|
-
|
|
96
|
-
## 17. Inspector evidence exposed
|
|
97
|
-
|
|
98
|
-
**Target inspector**: resolution status, tag, semantics (role/name), semantic state, landmark, geometry, style, layout metrics, visibility, containment - each via `EvidenceFieldView`, which renders `available`/`partial` (with source and, for partial, the reason), `unavailable` (with reason), and `not-applicable` (with optional reason) distinctly - never a fabricated empty string or zero for missing evidence. Plus the relationships involving that target.
|
|
99
|
-
|
|
100
|
-
**Observation inspector**: observation id, request id, schema version, producer, viewport, page title, requested/final URL, device pixel ratio, document width/height, window scroll X/Y, completion state, diagnostics, scroll-scenario evidence (or its honest absence), and the full relationship list.
|
|
101
|
-
|
|
102
|
-
## 18. Files created
|
|
103
|
-
|
|
104
|
-
- `src/viewerServer/evidence/observationView.ts`
|
|
105
|
-
- `viewer/src/types/observation.ts` (type-only re-exports of canonical Node domain types - erased at build time, no runtime coupling)
|
|
106
|
-
- `viewer/src/observation/targetOrder.ts`
|
|
107
|
-
- `viewer/src/hooks/useObservationRelationships.ts`
|
|
108
|
-
- `viewer/src/components/TargetOverlaySvg.tsx`, `TargetList.tsx`, `ObservationInspector.tsx`, `ObservationWorkspace.tsx`, `EvidenceFieldView.tsx`
|
|
109
|
-
- `tests/unit/observationCoordinateMapping.test.ts`, `observationRelationshipsServer.test.ts`, `observationFixtureSanity.test.ts`
|
|
110
|
-
- `tests/browser/observationSvgWorkspace.test.ts`
|
|
111
|
-
- `docs/reports/v0.8-observation-svg-inspection-batch3.md` (this file)
|
|
112
|
-
|
|
113
|
-
## 19. Files modified
|
|
114
|
-
|
|
115
|
-
- `src/viewerServer/httpServer.ts` - added `GET /api/observations/<handle>/relationships` routing (405 for write methods, 404 unknown handle, 409 not-an-observation/not-currently-loadable/derivation-failed, 200 with the graph); `/api/status`, `/api/index`, `/api/artifacts/<handle>`, `/api/media/<handle>/<role>`, and static-asset serving byte-for-byte unchanged.
|
|
116
|
-
- `tests/support/evidenceFixtures.ts` - added `realisticPageEvidence`, `unresolvedTarget`, `buildRealPng` (a genuinely decodable solid-color PNG, distinct from the existing header-only `buildMinimalPng`), `writeRichObservationFixture`; extended `buildObservation` with optional `pageEvidence`/`viewport` parameters (backward compatible - existing call sites unaffected, default values unchanged).
|
|
117
|
-
- `viewer/src/components/ArtifactPreview.tsx` - branches to `ObservationWorkspace` for `family === 'observation'`; every other family's raw-JSON preview is unchanged.
|
|
118
|
-
- `viewer/src/styles/index.css` - additive rules for the new workspace/list/SVG/inspector UI.
|
|
119
|
-
- `tests/unit/viewerPwaBuild.test.ts` - added the explicit Batch 3 cache-boundary assertion (§20).
|
|
120
|
-
- `tests/browser/viewerEvidenceShell.test.ts` - two pre-existing Batch 2 assertions that selected an `observation` record to test the *generic* raw-JSON on-demand-load path and the *generic* no-visualization invariant now legitimately conflict with Batch 3's real observation workspace; both were repointed to a `comparison` record (which still exercises exactly the generic path/invariant they were written to protect) rather than weakened - the same kind of sanctioned evolution as Batch 1→2's placeholder-text update and Batch 2's `TST-401`.
|
|
121
|
-
- `docs/ARCHITECTURE.md`, `docs/COMMANDS.md` - new/updated Batch 3 sections (§6-§17 above, condensed).
|
|
122
|
-
|
|
123
|
-
## 20. PWA cache verification
|
|
124
|
-
|
|
125
|
-
No `vite.config.ts`/service-worker change needed: `GET /api/observations/<handle>/relationships` lives under the already-denylisted `/api/` prefix. Extended `tests/unit/viewerPwaBuild.test.ts` with an explicit assertion against the real built `sw.js`: still exactly one `registerRoute` call, and the precache manifest contains no `/api/observations` entry.
|
|
126
|
-
|
|
127
|
-
## 21. Tests added/modified and behavior protected
|
|
128
|
-
|
|
129
|
-
| Test file | Level | Protects |
|
|
130
|
-
|---|---|---|
|
|
131
|
-
| `observationFixtureSanity.test.ts` | unit | The new `buildRealPng` generator produces a genuinely decodable PNG at exact viewport pixel dimensions (underpins every browser-level assertion below). |
|
|
132
|
-
| `observationCoordinateMapping.test.ts` | unit | `requestConfig.viewport` presence/fidelity; geometry preserved exactly (fractional values, no rounding); `devicePixelRatio≠1` never multiplies geometry; `partial` geometry state preserved (never promoted/discarded); geometry partly outside the viewport preserved unclamped; relationship engine never includes the unresolved target. |
|
|
133
|
-
| `observationRelationshipsServer.test.ts` | unit/integration (real HTTP) | Server relationship route is byte-for-byte identical to a direct `deriveLayoutRelationships` call (proves no second engine); unresolved target excluded from pairwise relationships; a real geometric relationship (`header above footer`) is produced; non-observation handle → 409; unknown handle → 404; unsupported-version observation → 409; write methods → 405. |
|
|
134
|
-
| `viewerPwaBuild.test.ts` (+1) | build/integration | New route covered by the existing `/api/` denylist, no new runtime-caching rule. |
|
|
135
|
-
| `viewerEvidenceShell.test.ts` (2 updated) | browser | Generic Batch 2 on-demand-load/no-visualization invariants still hold for non-observation families after Batch 3. |
|
|
136
|
-
| `observationSvgWorkspace.test.ts` | browser (real Chromium, real persisted evidence) | Full real-browser proof - see §22. |
|
|
137
|
-
|
|
138
|
-
## 22. Real-browser proof (task §33)
|
|
139
|
-
|
|
140
|
-
`tests/browser/observationSvgWorkspace.test.ts`, against the actual built `dist/viewer` PWA, the actual built loopback server, and one real observation (`writeRichObservationFixture`: real writer, genuinely decodable 800×600 PNG screenshot, two geometrically resolved targets plus one genuine `not-found` target). Five tests, all passing:
|
|
141
|
-
|
|
142
|
-
1. Screenshot visibly loads (`<image href>` resolves to a real `/api/media/observation:...` URL); resolved targets (`header`, `sidebar`) have real `<rect>` elements whose `x`/`y`/`width`/`height` attributes exactly match the fixture's canonical geometry (`0,0,800,80` and `0,100,200,400`); the unresolved target (`missingWidget`) receives **no** `<rect>` at all and is shown honestly (`not-found`) in the target list.
|
|
143
|
-
2. Selecting the SVG `<rect>` for `header` sets `aria-pressed="true"` on both the rect and its list entry, and the inspector shows `Target: header` with matching geometry text (`x:0 y:0 w:800 h:80`).
|
|
144
|
-
3. Selecting `sidebar` from the target list sets `aria-pressed="true"` on the corresponding SVG rect and updates the inspector - the reverse-direction synchronization.
|
|
145
|
-
4. The relationships panel shows the real canonical `above` relationship between `header`/`footer` labeled "Derived evidence", and at least one connector `<line>` is actually drawn between resolved targets.
|
|
146
|
-
5. After `page.setViewportSize` changes (from 1000×800 to 1400×900), the rendered SVG element's bounding-box aspect ratio remains within 0.05 of the canonical 800:600 (4:3) ratio - screenshot and overlay scale together, proving the `viewBox` mapping holds under resize.
|
|
147
|
-
|
|
148
|
-
## 23. Validation results
|
|
149
|
-
|
|
150
|
-
| Command | Result |
|
|
151
|
-
|---|---|
|
|
152
|
-
| `npm run typecheck` | **PASS** (zero errors, both `tsconfig.json` and `viewer/tsconfig.json`) |
|
|
153
|
-
| `npm run lint` | **PASS** (zero errors/warnings) |
|
|
154
|
-
| `npm test` | **PASS** — 1085/1085 tests, 59/59 files |
|
|
155
|
-
| `npm run build` | **PASS** — unchanged Node/library output plus `dist/viewerServer/evidence/observationView.js` and the rebuilt `dist/viewer/**` PWA |
|
|
156
|
-
| `npm run check:docs` | **PASS** — "Documentation check passed (17 required files)." |
|
|
157
|
-
| `npm run test:browser` | **PASS** — 137/137 tests, 13/13 files (real Chromium; run in full per task §37, not skipped) |
|
|
158
|
-
| `git diff --check` | **PASS** — no whitespace errors (only expected LF→CRLF notices) |
|
|
159
|
-
|
|
160
|
-
## 24. Built viewer Batch 3 smoke (task §38)
|
|
161
|
-
|
|
162
|
-
Fixture: one real observation (`smoke-rich-obs`, 800×600) built via a one-off script (not committed, `WORKFLOW_ROOT\tmp`) calling the actual compiled `dist/artifacts/artifactWriter.js` directly - a real, genuinely decodable PNG screenshot; `header`/`sidebar`/`footer` (resolved, geometrically arranged to produce a real `above` relationship) plus `missingWidget` (`not-found`) - under `WORKFLOW_ROOT\smoke\evidence-root`.
|
|
163
|
-
|
|
164
|
-
Command: `node dist/cli.js view --root "<WORKFLOW_ROOT>\smoke\evidence-root" --port 4319 --no-open`
|
|
165
|
-
|
|
166
|
-
All required checks passed against the real running built server:
|
|
167
|
-
|
|
168
|
-
- `/api/status` → `200`, correct root.
|
|
169
|
-
- `/api/index` → one `observation`/`supported` record with `logicalId: "smoke-rich-obs"`.
|
|
170
|
-
- `/api/artifacts/<handle>` → `200`, full artifact, all four configured targets present.
|
|
171
|
-
- `/api/media/<handle>/screenshot` → `200 image/png`, 2789 bytes (a real, non-trivial PNG, not a stub).
|
|
172
|
-
- `/api/observations/<handle>/relationships` → `200`; `unresolvedTargets` correctly lists `missingWidget` (`not-found`); 18 pairwise relationships; a real `header`-`above`-`footer` relationship confirmed present.
|
|
173
|
-
- PWA still loads (`/`, `/manifest.webmanifest`, `/sw.js` all `200`); `sw.js` contains no `api/observations` reference (not runtime-cached).
|
|
174
|
-
- `netstat` confirmed `127.0.0.1:4319` only, never `0.0.0.0`.
|
|
175
|
-
- Server located by real PID and terminated with `taskkill /F`; a follow-up `netstat` confirmed the port was released.
|
|
176
|
-
- A post-shutdown listing of the smoke evidence root shows exactly the two files the fixture script wrote (`manifest.json`, `screenshot.png`) - no stray writes, no modification.
|
|
177
|
-
|
|
178
|
-
**Result: PASS.** Logs retained under `WORKFLOW_ROOT\logs\` (`smoke-server.log`, `smoke-checks-1.log`, `smoke-checks-2.log`, `smoke-checks-3.log`, `index-response.json`, `artifact-response.json`, `relationships-response.json`).
|
|
179
|
-
|
|
180
|
-
## 25. Generated path inventory
|
|
181
|
-
|
|
182
|
-
| Path | Disposition |
|
|
183
|
-
|---|---|
|
|
184
|
-
| `WORKFLOW_ROOT\cache\npm` | Retained (npm cache from the my-dev-kit-index `npx` invocation) |
|
|
185
|
-
| `WORKFLOW_ROOT\tmp\vite-cache`, `WORKFLOW_ROOT\tmp\build-smoke-observation.mjs` | Retained (build cache empty again - Vite build mode doesn't populate it, same finding as Batches 1-2; the smoke-fixture script is dev/readiness tooling only, not committed) |
|
|
186
|
-
| `WORKFLOW_ROOT\logs\*` | Retained (smoke evidence, §24) |
|
|
187
|
-
| `WORKFLOW_ROOT\smoke\evidence-root` | Retained (real observation fixture tree built for the smoke test) |
|
|
188
|
-
| `WORKFLOW_ROOT\my-dev-kit-index\*` | Retained (successful index + cache-metadata, §5) |
|
|
189
|
-
| `WORKFLOW_ROOT\fixtures` | Retained, empty/unused (no committed deterministic repository fixture was needed - all Batch 3 fixtures are built programmatically via `tests/support/evidenceFixtures.ts`, matching the existing repository convention) |
|
|
190
|
-
| Repo-root `dist/` | Ordinary build output (gitignored); rebuilt cleanly by `scripts/clean.mjs` on every `npm run build` |
|
|
191
|
-
| Pre-existing repo-root `.my-dev-kit*`/`baselines`/`comparisons`/`contracts`/`evaluations`/`observations` | Pre-existing, empty, untouched (same finding as Batches 1-2) |
|
|
192
|
-
|
|
193
|
-
`WORKFLOW_ROOT`s for Batch 1 (`...\v0.8\batch-01`) and Batch 2 (`...\v0.8\batch-02`) were never targeted by any command in this session (verified) - preserved exactly as their own batches left them.
|
|
194
|
-
|
|
195
|
-
## 26. Repository pollution check
|
|
196
|
-
|
|
197
|
-
`git status --short` before staging showed only the 8 modified + 12 new Batch-3-owned paths listed in §18/§19. No unexpected file or directory appeared anywhere in the repository. No malformed sibling Batch 3 workflow path exists (§4).
|
|
198
|
-
|
|
199
|
-
## 27. Batch 1/2 regression check
|
|
200
|
-
|
|
201
|
-
**PASS.** All Batch 1 tests (`view` CLI, `127.0.0.1`/port `4319`, PWA shell/installability, cache boundary, server cleanup) and all Batch 2 tests (evidence indexing, unsupported-version handling, safe handles, on-demand artifact loading, media security, approved-reference image resolution, filesystem containment) pass unmodified except the two `viewerEvidenceShell.test.ts` updates described in §19, which preserve the exact invariants they originally protected while accounting for Batch 3's legitimate new observation-specific behavior.
|
|
202
|
-
|
|
203
|
-
## 28. v0.1-v0.7 regression check
|
|
204
|
-
|
|
205
|
-
**PASS.** Every pre-v0.8 unit and browser test suite (`observe`, `compare`, `approve-baseline`, `save-change-contract`, `evaluate-contract`, `import-reference`, `approve-reference`, `evaluate-reference-fidelity`) remains covered and passing - none was touched by this batch's diff. `src/domain/`, `src/browser/`, `src/artifacts/*Writer.ts`, and every existing reader are byte-for-byte unchanged.
|
|
206
|
-
|
|
207
|
-
## 29. Deviations
|
|
208
|
-
|
|
209
|
-
- None. Every task step (my-dev-kit retrieval, the coordinate audit, all required test categories, the full validation chain including `test:browser`, and the built-CLI smoke) was executed as specified.
|
|
210
|
-
|
|
211
|
-
## 30. Remaining uncovered risks
|
|
212
|
-
|
|
213
|
-
- **The relationship overlay draws one `<line>` per pairwise relationship *kind*, not one per target pair.** For a pair of targets that satisfy several relationship families simultaneously (common for axis-aligned rectangles - e.g. `horizontally-overlapping` + `above` + `does-not-overlap` all being true at once), multiple overlapping connector lines are drawn between the same two centers. This is not a fabrication (every line corresponds to a genuinely distinct canonical relationship record), but it is visually noisy for observations with several targets; a later batch could deduplicate by center-pair or use per-kind visual differentiation.
|
|
214
|
-
- **The observation inspector's completion/diagnostic display is a flat list**, not yet organized by diagnostic severity/target - acceptable for this batch's scope (task §23/§24 list required fields, not a required layout), but could be revisited alongside Batch 4+'s own inspector needs.
|
|
215
|
-
- Batch 1's and Batch 2's previously reported risks (install-prompt "available" branch untested in headless Chromium, `findImportedReferenceDir`'s per-request re-walk) remain unresolved and out of this batch's scope.
|
|
216
|
-
|
|
217
|
-
## 31. Out-of-scope confirmation
|
|
218
|
-
|
|
219
|
-
Confirmed absent from this batch's diff: before/after comparison UI, contract/change-scope visualization, external-reference visual inspection, side-by-side reference/candidate display, reference regions, reference/runtime binding, cross-reference selection, reference fidelity, independent visual-pane zoom/pan, synchronized lock, bounded-agent-context UI, source-correlation UI, arbitrary raw-file browsing, annotation, source editing, automatic target discovery, automatic binding, computer vision, pixel-diff scoring, image-to-code, cloud hosting, database, authentication, collaboration.
|
|
220
|
-
|
|
221
|
-
## 32. Final verdict
|
|
222
|
-
|
|
223
|
-
Batch 3 ("Runtime observation inspection and SVG overlays") is implemented and independently validated: a developer can select a supported observation through the existing Batch 2 data boundary, see its screenshot loaded on demand through the existing safe media endpoint, inspect stable runtime targets overlaid via SVG in the observation's own canonical coordinate domain (no evidence rewriting, verified against a genuine `devicePixelRatio≠1` case), select targets from either the list or the SVG with full bidirectional synchronization, inspect complete canonical target/observation evidence honestly (including genuinely unresolved targets, which never receive fabricated geometry), and inspect canonical layout-relationship evidence computed exclusively by the existing `deriveLayoutRelationships` engine - proven at the unit, real-HTTP, real-Chromium, and real-built-CLI-smoke levels. No Batch 4+ visualization, no v0.9 annotation, and no release/publication action was taken. Package version remains `0.7.0`.
|
|
1
|
+
# v0.8 Batch 3 — Runtime Observation Inspection and SVG Overlays — Implementation Report
|
|
2
|
+
|
|
3
|
+
## 1. Starting state
|
|
4
|
+
|
|
5
|
+
- Branch: `master`
|
|
6
|
+
- Starting HEAD: `be82055e29ca8d12cee61ae933dc85e6af3f2b9f` ("feat: add v0.8 viewer evidence indexing and readers")
|
|
7
|
+
- `origin/master` after `git fetch`: `a1de8ac01e1367b60021cb04226f56369fa2debb`
|
|
8
|
+
- `git rev-list --left-right --count origin/master...HEAD`: `0 2` — local is exactly Batch 1 + Batch 2 ahead of origin, no divergence.
|
|
9
|
+
- Starting `git status --short`: clean.
|
|
10
|
+
- Package version confirmed `0.7.0` throughout; never bumped.
|
|
11
|
+
|
|
12
|
+
## 2. Predecessor reports inspected
|
|
13
|
+
|
|
14
|
+
Read both local reports in full (not console summaries):
|
|
15
|
+
|
|
16
|
+
- `docs/reports/v0.8-viewer-runtime-pwa-batch1.md` — confirmed host `127.0.0.1`/port `4319`, `src/viewerServer/httpServer.ts`/`viewerService.ts` ownership, PWA `navigateFallbackDenylist: [/^\/api\//]`.
|
|
17
|
+
- `docs/reports/v0.8-evidence-index-readers-batch2.md` — confirmed exact module/API details reused unchanged this batch: `evidence/index.ts#loadArtifactByHandle`/`buildEvidenceIndexMetadata`, `evidence/handles.ts` (`<family-slug>:<percent-encoded relativeDir>`), `evidence/pathSafety.ts#resolveContainedDir` (including its Batch 2 forward-slash-root bugfix - re-used as-is, not re-litigated), `evidence/classify.ts#ClassifiedRecord`/`ViewerSupportState`, `evidence/projection.ts#EvidenceMetadataRecord`/`EvidenceArtifactDetail`, `GET /api/index`/`GET /api/artifacts/<handle>`/`GET /api/media/<handle>/<role>` exact semantics/status codes, `viewer/src/hooks/useEvidenceIndex.ts`/`useArtifactDetail.ts`, `viewer/src/components/EvidenceList.tsx`/`ArtifactPreview.tsx`. No parallel API was created; every Batch 3 addition is additive to this exact boundary.
|
|
18
|
+
|
|
19
|
+
## 3. Frozen planning authority inspected
|
|
20
|
+
|
|
21
|
+
`docs/DOCUMENTATION_PRESERVATION_POLICY.md`, `docs/PROJECT_MILESTONES.md` (Milestone 8), `docs/ROADMAP.md` (v0.8), `docs/plans/v0.8-implementation-plan.md` (Batch 3 section + cross-batch invariants §7) — all previously read in full during Batches 1-2, re-confirmed unchanged. `docs/ARCHITECTURE.md`, `docs/CONTRACTS.md` (media/schema-version constants, previously read in full), `docs/WORKFLOWS.md`, `docs/COMMANDS.md` (`view` section, then edited), `docs/DEVELOPMENT.md`. New this batch: `src/domain/schema.ts` (full `TargetGeometry`/`TargetEvidenceRecord`/`PageEvidence`-shape/`ScrollScenarioEvidence` read), `src/domain/relationships.ts` (`deriveLayoutRelationships` full signature/result type), `src/browser/evidenceCapture.ts` (`capturePageEvidence`/`captureResolvedTargetRecord` - the coordinate-audit source), `src/browser/chromiumAdapter.ts` (screenshot capture call, context creation), `src/artifacts/artifactReader.ts`, `src/viewerServer/httpServer.ts`, `src/viewerServer/evidence/*`, `viewer/src/*`, `tests/unit/relationshipDerivation.test.ts` (unresolved-target fixture pattern reused), `tests/unit/cliFrontendContracts.test.ts` (observation fixture pattern already reused since Batch 2).
|
|
22
|
+
|
|
23
|
+
Confirmed Batch 3's title/scope in `docs/plans/v0.8-implementation-plan.md` match the task exactly; no material difference found.
|
|
24
|
+
|
|
25
|
+
## 4. Prior-path audit (task §6)
|
|
26
|
+
|
|
27
|
+
Inspected `Z:\Users\newuser\Projects\`: exactly one sibling workflow directory exists, `my-frontend-observer.my-dev-kit-workflow` (correctly separated). No malformed duplicate (missing-separator variant) was found at any location - the two candidate paths given in the task text were in fact identical strings, so there was nothing to distinguish. Neither location was moved, deleted, or reused; Batch 3's own work used exclusively `...\my-dev-kit-workflow\v0.8\batch-03`.
|
|
28
|
+
|
|
29
|
+
## 5. WORKFLOW_ROOT and my-dev-kit retrieval
|
|
30
|
+
|
|
31
|
+
`WORKFLOW_ROOT` = `Z:\Users\newuser\Projects\my-frontend-observer.my-dev-kit-workflow\v0.8\batch-03`. Index built successfully:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
npx @dailephd/my-dev-kit@latest index --root . --src src --src tests --src viewer --out "<WORKFLOW_ROOT>\my-dev-kit-index" --call-graph --json
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
All seven required searches were run (observation geometry ownership, relationship ownership, scroll/visibility/overflow evidence, Batch 2 projection/API ownership, viewer React selection/data loading, screenshot coordinate semantics, plus the general index build). Results consistently pointed at the same files direct inspection then confirmed in detail (`src/domain/schema.ts`, `src/domain/relationships.ts`, `src/browser/evidenceCapture.ts`, `src/browser/chromiumAdapter.ts`, `src/viewerServer/evidence/*`) - no `lookup`/`source`/`slice` follow-up was needed since the searches were unambiguous and direct file reads (§3) established the exact contracts.
|
|
38
|
+
|
|
39
|
+
## 6. Coordinate audit (task §9 - the load-bearing decision)
|
|
40
|
+
|
|
41
|
+
Established from direct source inspection, not assumption:
|
|
42
|
+
|
|
43
|
+
- **Target geometry**: `src/browser/evidenceCapture.ts#captureResolvedTargetRecord` calls `el.getBoundingClientRect()` inside `handle.evaluate(...)`. This is viewport-relative CSS pixels (origin at the current viewport's top-left), captured from the same live page state the screenshot is taken from immediately after (same function/request lifecycle - `docs/ARCHITECTURE.md`'s existing "same live page/readiness state" invariant, unchanged by this batch).
|
|
44
|
+
- **Screenshot**: `src/browser/chromiumAdapter.ts` calls `page.screenshot({type:'png'})` with no `fullPage` option - Playwright's default is `fullPage: false`, capturing exactly the current viewport at the current scroll position.
|
|
45
|
+
- **Device scale factor**: `browser.newContext({viewport: {width, height}})` never sets `deviceScaleFactor` anywhere in this codebase, so Playwright's own default (`1`) applies to every observation this repository can currently produce. Consequence: the screenshot PNG's raw pixel dimensions equal `requestConfig.viewport.width × requestConfig.viewport.height` exactly - **1 CSS pixel = 1 PNG pixel** for every currently-producible observation.
|
|
46
|
+
- **`devicePixelRatio` disposition**: captured as `pageEvidence.devicePixelRatio` (`window.devicePixelRatio`, always `1` under the context above) - kept as **informational observation-level display evidence only**. It is never read by any Batch 3 geometry/coordinate code path (`grep`-verified: the only production reference to `devicePixelRatio` outside `evidenceCapture.ts` itself is the read-only display line in `ObservationInspector.tsx`).
|
|
47
|
+
- **Canonical viewport source**: `requestConfig.viewport` (`{width, height}`), a required, strongly-typed field validated on every `ObservationArtifact` (unlike the loosely-typed `pageEvidence: Record<string, EvidenceField<unknown>>` bag, which a hand-constructed test fixture could in principle omit fields from) - chosen as the authoritative SVG `viewBox` source for exactly this reason.
|
|
48
|
+
|
|
49
|
+
**Conclusion**: no coordinate rewriting is needed or performed. The SVG `viewBox` is set to `0 0 {requestConfig.viewport.width} {requestConfig.viewport.height}` - the exact frame `getBoundingClientRect()` already used - and the screenshot `<image>` fills that same viewBox (`preserveAspectRatio="none"`, safe here specifically because the two frames are pixel-identical per the above). Target rectangles use `geometry.x/y/width/height` completely unchanged. This is also robust against a hypothetical future capture path using a different `deviceScaleFactor`, because the `<image>`/viewBox scaling is browser-native presentation behavior, never a manual multiplication in this codebase - satisfying task §9's explicit preference for a `viewBox`-based presentation mapping over evidence rewriting. No `BLOCKED_V08_BATCH3_COORDINATE_MAPPING_INADEQUATE` condition was found.
|
|
50
|
+
|
|
51
|
+
## 7. Observation-view projection architecture
|
|
52
|
+
|
|
53
|
+
Deliberately **not** a new server-side artifact-detail endpoint. The existing Batch 2 `GET /api/artifacts/<handle>` already returns the full, already-validated `ObservationArtifact` (targetEvidence, pageEvidence, requestConfig, screenshot field, completion, diagnostics, provenance, browser) unchanged - everything the target/observation inspector needs. Batch 3's "projection" is therefore two things:
|
|
54
|
+
|
|
55
|
+
1. **One additive server-side computation** (`src/viewerServer/evidence/observationView.ts#getObservationRelationships`, exposed as `GET /api/observations/<handle>/relationships`) - the only genuinely new derivation this batch performs, and it is a thin, defense-in-depth-wrapped pass-through to the existing canonical `deriveLayoutRelationships` (§8). Ephemeral: computed fresh per request, never persisted, never a new artifact family.
|
|
56
|
+
2. **Client-side presentation reshaping only** (`viewer/src/observation/targetOrder.ts#orderedTargets`) - selects/orders already-fetched fields (configured-target order, `hasGeometry` flag), explicitly not evidence derivation (frozen plan §27 permits "selection; filtering; formatting").
|
|
57
|
+
|
|
58
|
+
Both are ephemeral, in-memory, per-request/per-render, and derived only from already-validated canonical domain values and canonical relationship-engine results - no `viewer.json`, no new artifact family, no mutation of `ObservationArtifact`.
|
|
59
|
+
|
|
60
|
+
## 8. Canonical relationship-engine reuse
|
|
61
|
+
|
|
62
|
+
`src/viewerServer/evidence/observationView.ts` calls `deriveLayoutRelationships` (`src/domain/relationships.ts`) exactly once, unchanged, against the already-classified, already-validated `ObservationArtifact` - the same function `docs/ARCHITECTURE.md`'s v0.4 architecture section documents as "the one canonical pure derivation." No relationship predicate (overlap, relative width, ordering, containment, fit, sequencing, page-width fit) is reimplemented anywhere in `src/viewerServer/` or `viewer/src/`. Proven by an exact-equality test (`tests/unit/observationRelationshipsServer.test.ts` "returns exactly the canonical `deriveLayoutRelationships(...)` result... never a second computation") that independently re-derives the same graph directly from the persisted artifact and asserts `toEqual` against the HTTP response body.
|
|
63
|
+
|
|
64
|
+
## 9. Screenshot loading path
|
|
65
|
+
|
|
66
|
+
Unchanged Batch 2 mechanism: `viewer/src/components/TargetOverlaySvg.tsx`'s `<image href={`/api/media/${handle}/screenshot`}>` - the same handle already used for `/api/artifacts/<handle>`, the same `screenshot` media role Batch 2 already implemented (`mediaResolver.ts`, unmodified). No new media role, no local path, no `file://`, no base64 embedding, no new viewer-owned copy of the bytes. Loaded only when the observation's visual workspace actually renders (i.e., only after a user selects a supported `observation` record) - never eagerly for the whole index.
|
|
67
|
+
|
|
68
|
+
## 10. SVG component/layout
|
|
69
|
+
|
|
70
|
+
`viewer/src/components/TargetOverlaySvg.tsx`: root `<svg viewBox="0 0 {w} {h}">` (§6); one `<image>` filling it; one `<rect>` per target with usable geometry (`role="button"`, keyboard-operable, `data-target-name` for identity); one `<text>` label per rectangle when the labels toggle is on; one `<line>` per pairwise relationship whose both endpoints have geometry (drawn between rectangle centers), only when the relationships toggle is on. `viewer/src/components/{TargetList,ObservationInspector,EvidenceFieldView,ObservationWorkspace}.tsx` provide the surrounding three-pane layout (Targets | Screenshot+SVG | Inspector) nested inside the existing Batch 1 `app-shell__workspace` region - the outer nav/details shell structure is untouched.
|
|
71
|
+
|
|
72
|
+
## 11. Target selection architecture
|
|
73
|
+
|
|
74
|
+
`viewer/src/components/ObservationWorkspace.tsx` holds `selected: string | undefined` (React `useState`, presentation-only, reset via a `useEffect` keyed on `handle` whenever a different observation is selected - task §17's "changing observation resets or safely rebinds selection" requirement). Both the target-list `<button>` and the SVG `<rect>` call the same `onSelect(name)` callback using the target's existing stable `name`; both re-render their own `aria-pressed`/selected-class state from the single shared `selected` value, and the inspector reads the same value - genuine bidirectional synchronization through one source of truth, not two parallel selection states. Proven in real Chromium (`tests/browser/observationSvgWorkspace.test.ts`, two dedicated tests: SVG→list/inspector and list→SVG).
|
|
75
|
+
|
|
76
|
+
## 12. Unresolved-target behavior
|
|
77
|
+
|
|
78
|
+
`orderedTargets()`'s `hasGeometry` is `true` only when `geometry.state` is `'available'` or `'partial'` - never merely because the target is configured. `TargetOverlaySvg` skips rendering entirely for any target without usable geometry (`geometryOf()` returns `undefined`, the `.map` callback returns `null`). No `x=0 y=0 width=0 height=0` fallback exists anywhere in the code. The target remains fully selectable from `TargetList` (shown with its real resolution status, e.g. `not-found (no geometry)`) and its canonical resolution/reason evidence is shown honestly in `ObservationInspector`. Proven with a real fixture (`missingWidget`, `not-found`) at the unit, server, and real-Chromium levels (`tests/unit/observationCoordinateMapping.test.ts`, `observationRelationshipsServer.test.ts`, `tests/browser/observationSvgWorkspace.test.ts`).
|
|
79
|
+
|
|
80
|
+
## 13. Overlay controls
|
|
81
|
+
|
|
82
|
+
Three independently toggleable controls (`ObservationWorkspace`'s `OverlayToggles` state): **geometry** (target rectangles), **labels** (target-name text, disabled/hidden when geometry is off, since a label with no rectangle would be presentation-meaningless), **relationships** (connector lines). Toggling never refetches or alters the underlying artifact/graph - purely a render-time filter over already-fetched data.
|
|
83
|
+
|
|
84
|
+
## 14. Visibility presentation
|
|
85
|
+
|
|
86
|
+
Not a separate overlay category in this batch: `TargetVisibility.visible` (existing canonical evidence, `derived` source) is shown honestly in the target inspector (`EvidenceFieldView` on `record.visibility`) exactly as the artifact states it - never inferred from screenshot pixels or from the mere existence of a rectangle. No new visibility threshold was introduced.
|
|
87
|
+
|
|
88
|
+
## 15. Overflow presentation
|
|
89
|
+
|
|
90
|
+
`record.style` (display/position/overflow-x/overflow-y, existing canonical `computed-browser` evidence) and `record.layout` (scroll/client dimensions, existing canonical `browser` evidence) are shown honestly in the target inspector via `EvidenceFieldView`, unmodified and unreinterpreted. No SVG rectangle-intersection-based overflow inference exists anywhere in this batch.
|
|
91
|
+
|
|
92
|
+
## 16. Scroll presentation
|
|
93
|
+
|
|
94
|
+
`ObservationInspector` distinguishes, using only existing canonical evidence: no scroll scenario configured (`artifact.scrollScenarioEvidence === undefined` - shown as an honest "No scroll scenario was configured for this observation" note) vs. available evidence (`initial`/`final`/`transition`/`scrollOwner`, shown via the existing `ScrollScenarioTransition`/`ScrollOwnerInterpretation` fields, `EvidenceFieldView` honoring the field's own `EvidenceField` state for `scrollOwner`). No browser action is re-run from the viewer; no historical trajectory is animated; no movement arrow is fabricated - the presentation is textual/status-only, exactly matching task §22's "if a faithful spatial overlay is not possible, show scroll evidence in the inspector/status layer."
|
|
95
|
+
|
|
96
|
+
## 17. Inspector evidence exposed
|
|
97
|
+
|
|
98
|
+
**Target inspector**: resolution status, tag, semantics (role/name), semantic state, landmark, geometry, style, layout metrics, visibility, containment - each via `EvidenceFieldView`, which renders `available`/`partial` (with source and, for partial, the reason), `unavailable` (with reason), and `not-applicable` (with optional reason) distinctly - never a fabricated empty string or zero for missing evidence. Plus the relationships involving that target.
|
|
99
|
+
|
|
100
|
+
**Observation inspector**: observation id, request id, schema version, producer, viewport, page title, requested/final URL, device pixel ratio, document width/height, window scroll X/Y, completion state, diagnostics, scroll-scenario evidence (or its honest absence), and the full relationship list.
|
|
101
|
+
|
|
102
|
+
## 18. Files created
|
|
103
|
+
|
|
104
|
+
- `src/viewerServer/evidence/observationView.ts`
|
|
105
|
+
- `viewer/src/types/observation.ts` (type-only re-exports of canonical Node domain types - erased at build time, no runtime coupling)
|
|
106
|
+
- `viewer/src/observation/targetOrder.ts`
|
|
107
|
+
- `viewer/src/hooks/useObservationRelationships.ts`
|
|
108
|
+
- `viewer/src/components/TargetOverlaySvg.tsx`, `TargetList.tsx`, `ObservationInspector.tsx`, `ObservationWorkspace.tsx`, `EvidenceFieldView.tsx`
|
|
109
|
+
- `tests/unit/observationCoordinateMapping.test.ts`, `observationRelationshipsServer.test.ts`, `observationFixtureSanity.test.ts`
|
|
110
|
+
- `tests/browser/observationSvgWorkspace.test.ts`
|
|
111
|
+
- `docs/reports/v0.8-observation-svg-inspection-batch3.md` (this file)
|
|
112
|
+
|
|
113
|
+
## 19. Files modified
|
|
114
|
+
|
|
115
|
+
- `src/viewerServer/httpServer.ts` - added `GET /api/observations/<handle>/relationships` routing (405 for write methods, 404 unknown handle, 409 not-an-observation/not-currently-loadable/derivation-failed, 200 with the graph); `/api/status`, `/api/index`, `/api/artifacts/<handle>`, `/api/media/<handle>/<role>`, and static-asset serving byte-for-byte unchanged.
|
|
116
|
+
- `tests/support/evidenceFixtures.ts` - added `realisticPageEvidence`, `unresolvedTarget`, `buildRealPng` (a genuinely decodable solid-color PNG, distinct from the existing header-only `buildMinimalPng`), `writeRichObservationFixture`; extended `buildObservation` with optional `pageEvidence`/`viewport` parameters (backward compatible - existing call sites unaffected, default values unchanged).
|
|
117
|
+
- `viewer/src/components/ArtifactPreview.tsx` - branches to `ObservationWorkspace` for `family === 'observation'`; every other family's raw-JSON preview is unchanged.
|
|
118
|
+
- `viewer/src/styles/index.css` - additive rules for the new workspace/list/SVG/inspector UI.
|
|
119
|
+
- `tests/unit/viewerPwaBuild.test.ts` - added the explicit Batch 3 cache-boundary assertion (§20).
|
|
120
|
+
- `tests/browser/viewerEvidenceShell.test.ts` - two pre-existing Batch 2 assertions that selected an `observation` record to test the *generic* raw-JSON on-demand-load path and the *generic* no-visualization invariant now legitimately conflict with Batch 3's real observation workspace; both were repointed to a `comparison` record (which still exercises exactly the generic path/invariant they were written to protect) rather than weakened - the same kind of sanctioned evolution as Batch 1→2's placeholder-text update and Batch 2's `TST-401`.
|
|
121
|
+
- `docs/ARCHITECTURE.md`, `docs/COMMANDS.md` - new/updated Batch 3 sections (§6-§17 above, condensed).
|
|
122
|
+
|
|
123
|
+
## 20. PWA cache verification
|
|
124
|
+
|
|
125
|
+
No `vite.config.ts`/service-worker change needed: `GET /api/observations/<handle>/relationships` lives under the already-denylisted `/api/` prefix. Extended `tests/unit/viewerPwaBuild.test.ts` with an explicit assertion against the real built `sw.js`: still exactly one `registerRoute` call, and the precache manifest contains no `/api/observations` entry.
|
|
126
|
+
|
|
127
|
+
## 21. Tests added/modified and behavior protected
|
|
128
|
+
|
|
129
|
+
| Test file | Level | Protects |
|
|
130
|
+
|---|---|---|
|
|
131
|
+
| `observationFixtureSanity.test.ts` | unit | The new `buildRealPng` generator produces a genuinely decodable PNG at exact viewport pixel dimensions (underpins every browser-level assertion below). |
|
|
132
|
+
| `observationCoordinateMapping.test.ts` | unit | `requestConfig.viewport` presence/fidelity; geometry preserved exactly (fractional values, no rounding); `devicePixelRatio≠1` never multiplies geometry; `partial` geometry state preserved (never promoted/discarded); geometry partly outside the viewport preserved unclamped; relationship engine never includes the unresolved target. |
|
|
133
|
+
| `observationRelationshipsServer.test.ts` | unit/integration (real HTTP) | Server relationship route is byte-for-byte identical to a direct `deriveLayoutRelationships` call (proves no second engine); unresolved target excluded from pairwise relationships; a real geometric relationship (`header above footer`) is produced; non-observation handle → 409; unknown handle → 404; unsupported-version observation → 409; write methods → 405. |
|
|
134
|
+
| `viewerPwaBuild.test.ts` (+1) | build/integration | New route covered by the existing `/api/` denylist, no new runtime-caching rule. |
|
|
135
|
+
| `viewerEvidenceShell.test.ts` (2 updated) | browser | Generic Batch 2 on-demand-load/no-visualization invariants still hold for non-observation families after Batch 3. |
|
|
136
|
+
| `observationSvgWorkspace.test.ts` | browser (real Chromium, real persisted evidence) | Full real-browser proof - see §22. |
|
|
137
|
+
|
|
138
|
+
## 22. Real-browser proof (task §33)
|
|
139
|
+
|
|
140
|
+
`tests/browser/observationSvgWorkspace.test.ts`, against the actual built `dist/viewer` PWA, the actual built loopback server, and one real observation (`writeRichObservationFixture`: real writer, genuinely decodable 800×600 PNG screenshot, two geometrically resolved targets plus one genuine `not-found` target). Five tests, all passing:
|
|
141
|
+
|
|
142
|
+
1. Screenshot visibly loads (`<image href>` resolves to a real `/api/media/observation:...` URL); resolved targets (`header`, `sidebar`) have real `<rect>` elements whose `x`/`y`/`width`/`height` attributes exactly match the fixture's canonical geometry (`0,0,800,80` and `0,100,200,400`); the unresolved target (`missingWidget`) receives **no** `<rect>` at all and is shown honestly (`not-found`) in the target list.
|
|
143
|
+
2. Selecting the SVG `<rect>` for `header` sets `aria-pressed="true"` on both the rect and its list entry, and the inspector shows `Target: header` with matching geometry text (`x:0 y:0 w:800 h:80`).
|
|
144
|
+
3. Selecting `sidebar` from the target list sets `aria-pressed="true"` on the corresponding SVG rect and updates the inspector - the reverse-direction synchronization.
|
|
145
|
+
4. The relationships panel shows the real canonical `above` relationship between `header`/`footer` labeled "Derived evidence", and at least one connector `<line>` is actually drawn between resolved targets.
|
|
146
|
+
5. After `page.setViewportSize` changes (from 1000×800 to 1400×900), the rendered SVG element's bounding-box aspect ratio remains within 0.05 of the canonical 800:600 (4:3) ratio - screenshot and overlay scale together, proving the `viewBox` mapping holds under resize.
|
|
147
|
+
|
|
148
|
+
## 23. Validation results
|
|
149
|
+
|
|
150
|
+
| Command | Result |
|
|
151
|
+
|---|---|
|
|
152
|
+
| `npm run typecheck` | **PASS** (zero errors, both `tsconfig.json` and `viewer/tsconfig.json`) |
|
|
153
|
+
| `npm run lint` | **PASS** (zero errors/warnings) |
|
|
154
|
+
| `npm test` | **PASS** — 1085/1085 tests, 59/59 files |
|
|
155
|
+
| `npm run build` | **PASS** — unchanged Node/library output plus `dist/viewerServer/evidence/observationView.js` and the rebuilt `dist/viewer/**` PWA |
|
|
156
|
+
| `npm run check:docs` | **PASS** — "Documentation check passed (17 required files)." |
|
|
157
|
+
| `npm run test:browser` | **PASS** — 137/137 tests, 13/13 files (real Chromium; run in full per task §37, not skipped) |
|
|
158
|
+
| `git diff --check` | **PASS** — no whitespace errors (only expected LF→CRLF notices) |
|
|
159
|
+
|
|
160
|
+
## 24. Built viewer Batch 3 smoke (task §38)
|
|
161
|
+
|
|
162
|
+
Fixture: one real observation (`smoke-rich-obs`, 800×600) built via a one-off script (not committed, `WORKFLOW_ROOT\tmp`) calling the actual compiled `dist/artifacts/artifactWriter.js` directly - a real, genuinely decodable PNG screenshot; `header`/`sidebar`/`footer` (resolved, geometrically arranged to produce a real `above` relationship) plus `missingWidget` (`not-found`) - under `WORKFLOW_ROOT\smoke\evidence-root`.
|
|
163
|
+
|
|
164
|
+
Command: `node dist/cli.js view --root "<WORKFLOW_ROOT>\smoke\evidence-root" --port 4319 --no-open`
|
|
165
|
+
|
|
166
|
+
All required checks passed against the real running built server:
|
|
167
|
+
|
|
168
|
+
- `/api/status` → `200`, correct root.
|
|
169
|
+
- `/api/index` → one `observation`/`supported` record with `logicalId: "smoke-rich-obs"`.
|
|
170
|
+
- `/api/artifacts/<handle>` → `200`, full artifact, all four configured targets present.
|
|
171
|
+
- `/api/media/<handle>/screenshot` → `200 image/png`, 2789 bytes (a real, non-trivial PNG, not a stub).
|
|
172
|
+
- `/api/observations/<handle>/relationships` → `200`; `unresolvedTargets` correctly lists `missingWidget` (`not-found`); 18 pairwise relationships; a real `header`-`above`-`footer` relationship confirmed present.
|
|
173
|
+
- PWA still loads (`/`, `/manifest.webmanifest`, `/sw.js` all `200`); `sw.js` contains no `api/observations` reference (not runtime-cached).
|
|
174
|
+
- `netstat` confirmed `127.0.0.1:4319` only, never `0.0.0.0`.
|
|
175
|
+
- Server located by real PID and terminated with `taskkill /F`; a follow-up `netstat` confirmed the port was released.
|
|
176
|
+
- A post-shutdown listing of the smoke evidence root shows exactly the two files the fixture script wrote (`manifest.json`, `screenshot.png`) - no stray writes, no modification.
|
|
177
|
+
|
|
178
|
+
**Result: PASS.** Logs retained under `WORKFLOW_ROOT\logs\` (`smoke-server.log`, `smoke-checks-1.log`, `smoke-checks-2.log`, `smoke-checks-3.log`, `index-response.json`, `artifact-response.json`, `relationships-response.json`).
|
|
179
|
+
|
|
180
|
+
## 25. Generated path inventory
|
|
181
|
+
|
|
182
|
+
| Path | Disposition |
|
|
183
|
+
|---|---|
|
|
184
|
+
| `WORKFLOW_ROOT\cache\npm` | Retained (npm cache from the my-dev-kit-index `npx` invocation) |
|
|
185
|
+
| `WORKFLOW_ROOT\tmp\vite-cache`, `WORKFLOW_ROOT\tmp\build-smoke-observation.mjs` | Retained (build cache empty again - Vite build mode doesn't populate it, same finding as Batches 1-2; the smoke-fixture script is dev/readiness tooling only, not committed) |
|
|
186
|
+
| `WORKFLOW_ROOT\logs\*` | Retained (smoke evidence, §24) |
|
|
187
|
+
| `WORKFLOW_ROOT\smoke\evidence-root` | Retained (real observation fixture tree built for the smoke test) |
|
|
188
|
+
| `WORKFLOW_ROOT\my-dev-kit-index\*` | Retained (successful index + cache-metadata, §5) |
|
|
189
|
+
| `WORKFLOW_ROOT\fixtures` | Retained, empty/unused (no committed deterministic repository fixture was needed - all Batch 3 fixtures are built programmatically via `tests/support/evidenceFixtures.ts`, matching the existing repository convention) |
|
|
190
|
+
| Repo-root `dist/` | Ordinary build output (gitignored); rebuilt cleanly by `scripts/clean.mjs` on every `npm run build` |
|
|
191
|
+
| Pre-existing repo-root `.my-dev-kit*`/`baselines`/`comparisons`/`contracts`/`evaluations`/`observations` | Pre-existing, empty, untouched (same finding as Batches 1-2) |
|
|
192
|
+
|
|
193
|
+
`WORKFLOW_ROOT`s for Batch 1 (`...\v0.8\batch-01`) and Batch 2 (`...\v0.8\batch-02`) were never targeted by any command in this session (verified) - preserved exactly as their own batches left them.
|
|
194
|
+
|
|
195
|
+
## 26. Repository pollution check
|
|
196
|
+
|
|
197
|
+
`git status --short` before staging showed only the 8 modified + 12 new Batch-3-owned paths listed in §18/§19. No unexpected file or directory appeared anywhere in the repository. No malformed sibling Batch 3 workflow path exists (§4).
|
|
198
|
+
|
|
199
|
+
## 27. Batch 1/2 regression check
|
|
200
|
+
|
|
201
|
+
**PASS.** All Batch 1 tests (`view` CLI, `127.0.0.1`/port `4319`, PWA shell/installability, cache boundary, server cleanup) and all Batch 2 tests (evidence indexing, unsupported-version handling, safe handles, on-demand artifact loading, media security, approved-reference image resolution, filesystem containment) pass unmodified except the two `viewerEvidenceShell.test.ts` updates described in §19, which preserve the exact invariants they originally protected while accounting for Batch 3's legitimate new observation-specific behavior.
|
|
202
|
+
|
|
203
|
+
## 28. v0.1-v0.7 regression check
|
|
204
|
+
|
|
205
|
+
**PASS.** Every pre-v0.8 unit and browser test suite (`observe`, `compare`, `approve-baseline`, `save-change-contract`, `evaluate-contract`, `import-reference`, `approve-reference`, `evaluate-reference-fidelity`) remains covered and passing - none was touched by this batch's diff. `src/domain/`, `src/browser/`, `src/artifacts/*Writer.ts`, and every existing reader are byte-for-byte unchanged.
|
|
206
|
+
|
|
207
|
+
## 29. Deviations
|
|
208
|
+
|
|
209
|
+
- None. Every task step (my-dev-kit retrieval, the coordinate audit, all required test categories, the full validation chain including `test:browser`, and the built-CLI smoke) was executed as specified.
|
|
210
|
+
|
|
211
|
+
## 30. Remaining uncovered risks
|
|
212
|
+
|
|
213
|
+
- **The relationship overlay draws one `<line>` per pairwise relationship *kind*, not one per target pair.** For a pair of targets that satisfy several relationship families simultaneously (common for axis-aligned rectangles - e.g. `horizontally-overlapping` + `above` + `does-not-overlap` all being true at once), multiple overlapping connector lines are drawn between the same two centers. This is not a fabrication (every line corresponds to a genuinely distinct canonical relationship record), but it is visually noisy for observations with several targets; a later batch could deduplicate by center-pair or use per-kind visual differentiation.
|
|
214
|
+
- **The observation inspector's completion/diagnostic display is a flat list**, not yet organized by diagnostic severity/target - acceptable for this batch's scope (task §23/§24 list required fields, not a required layout), but could be revisited alongside Batch 4+'s own inspector needs.
|
|
215
|
+
- Batch 1's and Batch 2's previously reported risks (install-prompt "available" branch untested in headless Chromium, `findImportedReferenceDir`'s per-request re-walk) remain unresolved and out of this batch's scope.
|
|
216
|
+
|
|
217
|
+
## 31. Out-of-scope confirmation
|
|
218
|
+
|
|
219
|
+
Confirmed absent from this batch's diff: before/after comparison UI, contract/change-scope visualization, external-reference visual inspection, side-by-side reference/candidate display, reference regions, reference/runtime binding, cross-reference selection, reference fidelity, independent visual-pane zoom/pan, synchronized lock, bounded-agent-context UI, source-correlation UI, arbitrary raw-file browsing, annotation, source editing, automatic target discovery, automatic binding, computer vision, pixel-diff scoring, image-to-code, cloud hosting, database, authentication, collaboration.
|
|
220
|
+
|
|
221
|
+
## 32. Final verdict
|
|
222
|
+
|
|
223
|
+
Batch 3 ("Runtime observation inspection and SVG overlays") is implemented and independently validated: a developer can select a supported observation through the existing Batch 2 data boundary, see its screenshot loaded on demand through the existing safe media endpoint, inspect stable runtime targets overlaid via SVG in the observation's own canonical coordinate domain (no evidence rewriting, verified against a genuine `devicePixelRatio≠1` case), select targets from either the list or the SVG with full bidirectional synchronization, inspect complete canonical target/observation evidence honestly (including genuinely unresolved targets, which never receive fabricated geometry), and inspect canonical layout-relationship evidence computed exclusively by the existing `deriveLayoutRelationships` engine - proven at the unit, real-HTTP, real-Chromium, and real-built-CLI-smoke levels. No Batch 4+ visualization, no v0.9 annotation, and no release/publication action was taken. Package version remains `0.7.0`.
|