--- id: CE-WP-0003 type: workplan title: "Form binding + visual guide — EvidenceLink, rect registry, SVG overlay" domain: citation_evidence repo: citation-evidence repo_id: a677c189-b4e2-4f2a-9e48-faa482c277e6 topic_slug: citation_evidence_mvp topic_id: 96fa8e80-9f74-40f2-84cd-644e9747b9ec state_hub_workstream_id: 7b5b7235-57e3-4835-8fa6-376bb518fe2d status: done owner: Bernd created: 2026-05-24 updated: 2026-05-25 depends_on_workplan: CE-WP-0002 spec_refs: - wiki/ProductRequirementsDocument.md - wiki/ArchitectureOverview.md - wiki/SharedContracts.md --- # CE-WP-0003 — Form Binding + Visual Guide Build the evidence-backed form mode and the SVG visual guide overlay. After this workplan, a user can: 1. Open a form next to the document viewer. 2. Drag (or click-to-link) an evidence item from the sidebar onto a form field. 3. Click a form field → its linked evidence items appear → the active evidence's source passage is scrolled into view and highlighted → an SVG guide visually connects the field, the evidence card, and the highlight. 4. Cycle through multiple evidence items on the same field. This is the workplan that stress-tests the rect-registry contract from `wiki/SharedContracts.md` §7. The form, the evidence card, and the viewer's highlight all need to publish rects to a single overlay that re-renders on scroll/resize/focus. ## Dependency Order ``` T01 (EvidenceLink + EvidenceSet types + relation/status enums) └─ T02 (binding service + in-memory link repo + active-state machine) └─ T03 (rect registry — the contract from SharedContracts.md §7) └─ T04 (form schema + simple field renderer) └─ T05 (side-by-side layout + drag-or-click to link) └─ T06 (active-evidence cycling on a field) └─ T07 (SVG visual guide overlay) └─ T08 (E2E test of PRD scenario steps 5-9) ``` --- ## T01 — `EvidenceLink` + `EvidenceSet` types ```task id: CE-WP-0003-T01 state_hub_task_id: 120b9b5a-9ca3-4dff-8c26-1b5c2e832dc4 priority: critical status: done ``` Add under `src/shared/`: - `src/shared/evidence-link.ts` — `EvidenceLink`, `EvidenceLink.status` enum per SharedContracts §2.4, `EvidenceLink.relation` enum per §2.5, `EvidenceTarget` generic shape - `src/shared/evidence-set.ts` — `EvidenceSet` with `activeEvidenceItemId` No services. Pure shapes. Add a unit test asserting that the union of all enum values matches the `SharedContracts.md` lists exactly — if someone adds a value without updating the doc, the test fails. --- ## T02 — Binding service + in-memory link repo + active-state machine ```task id: CE-WP-0003-T02 state_hub_task_id: f17e251d-4fd7-4ef3-b35c-e8e0dfb3a455 priority: high status: done depends_on: [T01] ``` Under `src/binder/`: - `repos/in-memory-links.ts` — Map-backed `EvidenceLinkRepository` - `services/bindings.ts` — `linkEvidenceToTarget`, `unlinkEvidence`, `listEvidenceForTarget`, `setActiveEvidence` - `state/active.ts` — a small machine tracking `(activeTarget, activeEvidenceItem, activeAnnotation)`. Exposed as a React context. Emit the events from SharedContracts §4 (`EvidenceLinkCreated`, `EvidenceItemActivated`, `FormFieldActivated`). --- ## T03 — Rect registry (the SharedContracts §7 contract) ```task id: CE-WP-0003-T03 state_hub_task_id: d3b853ef-7afe-491f-b40b-b6e980a23478 priority: critical status: done depends_on: [T02] ``` Implement under `src/binder/visual-guide/`: - `rect-registry.ts` — `RectRegistry` with `register`, `getRect`, `subscribe` per SharedContracts §7 - `react-hooks.ts` — `useRegisterRect(kind, id, ref)` for components to register a ref-derived rect - `events.ts` — registry emits `rect-changed` events on scroll/resize/focus/active-evidence-change (use ResizeObserver + IntersectionObserver + window resize + window scroll listeners) Unit tests: register a fake field, evidence card, and highlight; mutate their bounding rects; assert subscribers fire with the new rects. **This contract must not change after T03.** Three subsystems will depend on it in T05/T06/T07. --- ## T04 — Form schema + simple field renderer ```task id: CE-WP-0003-T04 state_hub_task_id: f42e1ecc-351c-4248-8872-1a25e79d3640 priority: medium status: done depends_on: [T01] ``` A deliberately minimal form schema lives in `src/app/forms/demo-schema.ts`: ```ts type FormFieldSchema = | { type: "text"; id: string; label: string } | { type: "textarea"; id: string; label: string } | { type: "date"; id: string; label: string }; ``` JSON Schema is **not** used yet — defer that to a later ADR. The MVP form just needs to render 3-4 fields and accept evidence links. - `src/binder/FormRenderer.tsx` renders the schema as a basic form (relocated from `src/work/` per `wiki/DependencyMap.md` §2/§5 — `work` cannot import `binder`, but FormRenderer needs `useRegisterRect` from `binder/visual-guide`. The "evidence-backed form" composition belongs in `binder/`; `app/` mounts both `work` panes and `binder` panes side-by-side.) - Each field registers itself with the rect registry as kind `"field"` with the field's `id` --- ## T05 — Side-by-side layout + link evidence to field ```task id: CE-WP-0003-T05 state_hub_task_id: 100fb1ca-6168-4e5d-9dc5-f051e6f9ff61 priority: high status: done depends_on: [T02, T04] ``` A new app route `/forms/demo` shows the side-by-side layout from Architecture §12.2: - Left: `FormRenderer` with a demo schema (3 fields) - Right: viewer (reusing `ViewerShell` from CE-WP-0002) - Bottom strip or popover: evidence list Linking interaction: click an evidence item, then click a field → link created. (Drag-and-drop is a polish item, not MVP.) Visual indication on linked fields (e.g. a chip showing the count of linked evidence items). --- ## T06 — Active-evidence cycling on a field ```task id: CE-WP-0003-T06 state_hub_task_id: e3bdf1d3-c7a1-484c-8895-8d103e7f9fe6 priority: high status: done depends_on: [T05] ``` When a field is focused: 1. Binder loads the field's evidence set. 2. The first evidence item becomes active. 3. The viewer scrolls to and highlights its annotation. 4. Keyboard `Tab`/`Shift-Tab` within the field's evidence chips cycles active evidence; viewer scrolls accordingly. 5. The evidence sidebar highlights the active evidence card. Each evidence card registers itself with the rect registry as `"evidence-card"`. --- ## T07 — SVG visual guide overlay ```task id: CE-WP-0003-T07 state_hub_task_id: e2ec50be-d9c5-47dd-b801-9c1afb01e6fd priority: high status: done depends_on: [T03, T06] ``` Implement `src/binder/visual-guide/Overlay.tsx`: - Single absolutely-positioned SVG covering the viewport - Subscribes to the rect registry - On every change, redraws two curves: `field → evidence-card` and `evidence-card → highlight` - Active-only — only the currently active triple gets drawn - Throttled to animation frames Acceptance: scroll the viewer, resize the window, change active evidence — the guide tracks every change without visible lag. The viewer adapter from CE-WP-0002 must expose `getHighlightClientRects(annotationId)` so the highlight's rect can be registered. --- ## T08 — E2E test of PRD scenario steps 5-9 ```task id: CE-WP-0003-T08 state_hub_task_id: e6754c8e-f9e2-435a-af28-31a693c6d9a8 priority: high status: done depends_on: [T05, T07] ``` Extend the Playwright E2E from CE-WP-0002-T09: 5. Navigate to `/forms/demo`. 6. Link the previously-created evidence item to the "summary" field. 7. Click the "summary" field. 8. Assert the field, the evidence card, and the highlight all have an `aria-current="true"` (or equivalent active marker). 9. Assert the SVG overlay contains exactly two `` elements (one field→card, one card→highlight). 10. Scroll the viewer; assert the SVG paths' endpoints update within the next animation frame. If this passes, the form-binding slice is complete and CE-WP-0004 may run in parallel with any deferred polish work.