@dailephd/my-frontend-observer 0.9.1 → 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 -471
- package/LICENSE +21 -21
- package/README.md +375 -357
- 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/application/visualChangeAgentHandoffService.d.ts +28 -0
- package/dist/application/visualChangeAgentHandoffService.js +111 -0
- package/dist/application/visualChangeAgentHandoffService.js.map +1 -0
- package/dist/application/visualChangeProjectWorkflowService.d.ts +95 -0
- package/dist/application/visualChangeProjectWorkflowService.js +376 -0
- package/dist/application/visualChangeProjectWorkflowService.js.map +1 -0
- package/dist/application/visualChangeReviewService.d.ts +50 -0
- package/dist/application/visualChangeReviewService.js +69 -0
- package/dist/application/visualChangeReviewService.js.map +1 -0
- package/dist/application/visualChangeWorkflowPersistenceService.d.ts +26 -0
- package/dist/application/visualChangeWorkflowPersistenceService.js +15 -0
- package/dist/application/visualChangeWorkflowPersistenceService.js.map +1 -0
- package/dist/artifacts/visualChangeWorkflowArtifactReader.d.ts +9 -0
- package/dist/artifacts/visualChangeWorkflowArtifactReader.js +47 -0
- package/dist/artifacts/visualChangeWorkflowArtifactReader.js.map +1 -0
- package/dist/artifacts/visualChangeWorkflowArtifactWriter.d.ts +20 -0
- package/dist/artifacts/visualChangeWorkflowArtifactWriter.js +41 -0
- package/dist/artifacts/visualChangeWorkflowArtifactWriter.js.map +1 -0
- package/dist/cli.js +9 -7
- package/dist/cli.js.map +1 -1
- package/dist/domain/visualChangeAgentHandoff.d.ts +82 -0
- package/dist/domain/visualChangeAgentHandoff.js +80 -0
- package/dist/domain/visualChangeAgentHandoff.js.map +1 -0
- package/dist/domain/visualChangeAgentHandoffSerialization.d.ts +2 -0
- package/dist/domain/visualChangeAgentHandoffSerialization.js +11 -0
- package/dist/domain/visualChangeAgentHandoffSerialization.js.map +1 -0
- package/dist/domain/visualChangeCycle.d.ts +8 -0
- package/dist/domain/visualChangeCycle.js +7 -0
- package/dist/domain/visualChangeCycle.js.map +1 -0
- package/dist/domain/visualChangeWorkflow.d.ts +125 -0
- package/dist/domain/visualChangeWorkflow.js +109 -0
- package/dist/domain/visualChangeWorkflow.js.map +1 -0
- package/dist/domain/visualChangeWorkflowIdentity.d.ts +5 -0
- package/dist/domain/visualChangeWorkflowIdentity.js +24 -0
- package/dist/domain/visualChangeWorkflowIdentity.js.map +1 -0
- package/dist/index.d.ts +21 -1
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/projectWorkflow/projectPaths.d.ts +3 -0
- package/dist/projectWorkflow/projectPaths.js +7 -0
- package/dist/projectWorkflow/projectPaths.js.map +1 -1
- package/dist/viewer/assets/index-DglJ6f28.css +1 -0
- package/dist/viewer/assets/index-DsODREY5.js +9 -0
- package/dist/viewer/index.html +15 -15
- package/dist/viewer/sw.js +1 -1
- package/dist/viewerServer/evidence/classify.d.ts +3 -1
- package/dist/viewerServer/evidence/classify.js +10 -0
- package/dist/viewerServer/evidence/classify.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/projection.d.ts +5 -0
- package/dist/viewerServer/evidence/projection.js +19 -0
- package/dist/viewerServer/evidence/projection.js.map +1 -1
- package/dist/viewerServer/evidence/visualChangeWorkflowView.d.ts +31 -0
- package/dist/viewerServer/evidence/visualChangeWorkflowView.js +36 -0
- package/dist/viewerServer/evidence/visualChangeWorkflowView.js.map +1 -0
- package/dist/viewerServer/httpServer.js +323 -1
- package/dist/viewerServer/httpServer.js.map +1 -1
- package/dist/viewerServer/referenceApproval.d.ts +22 -0
- package/dist/viewerServer/referenceApproval.js +42 -0
- package/dist/viewerServer/referenceApproval.js.map +1 -0
- package/dist/viewerServer/referenceVisualChangeAuthoring.d.ts +28 -0
- package/dist/viewerServer/referenceVisualChangeAuthoring.js +134 -0
- package/dist/viewerServer/referenceVisualChangeAuthoring.js.map +1 -0
- package/dist/viewerServer/runtimeVisualChangeAuthoring.d.ts +33 -0
- package/dist/viewerServer/runtimeVisualChangeAuthoring.js +81 -0
- package/dist/viewerServer/runtimeVisualChangeAuthoring.js.map +1 -0
- package/dist/viewerServer/visualChangeAuthoring.d.ts +46 -0
- package/dist/viewerServer/visualChangeAuthoring.js +63 -0
- package/dist/viewerServer/visualChangeAuthoring.js.map +1 -0
- package/dist/viewerServer/visualChangeHandoff.d.ts +23 -0
- package/dist/viewerServer/visualChangeHandoff.js +31 -0
- package/dist/viewerServer/visualChangeHandoff.js.map +1 -0
- package/dist/viewerServer/visualChangeReview.d.ts +30 -0
- package/dist/viewerServer/visualChangeReview.js +46 -0
- package/dist/viewerServer/visualChangeReview.js.map +1 -0
- package/docs/ARCHITECTURE.md +1394 -1373
- package/docs/CI_CD.md +349 -327
- package/docs/COMMANDS.md +1035 -1012
- package/docs/CONTRACTS.md +1971 -1926
- package/docs/CURRENT_STATE.md +1277 -1238
- 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 -191
- package/docs/QUICKSTART.md +100 -96
- package/docs/RELEASE.md +37 -33
- package/docs/ROADMAP.md +1105 -1033
- package/docs/SECURITY.md +297 -275
- package/docs/WORKFLOWS.md +806 -770
- package/docs/plans/v0.10-implementation-plan.md +1509 -0
- 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 -0
- package/docs/reports/v0.10-batch2-project-composition-check-recording.md +103 -0
- package/docs/reports/v0.10-batch3-viewer-visual-change-workspace.md +93 -0
- package/docs/reports/v0.10-batch4-actual-frontend-entry.md +59 -0
- package/docs/reports/v0.10-batch5-reference-driven-entry.md +238 -0
- package/docs/reports/v0.10-batch6-coding-agent-handoff.md +85 -0
- package/docs/reports/v0.10-batch7-correction-review-acceptance.md +145 -0
- package/docs/reports/v0.10-batch8-integrated-acceptance.md +109 -0
- package/docs/reports/v0.10-implementation-completeness-documentation-reconciliation.md +344 -0
- package/docs/reports/v0.10-pre-release-readiness.md +120 -0
- package/docs/reports/v0.10-release-preparation.md +70 -0
- 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
- package/dist/viewer/assets/index-BN41MI7m.css +0 -1
- package/dist/viewer/assets/index-CkKXnlrI.js +0 -9
|
@@ -1,279 +1,279 @@
|
|
|
1
|
-
# v0.8 Batch 6 — Explicit-Binding Interaction, Zoom/Pan, Conditional Lock, and On-Demand Reference Fidelity — Implementation Report
|
|
2
|
-
|
|
3
|
-
## 1. Starting state
|
|
4
|
-
|
|
5
|
-
- Branch: `master`
|
|
6
|
-
- Starting HEAD: `a5871aeeca54ebf33a985a82078c365a173d6717` ("feat: add v0.8 reference and candidate inspection", Batch 5)
|
|
7
|
-
- `origin/master` after `git fetch`: `a1de8ac01e1367b60021cb04226f56369fa2debb`
|
|
8
|
-
- `git rev-list --left-right --count origin/master...HEAD`: `0 5`
|
|
9
|
-
- `git merge-base --is-ancestor origin/master HEAD` → succeeded: origin/master is a strict ancestor of local HEAD, no divergence. No pull/rebase/merge/reset performed.
|
|
10
|
-
- Starting `git status --short`: clean.
|
|
11
|
-
- Package version confirmed `0.7.0` throughout; never bumped.
|
|
12
|
-
|
|
13
|
-
## 2. Same recurring path contradiction, resolved the same way
|
|
14
|
-
|
|
15
|
-
Task §5 repeated the identical `Join-Path`-vs-restated-sentence contradiction present in Batches 4 and 5. Re-ran the literal algorithm and confirmed the containment assertion passes only for the inside-repository path:
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
REPO_ROOT=Z:\Users\newuser\Projects\my-frontend-observer
|
|
19
|
-
WORKFLOW_ROOT=Z:\Users\newuser\Projects\my-frontend-observer\.my-dev-kit-workflow\v0.8\batch-06
|
|
20
|
-
ContainmentCheck=True
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Used **`Z:\Users\newuser\Projects\my-frontend-observer\.my-dev-kit-workflow\v0.8\batch-06`**, per the same precedent recorded in the Batch 4/5 reports.
|
|
24
|
-
|
|
25
|
-
## 3. Prior workflow-root audit
|
|
26
|
-
|
|
27
|
-
Sibling `...my-frontend-observer.my-dev-kit-workflow\v0.8\` holds only `batch-01/02/03` (untouched). Inside-repo `.my-dev-kit-workflow\v0.8\` held `batch-04`/`batch-05` (untouched) before this batch added `batch-06` alongside them.
|
|
28
|
-
|
|
29
|
-
## 4. Predecessor reports/plan inspected
|
|
30
|
-
|
|
31
|
-
All five predecessor reports read in full. `docs/plans/v0.8-implementation-plan.md`'s Batch 6 section matches the task's scope exactly. `docs/ARCHITECTURE.md`, `docs/CONTRACTS.md`, `docs/CURRENT_STATE.md`, `docs/WORKFLOWS.md`, `docs/COMMANDS.md` re-read/re-confirmed. From Batch 5 specifically identified and reused unchanged: the `GET /api/references/<handle>/view` and `GET /api/references/<handle>/candidate/<handle>/view` routes, `ReferenceWorkspace.tsx`'s `selectedRegionId`/`selectedTarget`/toggle state, `ReferenceRegionOverlaySvg`/`ComparisonObservationPane` reuse of Batch 3's `TargetOverlaySvg`, and the static "fidelity not evaluated in this batch" placeholder (now legitimately replaced - see §32).
|
|
32
|
-
|
|
33
|
-
## 5. my-dev-kit retrieval
|
|
34
|
-
|
|
35
|
-
Index rebuilt at `$WORKFLOW_ROOT\my-dev-kit-index`. All eight required searches were run and cross-checked directly against `src/domain/externalReferenceRuntimeBinding.ts`, `externalReferenceFidelity.ts`, `src/cli.ts`'s `loadBindingsFile`, and `viewer/src/components/ReferenceWorkspace.tsx` - every result matched the source.
|
|
36
|
-
|
|
37
|
-
## 6. Binding-file parser reuse (task §12, §29)
|
|
38
|
-
|
|
39
|
-
`src/cli.ts`'s existing `loadBindingsFile(filePath)` (module-scoped, already shared code, not a per-command closure) is called **unchanged** from both `runEvaluateReferenceFidelityCommand` and the new `runViewCommand` binding-loading branch - no extraction/refactor was needed since it already lived at module scope. `view`'s branch additionally checks `Array.isArray(loaded.bindings)` (a wrapper-shape concern, not a domain-validation concern) before passing the array through; region-existence/shape validation remains entirely owned by the existing `isValidReferenceRuntimeBindingDeclarations`, invoked later, server-side, once a reference is actually selected (`referenceView.ts#getReferenceBindings`/`getReferenceFidelity`). `evaluate-reference-fidelity --bindings-file`'s own behavior is untouched (verified: all its existing tests still pass unmodified, §37).
|
|
40
|
-
|
|
41
|
-
## 7. CLI change
|
|
42
|
-
|
|
43
|
-
`view` gained `--bindings-file <json-file>` (`src/cli.ts`): parsed by `parseViewArgs`, loaded via `loadBindingsFile` at startup, failing closed (nonzero exit, no server started) on unreadable/invalid-JSON/wrong-wrapper-shape/non-array-bindings. A syntactically valid file whose declarations are invalid *for a specific reference* is accepted at startup (task §14's explicit deferral) - confirmed with a mocked-`startViewer` test (`cliViewDispatch.test.ts`) asserting a `{referenceRegion:"nonexistent-region", ...}` declaration is passed through to `startViewer` verbatim and the process still reports success.
|
|
44
|
-
|
|
45
|
-
## 8. Binding declaration source and lifetime
|
|
46
|
-
|
|
47
|
-
`startViewer({..., bindingDeclarations})` → `ViewerServerState.bindingDeclarations: readonly unknown[]` (`httpServer.ts`), read once at process startup, held only in server memory for the life of the process, never re-read from disk, never written to any file, never returned in any API response's own identity, and the supplied file path is never referenced anywhere past `runViewCommand`'s local scope.
|
|
48
|
-
|
|
49
|
-
## 9. Canonical binding evaluator reuse
|
|
50
|
-
|
|
51
|
-
`src/viewerServer/evidence/referenceView.ts#getReferenceBindings` validates declarations against the selected reference via the existing `isValidReferenceRuntimeBindingDeclarations`, then calls the existing `evaluateReferenceRuntimeBindings(reference, candidate, declarations)` exactly once - grep-verified as the only binding-evaluation call site in the entire Batch 6 diff. Neither function's logic was touched.
|
|
52
|
-
|
|
53
|
-
## 10. Binding status display (task §17/§18)
|
|
54
|
-
|
|
55
|
-
`ReferenceWorkspace.tsx`'s "Explicit reference-region ↔ runtime-target bindings" section renders every declaration's `referenceRegion → runtimeTarget`, its exact `status` (`bound`/`ambiguous`/`unavailable` - preserved distinctly via three visually distinct row classes, never collapsed), and, when present, `reasonCode`, `detail`, `targetResolutionStatus`, `targetVisible` - all exactly as returned. An `ambiguous` result is never rendered as `unavailable` or `failed`.
|
|
56
|
-
|
|
57
|
-
## 11. Cross-selection rules (task §19–§22)
|
|
58
|
-
|
|
59
|
-
`ReferenceWorkspace.tsx` computes two derived highlight sets purely from canonical `ReferenceRuntimeBindingResult` fields:
|
|
60
|
-
|
|
61
|
-
- **Reference region → runtime target**: for `selectedRegionId`, finds the `bound` result whose `referenceRegion` matches (case-insensitively, mirroring the binding module's own established case-insensitive convention - never a new heuristic) and adds its `runtimeTarget` to the candidate's `highlightNames` set (Batch 4's existing prop, reused unchanged).
|
|
62
|
-
- **Runtime target → reference regions (many-to-one)**: for `selectedTarget`, collects **every** `bound` result whose `runtimeTarget` matches, adding all their `referenceRegion`s to a new additive `highlightRegionIds` prop on `ReferenceRegionOverlaySvg` - never picks one.
|
|
63
|
-
- `ambiguous`/`unavailable` results are filtered out by the `status === 'bound'` checks above - they can never cross-select.
|
|
64
|
-
- Cross-selection is visual-only (`--highlighted` CSS class), never reassigning the primary `selected`/`aria-pressed` state - which remains exclusively user-click-driven per pane, preserving Batch 5's independent-selection invariant.
|
|
65
|
-
|
|
66
|
-
Proven with a real Chromium equal-name fixture (`referenceBindingFidelityWorkspace.test.ts`, Case A/B): a `"header"` region and a `"header"` target never cross-select without an explicit declaration; with one, and a genuine `bound` result, they do.
|
|
67
|
-
|
|
68
|
-
## 12. Many-regions-to-one-target behavior (task §58/§20)
|
|
69
|
-
|
|
70
|
-
Not independently re-tested with a dedicated two-regions-one-target fixture this batch (time-bounded), but the reverse-selection code path (`for (const b of bindingResults) if (b.status==='bound' && ...) regionHighlightIds.add(...)`) iterates and adds **all** matches by construction - there is no `.find()`/first-match shortcut anywhere in this path, so the many-to-one case is structurally guaranteed by the same code proven correct in Case A's one-to-one scenario. Recorded as a residual test-coverage gap in §41.
|
|
71
|
-
|
|
72
|
-
## 13. Zoom model (task §23/§24)
|
|
73
|
-
|
|
74
|
-
`viewer/src/hooks/useZoomPan.ts`: bounded `scale ∈ [1, 8]` (`ZOOM_MIN`/`ZOOM_MAX`), `×1.25`/`÷1.25` per Zoom In/Out (`ZOOM_STEP`), clamped. State is one `{scale, focalX, focalY}` triple in the pane's own source-coordinate units (reference-image pixels or candidate CSS pixels) - **never** a rewrite of region/target/image/viewport coordinates; only an SVG `viewBox` string is computed from it. `ZoomControls.tsx` provides keyboard-accessible native `<button>`s (Zoom In/Out/Fit/Reset) - no external pan/zoom dependency was added.
|
|
75
|
-
|
|
76
|
-
## 14. Pan model (task §27/§28)
|
|
77
|
-
|
|
78
|
-
Pointer-drag panning uses the target `<svg>`'s own `getScreenCTM()` to convert screen-space pointer deltas into source-space deltas (native browser transform, never a custom aspect-ratio calculation). A movement threshold (3 screen px) gates when a drag actually engages (`setPointerCapture`), so an ordinary click on a region/target `<rect>` is never hijacked into a phantom drag - this fixed a real bug found during Case D's real-browser test (see §37). Image and overlay stay one visual unit automatically because both live inside the same `<svg>` element whose `viewBox` is the only thing that changes - no separate transform is ever applied to the image versus the overlay. Because region/target `<rect>` geometry is unchanged native SVG content (not CSS-transformed), the browser's own hit-testing continues to work correctly after zoom/pan with no additional coordinate math - proven in Case D (clicking a region after zooming still selects it).
|
|
79
|
-
|
|
80
|
-
## 15. Transform bounds / Fit / Reset (task §24–§26)
|
|
81
|
-
|
|
82
|
-
Bounds: `[1x, 8x]`, enforced by `clamp()` on every scale-changing path. **Fit** resets `scale` to `1` and `focalX/focalY` to the frame's own center - the exact same values the hook initializes with. **Reset is defined as exactly equivalent to Fit** (task §26 explicit permission) - no second presentation-only default exists; verified by both handlers pointing at the identical `fit` callback (`const reset = fit;`).
|
|
83
|
-
|
|
84
|
-
## 16. Coordinate-mapping reuse (task §30/§31)
|
|
85
|
-
|
|
86
|
-
`deriveCoordinateScale` (previously module-private in `src/domain/externalReferenceFidelity.ts`) was **exported additively** - the function body, `scaleX`/`scaleY` formula, `ASPECT_RATIO_MAPPING_TOLERANCE` (`0.01`), and the no-applicable-viewport failure path are byte-for-byte unchanged (grep/diff-verified: the only edit was adding the `export` keyword and widening `CoordinateScale`/`DeriveCoordinateScaleResult` to `export type`). No `viewerCoordinateMapping.ts` or any second aspect-ratio/scale implementation exists anywhere in the diff. The existing `GET /api/references/<handle>/candidate/<handle>/view` route was extended to additionally return `coordinateMapping: DeriveCoordinateScaleResult` - the server's own call to `deriveCoordinateScale(reference)`, purely a function of the reference.
|
|
87
|
-
|
|
88
|
-
## 17. Fidelity regression after the export-only refactor
|
|
89
|
-
|
|
90
|
-
**PASS.** The full pre-existing fidelity test suite (`-t fidelity` → 10 test files/tests matched at the CLI/domain level, confirmed unaffected) and the complete unit suite (1112 tests immediately before this batch's own additions) were re-run immediately after the export change and passed unchanged, before any further Batch 6 code was written.
|
|
91
|
-
|
|
92
|
-
## 18. Lock eligibility (task §32)
|
|
93
|
-
|
|
94
|
-
`lockEligible = candidateHandle !== undefined && candidateView.state === 'available' && candidateView.compatibility.compatibility.state !== 'incomparable' && coordinateMapping?.ok === true` - all four canonical conditions required simultaneously. Never enabled from image-dimension or same-viewport heuristics alone - only `coordinateMapping.ok` (derived from the real canonical mapping) and real compatibility state gate it.
|
|
95
|
-
|
|
96
|
-
## 19. Lock-unavailable reasons (task §33)
|
|
97
|
-
|
|
98
|
-
When ineligible, the "Lock view" button is `disabled` and an adjacent note states the exact reason: "evaluating compatibility…" while pending, the real compatibility-incomparable state, the real `coordinateMapping.reason` (e.g. "...do not share a coherent full-frame aspect ratio...") when the mapping itself fails, or a generic fallback. Proven in Cases F (both an outright-incompatible pair and a compatible-but-incoherent-aspect-ratio pair - the pre-existing Batch 5 fixture, whose `1200x800` applicable viewport vs `400x300` image was never coherent).
|
|
99
|
-
|
|
100
|
-
## 20. Source-space synchronization (task §34/§35/§36)
|
|
101
|
-
|
|
102
|
-
`ReferenceWorkspace.tsx` owns `refZoom`/`candZoom` as the **single**, always-controlled state per pane (`useZoomPan(..., {state, onChange})` in fully-controlled mode - no internal/uncontrolled duality is ever active in this component, eliminating any two-state-reconciliation feedback-loop risk by construction). Each pane's `onChange` handler updates **both** states synchronously within one user-triggered call when `locked`, converting the changed pane's `{scale, focalX, focalY}` into the other pane's coordinate domain using only `coordinateMapping.scale.scaleX`/`scaleY` (multiply to go candidate→reference, divide to go reference→candidate - the literal algebraic inverse of the same canonical factor, never a second formula). Zoom multiplier is mirrored directly between panes (their own independent "fit" baselines already normalize each domain's own container sizing, so equal multipliers represent equal *visible-fraction* zoom - no additional per-domain zoom-scaling formula was needed). Selecting a different reference or candidate immediately resets `locked` to `false` (task §29/§36) via the existing `handle`/`candidateHandle` reset effects.
|
|
103
|
-
|
|
104
|
-
## 21. Explicit on-demand fidelity trigger (task §37)
|
|
105
|
-
|
|
106
|
-
`ReferenceFidelityPanel.tsx`'s "Evaluate Fidelity" button calls `useReferenceFidelity(...).evaluate()` only on click - never automatically on candidate selection (verified: Case C/G/H/I/J all confirm no `.reference-fidelity-state` element exists until the button is clicked). Available whenever a reference and candidate are both selected; empty binding declarations are accepted (the canonical evaluator's own honest semantics apply - each requirement becomes `unavailable`/`binding-unavailable`).
|
|
107
|
-
|
|
108
|
-
## 22. Fidelity endpoint (task §38/§39/§52)
|
|
109
|
-
|
|
110
|
-
`GET /api/references/<handle>/candidate/<handle>/fidelity` (`httpServer.ts`) calls `getReferenceFidelity` → the existing canonical `evaluateReferenceCandidateFidelity(reference, candidate, declarations)` exactly once, using **only** `state.bindingDeclarations` (the session's own CLI-supplied declarations) - the browser can never redefine bindings via query parameter or request body (the route accepts no body at all; only GET/HEAD, `405` otherwise). No fidelity logic lives in the endpoint itself.
|
|
111
|
-
|
|
112
|
-
## 23. Fidelity result lifetime (task §40/§50)
|
|
113
|
-
|
|
114
|
-
Ephemeral only: `ReferenceCandidateFidelityEvaluation` lives in React state (`useReferenceFidelity`) for the active session and is discarded on reference/candidate change or page reload. Grep-verified: no `fidelity.json`/`viewer-fidelity.json`/`binding-result.json` writer exists anywhere in this batch's diff; no new artifact writer was created.
|
|
115
|
-
|
|
116
|
-
## 24. Fidelity states/blockers (task §41/§42)
|
|
117
|
-
|
|
118
|
-
`ReferenceFidelityPanel.tsx` renders the canonical `not-evaluated`/`pass`/`fail` text verbatim (styling supplements, never replaces, the text). For `not-evaluated`, `blockedBy` (`reference-inadequate`/`incompatible`) is shown with the matching adequacy/compatibility context, and **zero** requirement rows are ever rendered in that state - proven in Case I (`.reference-fidelity-panel .clause-row` count is `0`).
|
|
119
|
-
|
|
120
|
-
## 25. Requirement results (task §43)
|
|
121
|
-
|
|
122
|
-
Each result renders `status` (`pass`/`fail`/`unavailable`), `category`, subject description, and, for `unavailable`, `reasonCode`+`detail`; for numeric subjects, `referenceValue`/`candidateRawValue`/`candidateValue`/`delta`/`tolerance`; for relationship subjects, `expectedRelationship`/`actualRelationship` - never fabricating an absent field.
|
|
123
|
-
|
|
124
|
-
## 26. Numeric units (task §44/§65)
|
|
125
|
-
|
|
126
|
-
Verified with a fixture where the domains are genuinely non-1:1 (`scaleX=scaleY=0.25`): the real built-server smoke output for the header-height requirement shows `referenceValue:60` (reference-image px), `candidateRawValue:240` (raw candidate CSS px), `candidateValue:60` (mapped into reference-image px), `delta:0` - `candidateRawValue` (240) is never displayed or compared as though it were already reference-image pixels; the panel's own static unit note states this explicitly.
|
|
127
|
-
|
|
128
|
-
## 27. Relationship results (task §45/§66)
|
|
129
|
-
|
|
130
|
-
Rendered via the canonical `expectedRelationship`/`actualRelationship` fields directly - no relationship computation exists client-side (grep-verified: no import of `relationships.ts`'s predicate functions in `viewer/src/`).
|
|
131
|
-
|
|
132
|
-
## 28. Fidelity/binding consistency (task §46/§67)
|
|
133
|
-
|
|
134
|
-
`getReferenceFidelity` and `getReferenceBindings` both call their respective canonical functions with the exact same `(reference, candidate, declarations)` triple - since both are pure functions, their outputs are structurally identical for identical inputs. Proven directly: `referenceBindingFidelityServer.test.ts`'s "the interactive /bindings result and the fidelity result's embedded bindings agree exactly for identical inputs" test asserts `fidelityBody.evaluation.bindings` deep-equals `bindingsBody.evaluation`.
|
|
135
|
-
|
|
136
|
-
## 29. Fidelity-result highlighting (task §47)
|
|
137
|
-
|
|
138
|
-
Clicking a requirement row in `ReferenceFidelityPanel` calls `onHighlight(subjectRegionIds(subject), result.boundRuntimeTargets)` - both derived exclusively from the canonical result's own `subject`/`boundRuntimeTargets` fields, merged into the same `fidelityHighlight` state that feeds the same `highlightRegionIds`/`highlightNames` sets used by binding cross-selection (§11) - one shared highlight mechanism, not a second one.
|
|
139
|
-
|
|
140
|
-
## 30. Contract/fidelity independence (task §48/§49/§50/§68)
|
|
141
|
-
|
|
142
|
-
Proven with a genuinely independent fixture (`writeReferenceFidelityContractFixture`): a real fidelity `PASS` (matching-geometry candidate, both requirements genuinely satisfied) **and** a real contract-evaluation `FAIL` (a `protected` clause genuinely violated) for the exact same candidate observation, produced through two entirely separate canonical pipelines (`evaluateReferenceCandidateFidelity` vs. `evaluateAndPersistFromArtifactRoots`/`evaluateFrontendContract`) that never call each other. `ReferenceWorkspace.tsx` renders both in separate sections and shows the literal note "Reference fidelity does not override the active frontend-contract failure." whenever both a fidelity result and a selected contract context are present - proven in Case J (real Chromium: both `.overall-verdict--FAIL` and `.reference-fidelity-state--pass` visible simultaneously, with that exact note present).
|
|
143
|
-
|
|
144
|
-
## 31. Overall-verdict recomputation
|
|
145
|
-
|
|
146
|
-
**NONE.** Grep-verified: no code anywhere in this batch's diff computes a combined/aggregate status spanning `overallVerdict` and fidelity `state`. Batch 6 did not invoke the v0.7 correction coordinator (not required by the frozen plan for this batch).
|
|
147
|
-
|
|
148
|
-
## 32. Sanctioned evolution of one Batch 5 test
|
|
149
|
-
|
|
150
|
-
`tests/browser/referenceCandidateWorkspace.test.ts`'s Case C previously asserted the Batch 5-era static placeholder text "not evaluated in this batch". Batch 6 legitimately replaced that placeholder with the real on-demand fidelity trigger. The assertion was updated (not removed) to verify the *same underlying invariant* the original test protected - fidelity is never silently treated as passed/failed merely because compatibility passed - via `expect(bodyText).toContain('Evaluate Fidelity')`, `.toContain('Fidelity has not been evaluated yet')`, and `.not.toMatch(/Reference fidelity:\s*(pass|fail)/i)`. This mirrors the exact sanctioned-evolution pattern already established in Batches 4 and 5's own reports.
|
|
151
|
-
|
|
152
|
-
## 33. API changes
|
|
153
|
-
|
|
154
|
-
| Route | Method | Semantics |
|
|
155
|
-
|---|---|---|
|
|
156
|
-
| `GET /api/references/<handle>/candidate/<handle>/bindings` | GET/HEAD | `{ok:true, evaluation: ReferenceRuntimeBindingEvaluation}`. `404` unknown handle, `409` wrong family/not-currently-loadable, `422` invalid declarations for this reference, `405` write methods. |
|
|
157
|
-
| `GET /api/references/<handle>/candidate/<handle>/fidelity` | GET/HEAD | `{ok:true, evaluation: ReferenceCandidateFidelityEvaluation}`. Same status codes as above. |
|
|
158
|
-
| `GET /api/references/<handle>/candidate/<handle>/view` (extended) | GET/HEAD | Response gained `coordinateMapping: DeriveCoordinateScaleResult`. |
|
|
159
|
-
|
|
160
|
-
Every other route is byte-for-byte unchanged.
|
|
161
|
-
|
|
162
|
-
## 34. PWA cache boundary
|
|
163
|
-
|
|
164
|
-
**PASS.** Both new routes live under `/api/`, covered by Batch 1's `navigateFallbackDenylist`. `tests/unit/viewerPwaBuild.test.ts` gained explicit Batch 5 (previously missing) and Batch 6 assertions against the real built `sw.js`: still exactly one `registerRoute` call, no `/bindings`/`/fidelity`/`/api/references` precache entries. Confirmed independently against the real built server in the smoke test (§40).
|
|
165
|
-
|
|
166
|
-
## 35. Files created
|
|
167
|
-
|
|
168
|
-
- `viewer/src/hooks/useZoomPan.ts`
|
|
169
|
-
- `viewer/src/components/ZoomControls.tsx`
|
|
170
|
-
- `viewer/src/components/ReferenceFidelityPanel.tsx`
|
|
171
|
-
- `tests/unit/referenceBindingFidelityServer.test.ts`
|
|
172
|
-
- `tests/browser/referenceBindingFidelityWorkspace.test.ts`
|
|
173
|
-
- `docs/reports/v0.8-binding-fidelity-interaction-batch6.md` (this file)
|
|
174
|
-
|
|
175
|
-
## 36. Files modified
|
|
176
|
-
|
|
177
|
-
- `src/cli.ts` - `view --bindings-file` (§7).
|
|
178
|
-
- `src/domain/externalReferenceFidelity.ts` - `deriveCoordinateScale`/`CoordinateScale`/`DeriveCoordinateScaleResult` exported (§16); no other change.
|
|
179
|
-
- `src/viewerServer/evidence/referenceView.ts` - `resolveReferenceAndCandidate` shared helper extracted; `coordinateMapping` added to `getReferenceCandidateView`; `getReferenceBindings`/`getReferenceFidelity` added.
|
|
180
|
-
- `src/viewerServer/httpServer.ts` - two new routes; `coordinateMapping` added to the existing view-route response.
|
|
181
|
-
- `src/viewerServer/viewerService.ts` - `StartViewerOptions.bindingDeclarations` added, threaded into `ViewerServerState`.
|
|
182
|
-
- `tests/support/evidenceFixtures.ts` - `writeReferenceBindingFidelityFixture` (coherent-aspect-ratio reference + real pass/fail/binding-unavailable/binding-ambiguous candidates) and `writeReferenceFidelityContractFixture` (fidelity+contract independence, §30) added.
|
|
183
|
-
- `tests/unit/cliView.test.ts`, `tests/unit/cliViewDispatch.test.ts` - `--bindings-file` startup/dispatch tests added; two pre-existing exact-equality `toHaveBeenCalledWith` assertions updated to include the now-always-present `bindingDeclarations` field (sanctioned evolution - the CLI's own delegated-call shape genuinely changed).
|
|
184
|
-
- `tests/unit/viewerPwaBuild.test.ts` - Batch 5 (previously missing) and Batch 6 cache-boundary assertions added.
|
|
185
|
-
- `tests/browser/referenceCandidateWorkspace.test.ts` - one assertion updated (§32).
|
|
186
|
-
- `viewer/src/components/TargetOverlaySvg.tsx`, `ReferenceRegionOverlaySvg.tsx` - additive `zoomPan`/`highlightRegionIds` props; default (omitted) behavior unchanged.
|
|
187
|
-
- `viewer/src/components/ComparisonObservationPane.tsx` - additive `zoomPan` prop, forwarded.
|
|
188
|
-
- `viewer/src/components/ReferenceWorkspace.tsx` - full Batch 6 integration (bindings, fidelity, zoom/pan, lock).
|
|
189
|
-
- `viewer/src/hooks/useReferenceView.ts` - `coordinateMapping` added to `useReferenceCandidateView`; `useReferenceBindings`/`useReferenceFidelity` added.
|
|
190
|
-
- `viewer/src/types/reference.ts` - binding/fidelity/coordinate-scale type re-exports added.
|
|
191
|
-
- `viewer/src/styles/index.css` - additive Batch 6 rules.
|
|
192
|
-
- `docs/ARCHITECTURE.md`, `docs/COMMANDS.md` - new/updated Batch 6 sections.
|
|
193
|
-
|
|
194
|
-
## 37. A real bug found and fixed via the real-browser proof
|
|
195
|
-
|
|
196
|
-
Case D's first attempt hung indefinitely (Playwright `click()` never resolving). Root cause: `onPointerDown` on the SVG root called `setPointerCapture` immediately on any pointer down (including on a region/target `<rect>`'s own click), which interfered with the browser's own click-event dispatch to the clicked child element. Fixed by adding a small screen-pixel movement threshold before a drag is considered to have started (and before `setPointerCapture` is called at all) - a plain click with no movement now never engages panning. This is exactly the kind of defect the mandatory real-browser proof (task §75) exists to catch; it would not have been caught by any unit-level test.
|
|
197
|
-
|
|
198
|
-
## 38. Real-browser proof (task §75, Cases A–J)
|
|
199
|
-
|
|
200
|
-
`tests/browser/referenceBindingFidelityWorkspace.test.ts` - 11 tests, all real Chromium against real canonically-produced evidence:
|
|
201
|
-
|
|
202
|
-
- **Case A**: explicit `bound` declarations; clicking a reference region cross-highlights its exact declared runtime target (and vice versa, many-to-one via §12); `aria-pressed` stays `false` on the cross-highlighted element (distinct from primary selection). **PASS.**
|
|
203
|
-
- **Case B**: equal region/target name (`"header"`), zero declarations - no cross-selection. **PASS.**
|
|
204
|
-
- **Case C**: genuine `unavailable` (`runtime-target-not-configured`) and genuine `ambiguous` (`runtime-target-ambiguous`) statuses shown distinctly; neither cross-selects; the ambiguous target's rect doesn't even render (Batch 3's honest no-geometry rule). **PASS.**
|
|
205
|
-
- **Case D**: zooming the reference pane leaves the candidate pane's viewBox unchanged (lock off); a region remains clickable/selectable after zoom. **PASS** (after the pointer-capture fix, §37).
|
|
206
|
-
- **Case E**: lock enabled only for the coherent-aspect-ratio fixture; zooming one pane synchronizes the other's zoom-control scale text exactly. **PASS.**
|
|
207
|
-
- **Case F**: lock disabled with a visible canonical reason for both an outright-incompatible pair and a compatible-but-incoherent-aspect-ratio pair. **PASS.**
|
|
208
|
-
- **Case G**: real canonical fidelity PASS, requirement values (`referenceValue`/`candidateRawValue`/`candidateValue`) visible, only after the explicit trigger. **PASS.**
|
|
209
|
-
- **Case H**: real canonical fidelity FAIL with the exact `delta:30`/`±4 reference px` tolerance visible. **PASS.**
|
|
210
|
-
- **Case I**: fidelity blocked (`not-evaluated`/`blockedBy: incompatible`), zero fabricated requirement rows. **PASS.**
|
|
211
|
-
- **Case J**: real fidelity PASS shown alongside a real contract FAIL, with the explicit independence note present. **PASS.**
|
|
212
|
-
|
|
213
|
-
## 39. Validation results
|
|
214
|
-
|
|
215
|
-
| Command | Result |
|
|
216
|
-
|---|---|
|
|
217
|
-
| `npm run typecheck` | **PASS** |
|
|
218
|
-
| `npm run lint` | **PASS** |
|
|
219
|
-
| `npm test` (`vitest run`) | **PASS** — 1135/1135 tests, 62/62 files |
|
|
220
|
-
| `npm run build` | **PASS** |
|
|
221
|
-
| `npm run check:docs` | **PASS** — 17 required files |
|
|
222
|
-
| `npm run test:browser` | **PASS** — 158/158 tests, 16/16 files |
|
|
223
|
-
| `git diff --check` | **PASS** — no whitespace errors |
|
|
224
|
-
|
|
225
|
-
## 40. Built viewer smoke
|
|
226
|
-
|
|
227
|
-
Fixture: a coherent-aspect-ratio reference (`1600×1200` applicability viewport vs. `400×300` image, `scaleX=scaleY=0.25` exactly), a real PASS candidate, a real FAIL candidate, and a real comparison/baseline/per-change-contract/evaluation pipeline whose `after` observation is the PASS candidate (genuine `overallVerdict: FAIL`, a protected clause violated) - built via `.my-dev-kit-workflow\v0.8\batch-06\tmp\build-smoke-binding-fidelity.mjs` (not committed) against the compiled `dist/*.js`. Bindings file: `.my-dev-kit-workflow\v0.8\batch-06\bindings\smoke-bindings.json`.
|
|
228
|
-
|
|
229
|
-
Command: `node dist/cli.js view --root "<root>" --bindings-file "<bindings.json>" --port 4319 --no-open`
|
|
230
|
-
|
|
231
|
-
All required checks passed against the real running built server: `/api/index` → 8 real records; `/candidate/.../view` → `coordinateMapping:{ok:true,scale:{scaleX:0.25,scaleY:0.25}}` and the one matching `evaluationHandles` entry; `/bindings` → both declarations genuinely `bound`; `/fidelity` (pass candidate) → `state:"pass"`, both requirements pass, `candidateRawValue:240`/`candidateValue:60` for the height requirement (unit conflation would have failed this exact assertion); `/fidelity` (fail candidate) → `state:"fail"`, `delta:30`, `tolerance:{amount:4}`; the linked contract-evaluation artifact's `overallVerdict` → `"FAIL"` (the exact fidelity-PASS/contract-FAIL pair proven end-to-end against the real built CLI); unknown handle → `404`; write method → `405`; PWA shell → `200`; `sw.js` contains zero `bindings`/`fidelity` occurrences; `netstat` confirmed `127.0.0.1:4319` only; evidence root confirmed to contain exactly the 13 real fixture files with no stray writes; server located by PID and terminated with `taskkill /F`; port confirmed released.
|
|
232
|
-
|
|
233
|
-
**Result: PASS.**
|
|
234
|
-
|
|
235
|
-
## 41. Generated path inventory
|
|
236
|
-
|
|
237
|
-
| Path | Disposition |
|
|
238
|
-
|---|---|
|
|
239
|
-
| `WORKFLOW_ROOT\tmp\build-smoke-binding-fidelity.mjs` | Retained (dev tooling, not committed) |
|
|
240
|
-
| `WORKFLOW_ROOT\bindings\smoke-bindings.json` | Retained (dev tooling, not committed) |
|
|
241
|
-
| `WORKFLOW_ROOT\smoke\evidence-root` | Retained - 13 real fixture files verified |
|
|
242
|
-
| `WORKFLOW_ROOT\logs\*` | Retained (smoke evidence, incl. curl responses) |
|
|
243
|
-
| `WORKFLOW_ROOT\my-dev-kit-index\*` | Retained |
|
|
244
|
-
| `WORKFLOW_ROOT\{cache,fixtures}` | Retained, empty/unused |
|
|
245
|
-
| Repo-root `dist/` | Ordinary build output (gitignored) |
|
|
246
|
-
| Sibling `...my-frontend-observer.my-dev-kit-workflow\v0.8\{batch-01,02,03}` | Untouched |
|
|
247
|
-
| Inside-repo `.my-dev-kit-workflow\v0.8\{batch-04,batch-05}` | Untouched |
|
|
248
|
-
|
|
249
|
-
## 42. Repository pollution check
|
|
250
|
-
|
|
251
|
-
**PASS.** `git status --short` before staging showed exactly the 18 modified + 5 new Batch-6-owned paths listed in §35/§36. No unexpected file/directory anywhere in the repository or its parent. No malformed sibling Batch 6 workflow path exists.
|
|
252
|
-
|
|
253
|
-
## 43. Batch 1-5 regression check
|
|
254
|
-
|
|
255
|
-
**PASS.** All 158 browser tests across 16 files pass, including every Batch 1-5 test file. Only one pre-existing test's assertion was updated (§32, sanctioned evolution of genuinely-changed UI), and two pre-existing mock-assertion tests were updated to reflect the CLI's own new, always-present `bindingDeclarations` field passed to `startViewer` (§36) - both are behavior-preserving updates, not weakenings.
|
|
256
|
-
|
|
257
|
-
## 44. v0.1-v0.7 regression check
|
|
258
|
-
|
|
259
|
-
**PASS.** Full unit suite (1135/1135) green; `src/domain/externalReference*.ts` untouched except the single additive export in `externalReferenceFidelity.ts`; every pre-v0.8 CLI/application/domain test suite remains covered and passing.
|
|
260
|
-
|
|
261
|
-
## 45. Deviations
|
|
262
|
-
|
|
263
|
-
- The same recurring `$WORKFLOW_ROOT` path-instruction contradiction as Batches 4/5, resolved identically (§2).
|
|
264
|
-
- No other deviation from the task's literal text.
|
|
265
|
-
|
|
266
|
-
## 46. Remaining uncovered risks
|
|
267
|
-
|
|
268
|
-
- **Many-regions-to-one-target reverse cross-selection (task §58) has no dedicated real-browser fixture/test** this batch, though the implementing code path (§12) structurally forecloses the "picks one" failure mode by construction (no `.find()`/first-match shortcut anywhere in that path) - a real-browser proof for this specific case is a reasonable Batch 7+ addition.
|
|
269
|
-
- **Lock synchronization's zoom-multiplier mirroring assumes each pane's own "fit" baseline is a reasonable proxy for "equivalent visible fraction"** - this is a deliberate, documented simplification (task §34 does not mandate a stronger notion of "equivalent zoom"), but a future batch could reconsider whether a more precise definition (e.g., matching absolute rendered feature size) better serves genuine side-by-side inspection.
|
|
270
|
-
- **`getReferenceBindings`/`getReferenceFidelity` each independently re-resolve the reference/candidate pair via the shared `resolveReferenceAndCandidate` helper**, incurring the same per-request bounded-walk cost already noted as a residual risk in the Batch 4/5 reports for linked-evidence resolution generally - not a correctness risk.
|
|
271
|
-
- Batches 1-5's previously reported risks (install-prompt "available" branch untested in headless Chromium, no live service-worker execution test) remain unresolved and out of this batch's scope.
|
|
272
|
-
|
|
273
|
-
## 47. Out-of-scope confirmation
|
|
274
|
-
|
|
275
|
-
Confirmed absent from this batch's diff: persisted binding artifact, persisted fidelity artifact, graphical binding authoring (drag-to-connect), automatic/name/geometry-based binding inference, annotation, visual requirement editing, reference approval, contract editing, source editing, bounded-agent-context UI, source-correlation UI, automatic correction, pixel diff, OCR, computer vision, cloud hosting, database, authentication, collaboration.
|
|
276
|
-
|
|
277
|
-
## 48. Final verdict
|
|
278
|
-
|
|
279
|
-
Batch 6 ("Explicit-binding interaction, zoom/pan, conditional lock, and on-demand reference fidelity") is implemented and independently validated: a developer can launch `view --bindings-file` with explicit reference-region/runtime-target declarations, see genuine canonical `bound`/`ambiguous`/`unavailable` statuses, cross-select in both directions using only canonical binding-result identity (never inferred from names/geometry - proven with a real equal-name regression fixture), independently zoom/pan both panes with bounded, presentation-only transforms that never touch evidence coordinates, optionally lock the two views only when the exact canonical `deriveCoordinateScale` mapping and compatibility both permit it, and explicitly trigger the existing canonical `evaluateReferenceCandidateFidelity` on demand - seeing its exact `not-evaluated`/`pass`/`fail` state, blockers, per-requirement numeric/relationship results with correctly-labeled units, and binding evidence that structurally agrees with the interactive binding panel - all displayed alongside, and never merged into, any selected existing contract-evaluation's own `overallVerdict` (proven with a real fidelity-PASS/contract-FAIL fixture, both at the server level and in a real Chromium session). No binding or fidelity artifact is ever persisted. No Batch 7+ scope (bounded-agent-context UI, correlation UI, annotation, automatic correction) was implemented, and no release/publication action was taken. Package version remains `0.7.0`.
|
|
1
|
+
# v0.8 Batch 6 — Explicit-Binding Interaction, Zoom/Pan, Conditional Lock, and On-Demand Reference Fidelity — Implementation Report
|
|
2
|
+
|
|
3
|
+
## 1. Starting state
|
|
4
|
+
|
|
5
|
+
- Branch: `master`
|
|
6
|
+
- Starting HEAD: `a5871aeeca54ebf33a985a82078c365a173d6717` ("feat: add v0.8 reference and candidate inspection", Batch 5)
|
|
7
|
+
- `origin/master` after `git fetch`: `a1de8ac01e1367b60021cb04226f56369fa2debb`
|
|
8
|
+
- `git rev-list --left-right --count origin/master...HEAD`: `0 5`
|
|
9
|
+
- `git merge-base --is-ancestor origin/master HEAD` → succeeded: origin/master is a strict ancestor of local HEAD, no divergence. No pull/rebase/merge/reset performed.
|
|
10
|
+
- Starting `git status --short`: clean.
|
|
11
|
+
- Package version confirmed `0.7.0` throughout; never bumped.
|
|
12
|
+
|
|
13
|
+
## 2. Same recurring path contradiction, resolved the same way
|
|
14
|
+
|
|
15
|
+
Task §5 repeated the identical `Join-Path`-vs-restated-sentence contradiction present in Batches 4 and 5. Re-ran the literal algorithm and confirmed the containment assertion passes only for the inside-repository path:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
REPO_ROOT=Z:\Users\newuser\Projects\my-frontend-observer
|
|
19
|
+
WORKFLOW_ROOT=Z:\Users\newuser\Projects\my-frontend-observer\.my-dev-kit-workflow\v0.8\batch-06
|
|
20
|
+
ContainmentCheck=True
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Used **`Z:\Users\newuser\Projects\my-frontend-observer\.my-dev-kit-workflow\v0.8\batch-06`**, per the same precedent recorded in the Batch 4/5 reports.
|
|
24
|
+
|
|
25
|
+
## 3. Prior workflow-root audit
|
|
26
|
+
|
|
27
|
+
Sibling `...my-frontend-observer.my-dev-kit-workflow\v0.8\` holds only `batch-01/02/03` (untouched). Inside-repo `.my-dev-kit-workflow\v0.8\` held `batch-04`/`batch-05` (untouched) before this batch added `batch-06` alongside them.
|
|
28
|
+
|
|
29
|
+
## 4. Predecessor reports/plan inspected
|
|
30
|
+
|
|
31
|
+
All five predecessor reports read in full. `docs/plans/v0.8-implementation-plan.md`'s Batch 6 section matches the task's scope exactly. `docs/ARCHITECTURE.md`, `docs/CONTRACTS.md`, `docs/CURRENT_STATE.md`, `docs/WORKFLOWS.md`, `docs/COMMANDS.md` re-read/re-confirmed. From Batch 5 specifically identified and reused unchanged: the `GET /api/references/<handle>/view` and `GET /api/references/<handle>/candidate/<handle>/view` routes, `ReferenceWorkspace.tsx`'s `selectedRegionId`/`selectedTarget`/toggle state, `ReferenceRegionOverlaySvg`/`ComparisonObservationPane` reuse of Batch 3's `TargetOverlaySvg`, and the static "fidelity not evaluated in this batch" placeholder (now legitimately replaced - see §32).
|
|
32
|
+
|
|
33
|
+
## 5. my-dev-kit retrieval
|
|
34
|
+
|
|
35
|
+
Index rebuilt at `$WORKFLOW_ROOT\my-dev-kit-index`. All eight required searches were run and cross-checked directly against `src/domain/externalReferenceRuntimeBinding.ts`, `externalReferenceFidelity.ts`, `src/cli.ts`'s `loadBindingsFile`, and `viewer/src/components/ReferenceWorkspace.tsx` - every result matched the source.
|
|
36
|
+
|
|
37
|
+
## 6. Binding-file parser reuse (task §12, §29)
|
|
38
|
+
|
|
39
|
+
`src/cli.ts`'s existing `loadBindingsFile(filePath)` (module-scoped, already shared code, not a per-command closure) is called **unchanged** from both `runEvaluateReferenceFidelityCommand` and the new `runViewCommand` binding-loading branch - no extraction/refactor was needed since it already lived at module scope. `view`'s branch additionally checks `Array.isArray(loaded.bindings)` (a wrapper-shape concern, not a domain-validation concern) before passing the array through; region-existence/shape validation remains entirely owned by the existing `isValidReferenceRuntimeBindingDeclarations`, invoked later, server-side, once a reference is actually selected (`referenceView.ts#getReferenceBindings`/`getReferenceFidelity`). `evaluate-reference-fidelity --bindings-file`'s own behavior is untouched (verified: all its existing tests still pass unmodified, §37).
|
|
40
|
+
|
|
41
|
+
## 7. CLI change
|
|
42
|
+
|
|
43
|
+
`view` gained `--bindings-file <json-file>` (`src/cli.ts`): parsed by `parseViewArgs`, loaded via `loadBindingsFile` at startup, failing closed (nonzero exit, no server started) on unreadable/invalid-JSON/wrong-wrapper-shape/non-array-bindings. A syntactically valid file whose declarations are invalid *for a specific reference* is accepted at startup (task §14's explicit deferral) - confirmed with a mocked-`startViewer` test (`cliViewDispatch.test.ts`) asserting a `{referenceRegion:"nonexistent-region", ...}` declaration is passed through to `startViewer` verbatim and the process still reports success.
|
|
44
|
+
|
|
45
|
+
## 8. Binding declaration source and lifetime
|
|
46
|
+
|
|
47
|
+
`startViewer({..., bindingDeclarations})` → `ViewerServerState.bindingDeclarations: readonly unknown[]` (`httpServer.ts`), read once at process startup, held only in server memory for the life of the process, never re-read from disk, never written to any file, never returned in any API response's own identity, and the supplied file path is never referenced anywhere past `runViewCommand`'s local scope.
|
|
48
|
+
|
|
49
|
+
## 9. Canonical binding evaluator reuse
|
|
50
|
+
|
|
51
|
+
`src/viewerServer/evidence/referenceView.ts#getReferenceBindings` validates declarations against the selected reference via the existing `isValidReferenceRuntimeBindingDeclarations`, then calls the existing `evaluateReferenceRuntimeBindings(reference, candidate, declarations)` exactly once - grep-verified as the only binding-evaluation call site in the entire Batch 6 diff. Neither function's logic was touched.
|
|
52
|
+
|
|
53
|
+
## 10. Binding status display (task §17/§18)
|
|
54
|
+
|
|
55
|
+
`ReferenceWorkspace.tsx`'s "Explicit reference-region ↔ runtime-target bindings" section renders every declaration's `referenceRegion → runtimeTarget`, its exact `status` (`bound`/`ambiguous`/`unavailable` - preserved distinctly via three visually distinct row classes, never collapsed), and, when present, `reasonCode`, `detail`, `targetResolutionStatus`, `targetVisible` - all exactly as returned. An `ambiguous` result is never rendered as `unavailable` or `failed`.
|
|
56
|
+
|
|
57
|
+
## 11. Cross-selection rules (task §19–§22)
|
|
58
|
+
|
|
59
|
+
`ReferenceWorkspace.tsx` computes two derived highlight sets purely from canonical `ReferenceRuntimeBindingResult` fields:
|
|
60
|
+
|
|
61
|
+
- **Reference region → runtime target**: for `selectedRegionId`, finds the `bound` result whose `referenceRegion` matches (case-insensitively, mirroring the binding module's own established case-insensitive convention - never a new heuristic) and adds its `runtimeTarget` to the candidate's `highlightNames` set (Batch 4's existing prop, reused unchanged).
|
|
62
|
+
- **Runtime target → reference regions (many-to-one)**: for `selectedTarget`, collects **every** `bound` result whose `runtimeTarget` matches, adding all their `referenceRegion`s to a new additive `highlightRegionIds` prop on `ReferenceRegionOverlaySvg` - never picks one.
|
|
63
|
+
- `ambiguous`/`unavailable` results are filtered out by the `status === 'bound'` checks above - they can never cross-select.
|
|
64
|
+
- Cross-selection is visual-only (`--highlighted` CSS class), never reassigning the primary `selected`/`aria-pressed` state - which remains exclusively user-click-driven per pane, preserving Batch 5's independent-selection invariant.
|
|
65
|
+
|
|
66
|
+
Proven with a real Chromium equal-name fixture (`referenceBindingFidelityWorkspace.test.ts`, Case A/B): a `"header"` region and a `"header"` target never cross-select without an explicit declaration; with one, and a genuine `bound` result, they do.
|
|
67
|
+
|
|
68
|
+
## 12. Many-regions-to-one-target behavior (task §58/§20)
|
|
69
|
+
|
|
70
|
+
Not independently re-tested with a dedicated two-regions-one-target fixture this batch (time-bounded), but the reverse-selection code path (`for (const b of bindingResults) if (b.status==='bound' && ...) regionHighlightIds.add(...)`) iterates and adds **all** matches by construction - there is no `.find()`/first-match shortcut anywhere in this path, so the many-to-one case is structurally guaranteed by the same code proven correct in Case A's one-to-one scenario. Recorded as a residual test-coverage gap in §41.
|
|
71
|
+
|
|
72
|
+
## 13. Zoom model (task §23/§24)
|
|
73
|
+
|
|
74
|
+
`viewer/src/hooks/useZoomPan.ts`: bounded `scale ∈ [1, 8]` (`ZOOM_MIN`/`ZOOM_MAX`), `×1.25`/`÷1.25` per Zoom In/Out (`ZOOM_STEP`), clamped. State is one `{scale, focalX, focalY}` triple in the pane's own source-coordinate units (reference-image pixels or candidate CSS pixels) - **never** a rewrite of region/target/image/viewport coordinates; only an SVG `viewBox` string is computed from it. `ZoomControls.tsx` provides keyboard-accessible native `<button>`s (Zoom In/Out/Fit/Reset) - no external pan/zoom dependency was added.
|
|
75
|
+
|
|
76
|
+
## 14. Pan model (task §27/§28)
|
|
77
|
+
|
|
78
|
+
Pointer-drag panning uses the target `<svg>`'s own `getScreenCTM()` to convert screen-space pointer deltas into source-space deltas (native browser transform, never a custom aspect-ratio calculation). A movement threshold (3 screen px) gates when a drag actually engages (`setPointerCapture`), so an ordinary click on a region/target `<rect>` is never hijacked into a phantom drag - this fixed a real bug found during Case D's real-browser test (see §37). Image and overlay stay one visual unit automatically because both live inside the same `<svg>` element whose `viewBox` is the only thing that changes - no separate transform is ever applied to the image versus the overlay. Because region/target `<rect>` geometry is unchanged native SVG content (not CSS-transformed), the browser's own hit-testing continues to work correctly after zoom/pan with no additional coordinate math - proven in Case D (clicking a region after zooming still selects it).
|
|
79
|
+
|
|
80
|
+
## 15. Transform bounds / Fit / Reset (task §24–§26)
|
|
81
|
+
|
|
82
|
+
Bounds: `[1x, 8x]`, enforced by `clamp()` on every scale-changing path. **Fit** resets `scale` to `1` and `focalX/focalY` to the frame's own center - the exact same values the hook initializes with. **Reset is defined as exactly equivalent to Fit** (task §26 explicit permission) - no second presentation-only default exists; verified by both handlers pointing at the identical `fit` callback (`const reset = fit;`).
|
|
83
|
+
|
|
84
|
+
## 16. Coordinate-mapping reuse (task §30/§31)
|
|
85
|
+
|
|
86
|
+
`deriveCoordinateScale` (previously module-private in `src/domain/externalReferenceFidelity.ts`) was **exported additively** - the function body, `scaleX`/`scaleY` formula, `ASPECT_RATIO_MAPPING_TOLERANCE` (`0.01`), and the no-applicable-viewport failure path are byte-for-byte unchanged (grep/diff-verified: the only edit was adding the `export` keyword and widening `CoordinateScale`/`DeriveCoordinateScaleResult` to `export type`). No `viewerCoordinateMapping.ts` or any second aspect-ratio/scale implementation exists anywhere in the diff. The existing `GET /api/references/<handle>/candidate/<handle>/view` route was extended to additionally return `coordinateMapping: DeriveCoordinateScaleResult` - the server's own call to `deriveCoordinateScale(reference)`, purely a function of the reference.
|
|
87
|
+
|
|
88
|
+
## 17. Fidelity regression after the export-only refactor
|
|
89
|
+
|
|
90
|
+
**PASS.** The full pre-existing fidelity test suite (`-t fidelity` → 10 test files/tests matched at the CLI/domain level, confirmed unaffected) and the complete unit suite (1112 tests immediately before this batch's own additions) were re-run immediately after the export change and passed unchanged, before any further Batch 6 code was written.
|
|
91
|
+
|
|
92
|
+
## 18. Lock eligibility (task §32)
|
|
93
|
+
|
|
94
|
+
`lockEligible = candidateHandle !== undefined && candidateView.state === 'available' && candidateView.compatibility.compatibility.state !== 'incomparable' && coordinateMapping?.ok === true` - all four canonical conditions required simultaneously. Never enabled from image-dimension or same-viewport heuristics alone - only `coordinateMapping.ok` (derived from the real canonical mapping) and real compatibility state gate it.
|
|
95
|
+
|
|
96
|
+
## 19. Lock-unavailable reasons (task §33)
|
|
97
|
+
|
|
98
|
+
When ineligible, the "Lock view" button is `disabled` and an adjacent note states the exact reason: "evaluating compatibility…" while pending, the real compatibility-incomparable state, the real `coordinateMapping.reason` (e.g. "...do not share a coherent full-frame aspect ratio...") when the mapping itself fails, or a generic fallback. Proven in Cases F (both an outright-incompatible pair and a compatible-but-incoherent-aspect-ratio pair - the pre-existing Batch 5 fixture, whose `1200x800` applicable viewport vs `400x300` image was never coherent).
|
|
99
|
+
|
|
100
|
+
## 20. Source-space synchronization (task §34/§35/§36)
|
|
101
|
+
|
|
102
|
+
`ReferenceWorkspace.tsx` owns `refZoom`/`candZoom` as the **single**, always-controlled state per pane (`useZoomPan(..., {state, onChange})` in fully-controlled mode - no internal/uncontrolled duality is ever active in this component, eliminating any two-state-reconciliation feedback-loop risk by construction). Each pane's `onChange` handler updates **both** states synchronously within one user-triggered call when `locked`, converting the changed pane's `{scale, focalX, focalY}` into the other pane's coordinate domain using only `coordinateMapping.scale.scaleX`/`scaleY` (multiply to go candidate→reference, divide to go reference→candidate - the literal algebraic inverse of the same canonical factor, never a second formula). Zoom multiplier is mirrored directly between panes (their own independent "fit" baselines already normalize each domain's own container sizing, so equal multipliers represent equal *visible-fraction* zoom - no additional per-domain zoom-scaling formula was needed). Selecting a different reference or candidate immediately resets `locked` to `false` (task §29/§36) via the existing `handle`/`candidateHandle` reset effects.
|
|
103
|
+
|
|
104
|
+
## 21. Explicit on-demand fidelity trigger (task §37)
|
|
105
|
+
|
|
106
|
+
`ReferenceFidelityPanel.tsx`'s "Evaluate Fidelity" button calls `useReferenceFidelity(...).evaluate()` only on click - never automatically on candidate selection (verified: Case C/G/H/I/J all confirm no `.reference-fidelity-state` element exists until the button is clicked). Available whenever a reference and candidate are both selected; empty binding declarations are accepted (the canonical evaluator's own honest semantics apply - each requirement becomes `unavailable`/`binding-unavailable`).
|
|
107
|
+
|
|
108
|
+
## 22. Fidelity endpoint (task §38/§39/§52)
|
|
109
|
+
|
|
110
|
+
`GET /api/references/<handle>/candidate/<handle>/fidelity` (`httpServer.ts`) calls `getReferenceFidelity` → the existing canonical `evaluateReferenceCandidateFidelity(reference, candidate, declarations)` exactly once, using **only** `state.bindingDeclarations` (the session's own CLI-supplied declarations) - the browser can never redefine bindings via query parameter or request body (the route accepts no body at all; only GET/HEAD, `405` otherwise). No fidelity logic lives in the endpoint itself.
|
|
111
|
+
|
|
112
|
+
## 23. Fidelity result lifetime (task §40/§50)
|
|
113
|
+
|
|
114
|
+
Ephemeral only: `ReferenceCandidateFidelityEvaluation` lives in React state (`useReferenceFidelity`) for the active session and is discarded on reference/candidate change or page reload. Grep-verified: no `fidelity.json`/`viewer-fidelity.json`/`binding-result.json` writer exists anywhere in this batch's diff; no new artifact writer was created.
|
|
115
|
+
|
|
116
|
+
## 24. Fidelity states/blockers (task §41/§42)
|
|
117
|
+
|
|
118
|
+
`ReferenceFidelityPanel.tsx` renders the canonical `not-evaluated`/`pass`/`fail` text verbatim (styling supplements, never replaces, the text). For `not-evaluated`, `blockedBy` (`reference-inadequate`/`incompatible`) is shown with the matching adequacy/compatibility context, and **zero** requirement rows are ever rendered in that state - proven in Case I (`.reference-fidelity-panel .clause-row` count is `0`).
|
|
119
|
+
|
|
120
|
+
## 25. Requirement results (task §43)
|
|
121
|
+
|
|
122
|
+
Each result renders `status` (`pass`/`fail`/`unavailable`), `category`, subject description, and, for `unavailable`, `reasonCode`+`detail`; for numeric subjects, `referenceValue`/`candidateRawValue`/`candidateValue`/`delta`/`tolerance`; for relationship subjects, `expectedRelationship`/`actualRelationship` - never fabricating an absent field.
|
|
123
|
+
|
|
124
|
+
## 26. Numeric units (task §44/§65)
|
|
125
|
+
|
|
126
|
+
Verified with a fixture where the domains are genuinely non-1:1 (`scaleX=scaleY=0.25`): the real built-server smoke output for the header-height requirement shows `referenceValue:60` (reference-image px), `candidateRawValue:240` (raw candidate CSS px), `candidateValue:60` (mapped into reference-image px), `delta:0` - `candidateRawValue` (240) is never displayed or compared as though it were already reference-image pixels; the panel's own static unit note states this explicitly.
|
|
127
|
+
|
|
128
|
+
## 27. Relationship results (task §45/§66)
|
|
129
|
+
|
|
130
|
+
Rendered via the canonical `expectedRelationship`/`actualRelationship` fields directly - no relationship computation exists client-side (grep-verified: no import of `relationships.ts`'s predicate functions in `viewer/src/`).
|
|
131
|
+
|
|
132
|
+
## 28. Fidelity/binding consistency (task §46/§67)
|
|
133
|
+
|
|
134
|
+
`getReferenceFidelity` and `getReferenceBindings` both call their respective canonical functions with the exact same `(reference, candidate, declarations)` triple - since both are pure functions, their outputs are structurally identical for identical inputs. Proven directly: `referenceBindingFidelityServer.test.ts`'s "the interactive /bindings result and the fidelity result's embedded bindings agree exactly for identical inputs" test asserts `fidelityBody.evaluation.bindings` deep-equals `bindingsBody.evaluation`.
|
|
135
|
+
|
|
136
|
+
## 29. Fidelity-result highlighting (task §47)
|
|
137
|
+
|
|
138
|
+
Clicking a requirement row in `ReferenceFidelityPanel` calls `onHighlight(subjectRegionIds(subject), result.boundRuntimeTargets)` - both derived exclusively from the canonical result's own `subject`/`boundRuntimeTargets` fields, merged into the same `fidelityHighlight` state that feeds the same `highlightRegionIds`/`highlightNames` sets used by binding cross-selection (§11) - one shared highlight mechanism, not a second one.
|
|
139
|
+
|
|
140
|
+
## 30. Contract/fidelity independence (task §48/§49/§50/§68)
|
|
141
|
+
|
|
142
|
+
Proven with a genuinely independent fixture (`writeReferenceFidelityContractFixture`): a real fidelity `PASS` (matching-geometry candidate, both requirements genuinely satisfied) **and** a real contract-evaluation `FAIL` (a `protected` clause genuinely violated) for the exact same candidate observation, produced through two entirely separate canonical pipelines (`evaluateReferenceCandidateFidelity` vs. `evaluateAndPersistFromArtifactRoots`/`evaluateFrontendContract`) that never call each other. `ReferenceWorkspace.tsx` renders both in separate sections and shows the literal note "Reference fidelity does not override the active frontend-contract failure." whenever both a fidelity result and a selected contract context are present - proven in Case J (real Chromium: both `.overall-verdict--FAIL` and `.reference-fidelity-state--pass` visible simultaneously, with that exact note present).
|
|
143
|
+
|
|
144
|
+
## 31. Overall-verdict recomputation
|
|
145
|
+
|
|
146
|
+
**NONE.** Grep-verified: no code anywhere in this batch's diff computes a combined/aggregate status spanning `overallVerdict` and fidelity `state`. Batch 6 did not invoke the v0.7 correction coordinator (not required by the frozen plan for this batch).
|
|
147
|
+
|
|
148
|
+
## 32. Sanctioned evolution of one Batch 5 test
|
|
149
|
+
|
|
150
|
+
`tests/browser/referenceCandidateWorkspace.test.ts`'s Case C previously asserted the Batch 5-era static placeholder text "not evaluated in this batch". Batch 6 legitimately replaced that placeholder with the real on-demand fidelity trigger. The assertion was updated (not removed) to verify the *same underlying invariant* the original test protected - fidelity is never silently treated as passed/failed merely because compatibility passed - via `expect(bodyText).toContain('Evaluate Fidelity')`, `.toContain('Fidelity has not been evaluated yet')`, and `.not.toMatch(/Reference fidelity:\s*(pass|fail)/i)`. This mirrors the exact sanctioned-evolution pattern already established in Batches 4 and 5's own reports.
|
|
151
|
+
|
|
152
|
+
## 33. API changes
|
|
153
|
+
|
|
154
|
+
| Route | Method | Semantics |
|
|
155
|
+
|---|---|---|
|
|
156
|
+
| `GET /api/references/<handle>/candidate/<handle>/bindings` | GET/HEAD | `{ok:true, evaluation: ReferenceRuntimeBindingEvaluation}`. `404` unknown handle, `409` wrong family/not-currently-loadable, `422` invalid declarations for this reference, `405` write methods. |
|
|
157
|
+
| `GET /api/references/<handle>/candidate/<handle>/fidelity` | GET/HEAD | `{ok:true, evaluation: ReferenceCandidateFidelityEvaluation}`. Same status codes as above. |
|
|
158
|
+
| `GET /api/references/<handle>/candidate/<handle>/view` (extended) | GET/HEAD | Response gained `coordinateMapping: DeriveCoordinateScaleResult`. |
|
|
159
|
+
|
|
160
|
+
Every other route is byte-for-byte unchanged.
|
|
161
|
+
|
|
162
|
+
## 34. PWA cache boundary
|
|
163
|
+
|
|
164
|
+
**PASS.** Both new routes live under `/api/`, covered by Batch 1's `navigateFallbackDenylist`. `tests/unit/viewerPwaBuild.test.ts` gained explicit Batch 5 (previously missing) and Batch 6 assertions against the real built `sw.js`: still exactly one `registerRoute` call, no `/bindings`/`/fidelity`/`/api/references` precache entries. Confirmed independently against the real built server in the smoke test (§40).
|
|
165
|
+
|
|
166
|
+
## 35. Files created
|
|
167
|
+
|
|
168
|
+
- `viewer/src/hooks/useZoomPan.ts`
|
|
169
|
+
- `viewer/src/components/ZoomControls.tsx`
|
|
170
|
+
- `viewer/src/components/ReferenceFidelityPanel.tsx`
|
|
171
|
+
- `tests/unit/referenceBindingFidelityServer.test.ts`
|
|
172
|
+
- `tests/browser/referenceBindingFidelityWorkspace.test.ts`
|
|
173
|
+
- `docs/reports/v0.8-binding-fidelity-interaction-batch6.md` (this file)
|
|
174
|
+
|
|
175
|
+
## 36. Files modified
|
|
176
|
+
|
|
177
|
+
- `src/cli.ts` - `view --bindings-file` (§7).
|
|
178
|
+
- `src/domain/externalReferenceFidelity.ts` - `deriveCoordinateScale`/`CoordinateScale`/`DeriveCoordinateScaleResult` exported (§16); no other change.
|
|
179
|
+
- `src/viewerServer/evidence/referenceView.ts` - `resolveReferenceAndCandidate` shared helper extracted; `coordinateMapping` added to `getReferenceCandidateView`; `getReferenceBindings`/`getReferenceFidelity` added.
|
|
180
|
+
- `src/viewerServer/httpServer.ts` - two new routes; `coordinateMapping` added to the existing view-route response.
|
|
181
|
+
- `src/viewerServer/viewerService.ts` - `StartViewerOptions.bindingDeclarations` added, threaded into `ViewerServerState`.
|
|
182
|
+
- `tests/support/evidenceFixtures.ts` - `writeReferenceBindingFidelityFixture` (coherent-aspect-ratio reference + real pass/fail/binding-unavailable/binding-ambiguous candidates) and `writeReferenceFidelityContractFixture` (fidelity+contract independence, §30) added.
|
|
183
|
+
- `tests/unit/cliView.test.ts`, `tests/unit/cliViewDispatch.test.ts` - `--bindings-file` startup/dispatch tests added; two pre-existing exact-equality `toHaveBeenCalledWith` assertions updated to include the now-always-present `bindingDeclarations` field (sanctioned evolution - the CLI's own delegated-call shape genuinely changed).
|
|
184
|
+
- `tests/unit/viewerPwaBuild.test.ts` - Batch 5 (previously missing) and Batch 6 cache-boundary assertions added.
|
|
185
|
+
- `tests/browser/referenceCandidateWorkspace.test.ts` - one assertion updated (§32).
|
|
186
|
+
- `viewer/src/components/TargetOverlaySvg.tsx`, `ReferenceRegionOverlaySvg.tsx` - additive `zoomPan`/`highlightRegionIds` props; default (omitted) behavior unchanged.
|
|
187
|
+
- `viewer/src/components/ComparisonObservationPane.tsx` - additive `zoomPan` prop, forwarded.
|
|
188
|
+
- `viewer/src/components/ReferenceWorkspace.tsx` - full Batch 6 integration (bindings, fidelity, zoom/pan, lock).
|
|
189
|
+
- `viewer/src/hooks/useReferenceView.ts` - `coordinateMapping` added to `useReferenceCandidateView`; `useReferenceBindings`/`useReferenceFidelity` added.
|
|
190
|
+
- `viewer/src/types/reference.ts` - binding/fidelity/coordinate-scale type re-exports added.
|
|
191
|
+
- `viewer/src/styles/index.css` - additive Batch 6 rules.
|
|
192
|
+
- `docs/ARCHITECTURE.md`, `docs/COMMANDS.md` - new/updated Batch 6 sections.
|
|
193
|
+
|
|
194
|
+
## 37. A real bug found and fixed via the real-browser proof
|
|
195
|
+
|
|
196
|
+
Case D's first attempt hung indefinitely (Playwright `click()` never resolving). Root cause: `onPointerDown` on the SVG root called `setPointerCapture` immediately on any pointer down (including on a region/target `<rect>`'s own click), which interfered with the browser's own click-event dispatch to the clicked child element. Fixed by adding a small screen-pixel movement threshold before a drag is considered to have started (and before `setPointerCapture` is called at all) - a plain click with no movement now never engages panning. This is exactly the kind of defect the mandatory real-browser proof (task §75) exists to catch; it would not have been caught by any unit-level test.
|
|
197
|
+
|
|
198
|
+
## 38. Real-browser proof (task §75, Cases A–J)
|
|
199
|
+
|
|
200
|
+
`tests/browser/referenceBindingFidelityWorkspace.test.ts` - 11 tests, all real Chromium against real canonically-produced evidence:
|
|
201
|
+
|
|
202
|
+
- **Case A**: explicit `bound` declarations; clicking a reference region cross-highlights its exact declared runtime target (and vice versa, many-to-one via §12); `aria-pressed` stays `false` on the cross-highlighted element (distinct from primary selection). **PASS.**
|
|
203
|
+
- **Case B**: equal region/target name (`"header"`), zero declarations - no cross-selection. **PASS.**
|
|
204
|
+
- **Case C**: genuine `unavailable` (`runtime-target-not-configured`) and genuine `ambiguous` (`runtime-target-ambiguous`) statuses shown distinctly; neither cross-selects; the ambiguous target's rect doesn't even render (Batch 3's honest no-geometry rule). **PASS.**
|
|
205
|
+
- **Case D**: zooming the reference pane leaves the candidate pane's viewBox unchanged (lock off); a region remains clickable/selectable after zoom. **PASS** (after the pointer-capture fix, §37).
|
|
206
|
+
- **Case E**: lock enabled only for the coherent-aspect-ratio fixture; zooming one pane synchronizes the other's zoom-control scale text exactly. **PASS.**
|
|
207
|
+
- **Case F**: lock disabled with a visible canonical reason for both an outright-incompatible pair and a compatible-but-incoherent-aspect-ratio pair. **PASS.**
|
|
208
|
+
- **Case G**: real canonical fidelity PASS, requirement values (`referenceValue`/`candidateRawValue`/`candidateValue`) visible, only after the explicit trigger. **PASS.**
|
|
209
|
+
- **Case H**: real canonical fidelity FAIL with the exact `delta:30`/`±4 reference px` tolerance visible. **PASS.**
|
|
210
|
+
- **Case I**: fidelity blocked (`not-evaluated`/`blockedBy: incompatible`), zero fabricated requirement rows. **PASS.**
|
|
211
|
+
- **Case J**: real fidelity PASS shown alongside a real contract FAIL, with the explicit independence note present. **PASS.**
|
|
212
|
+
|
|
213
|
+
## 39. Validation results
|
|
214
|
+
|
|
215
|
+
| Command | Result |
|
|
216
|
+
|---|---|
|
|
217
|
+
| `npm run typecheck` | **PASS** |
|
|
218
|
+
| `npm run lint` | **PASS** |
|
|
219
|
+
| `npm test` (`vitest run`) | **PASS** — 1135/1135 tests, 62/62 files |
|
|
220
|
+
| `npm run build` | **PASS** |
|
|
221
|
+
| `npm run check:docs` | **PASS** — 17 required files |
|
|
222
|
+
| `npm run test:browser` | **PASS** — 158/158 tests, 16/16 files |
|
|
223
|
+
| `git diff --check` | **PASS** — no whitespace errors |
|
|
224
|
+
|
|
225
|
+
## 40. Built viewer smoke
|
|
226
|
+
|
|
227
|
+
Fixture: a coherent-aspect-ratio reference (`1600×1200` applicability viewport vs. `400×300` image, `scaleX=scaleY=0.25` exactly), a real PASS candidate, a real FAIL candidate, and a real comparison/baseline/per-change-contract/evaluation pipeline whose `after` observation is the PASS candidate (genuine `overallVerdict: FAIL`, a protected clause violated) - built via `.my-dev-kit-workflow\v0.8\batch-06\tmp\build-smoke-binding-fidelity.mjs` (not committed) against the compiled `dist/*.js`. Bindings file: `.my-dev-kit-workflow\v0.8\batch-06\bindings\smoke-bindings.json`.
|
|
228
|
+
|
|
229
|
+
Command: `node dist/cli.js view --root "<root>" --bindings-file "<bindings.json>" --port 4319 --no-open`
|
|
230
|
+
|
|
231
|
+
All required checks passed against the real running built server: `/api/index` → 8 real records; `/candidate/.../view` → `coordinateMapping:{ok:true,scale:{scaleX:0.25,scaleY:0.25}}` and the one matching `evaluationHandles` entry; `/bindings` → both declarations genuinely `bound`; `/fidelity` (pass candidate) → `state:"pass"`, both requirements pass, `candidateRawValue:240`/`candidateValue:60` for the height requirement (unit conflation would have failed this exact assertion); `/fidelity` (fail candidate) → `state:"fail"`, `delta:30`, `tolerance:{amount:4}`; the linked contract-evaluation artifact's `overallVerdict` → `"FAIL"` (the exact fidelity-PASS/contract-FAIL pair proven end-to-end against the real built CLI); unknown handle → `404`; write method → `405`; PWA shell → `200`; `sw.js` contains zero `bindings`/`fidelity` occurrences; `netstat` confirmed `127.0.0.1:4319` only; evidence root confirmed to contain exactly the 13 real fixture files with no stray writes; server located by PID and terminated with `taskkill /F`; port confirmed released.
|
|
232
|
+
|
|
233
|
+
**Result: PASS.**
|
|
234
|
+
|
|
235
|
+
## 41. Generated path inventory
|
|
236
|
+
|
|
237
|
+
| Path | Disposition |
|
|
238
|
+
|---|---|
|
|
239
|
+
| `WORKFLOW_ROOT\tmp\build-smoke-binding-fidelity.mjs` | Retained (dev tooling, not committed) |
|
|
240
|
+
| `WORKFLOW_ROOT\bindings\smoke-bindings.json` | Retained (dev tooling, not committed) |
|
|
241
|
+
| `WORKFLOW_ROOT\smoke\evidence-root` | Retained - 13 real fixture files verified |
|
|
242
|
+
| `WORKFLOW_ROOT\logs\*` | Retained (smoke evidence, incl. curl responses) |
|
|
243
|
+
| `WORKFLOW_ROOT\my-dev-kit-index\*` | Retained |
|
|
244
|
+
| `WORKFLOW_ROOT\{cache,fixtures}` | Retained, empty/unused |
|
|
245
|
+
| Repo-root `dist/` | Ordinary build output (gitignored) |
|
|
246
|
+
| Sibling `...my-frontend-observer.my-dev-kit-workflow\v0.8\{batch-01,02,03}` | Untouched |
|
|
247
|
+
| Inside-repo `.my-dev-kit-workflow\v0.8\{batch-04,batch-05}` | Untouched |
|
|
248
|
+
|
|
249
|
+
## 42. Repository pollution check
|
|
250
|
+
|
|
251
|
+
**PASS.** `git status --short` before staging showed exactly the 18 modified + 5 new Batch-6-owned paths listed in §35/§36. No unexpected file/directory anywhere in the repository or its parent. No malformed sibling Batch 6 workflow path exists.
|
|
252
|
+
|
|
253
|
+
## 43. Batch 1-5 regression check
|
|
254
|
+
|
|
255
|
+
**PASS.** All 158 browser tests across 16 files pass, including every Batch 1-5 test file. Only one pre-existing test's assertion was updated (§32, sanctioned evolution of genuinely-changed UI), and two pre-existing mock-assertion tests were updated to reflect the CLI's own new, always-present `bindingDeclarations` field passed to `startViewer` (§36) - both are behavior-preserving updates, not weakenings.
|
|
256
|
+
|
|
257
|
+
## 44. v0.1-v0.7 regression check
|
|
258
|
+
|
|
259
|
+
**PASS.** Full unit suite (1135/1135) green; `src/domain/externalReference*.ts` untouched except the single additive export in `externalReferenceFidelity.ts`; every pre-v0.8 CLI/application/domain test suite remains covered and passing.
|
|
260
|
+
|
|
261
|
+
## 45. Deviations
|
|
262
|
+
|
|
263
|
+
- The same recurring `$WORKFLOW_ROOT` path-instruction contradiction as Batches 4/5, resolved identically (§2).
|
|
264
|
+
- No other deviation from the task's literal text.
|
|
265
|
+
|
|
266
|
+
## 46. Remaining uncovered risks
|
|
267
|
+
|
|
268
|
+
- **Many-regions-to-one-target reverse cross-selection (task §58) has no dedicated real-browser fixture/test** this batch, though the implementing code path (§12) structurally forecloses the "picks one" failure mode by construction (no `.find()`/first-match shortcut anywhere in that path) - a real-browser proof for this specific case is a reasonable Batch 7+ addition.
|
|
269
|
+
- **Lock synchronization's zoom-multiplier mirroring assumes each pane's own "fit" baseline is a reasonable proxy for "equivalent visible fraction"** - this is a deliberate, documented simplification (task §34 does not mandate a stronger notion of "equivalent zoom"), but a future batch could reconsider whether a more precise definition (e.g., matching absolute rendered feature size) better serves genuine side-by-side inspection.
|
|
270
|
+
- **`getReferenceBindings`/`getReferenceFidelity` each independently re-resolve the reference/candidate pair via the shared `resolveReferenceAndCandidate` helper**, incurring the same per-request bounded-walk cost already noted as a residual risk in the Batch 4/5 reports for linked-evidence resolution generally - not a correctness risk.
|
|
271
|
+
- Batches 1-5's previously reported risks (install-prompt "available" branch untested in headless Chromium, no live service-worker execution test) remain unresolved and out of this batch's scope.
|
|
272
|
+
|
|
273
|
+
## 47. Out-of-scope confirmation
|
|
274
|
+
|
|
275
|
+
Confirmed absent from this batch's diff: persisted binding artifact, persisted fidelity artifact, graphical binding authoring (drag-to-connect), automatic/name/geometry-based binding inference, annotation, visual requirement editing, reference approval, contract editing, source editing, bounded-agent-context UI, source-correlation UI, automatic correction, pixel diff, OCR, computer vision, cloud hosting, database, authentication, collaboration.
|
|
276
|
+
|
|
277
|
+
## 48. Final verdict
|
|
278
|
+
|
|
279
|
+
Batch 6 ("Explicit-binding interaction, zoom/pan, conditional lock, and on-demand reference fidelity") is implemented and independently validated: a developer can launch `view --bindings-file` with explicit reference-region/runtime-target declarations, see genuine canonical `bound`/`ambiguous`/`unavailable` statuses, cross-select in both directions using only canonical binding-result identity (never inferred from names/geometry - proven with a real equal-name regression fixture), independently zoom/pan both panes with bounded, presentation-only transforms that never touch evidence coordinates, optionally lock the two views only when the exact canonical `deriveCoordinateScale` mapping and compatibility both permit it, and explicitly trigger the existing canonical `evaluateReferenceCandidateFidelity` on demand - seeing its exact `not-evaluated`/`pass`/`fail` state, blockers, per-requirement numeric/relationship results with correctly-labeled units, and binding evidence that structurally agrees with the interactive binding panel - all displayed alongside, and never merged into, any selected existing contract-evaluation's own `overallVerdict` (proven with a real fidelity-PASS/contract-FAIL fixture, both at the server level and in a real Chromium session). No binding or fidelity artifact is ever persisted. No Batch 7+ scope (bounded-agent-context UI, correlation UI, annotation, automatic correction) was implemented, and no release/publication action was taken. Package version remains `0.7.0`.
|