T01 EvidenceLink/EvidenceSet types
- src/shared/evidence-link.ts: status (§2.4), relation (§2.5), target
- src/shared/evidence-set.ts: ordered group + activeEvidenceItemId
- enum-conformance test parses SharedContracts.md and asserts the
runtime lists match exactly
T02 Binding service + in-memory link repo + active-state machine
- src/binder/repos/in-memory-links.ts: Map-backed EvidenceLinkRepository
- src/binder/services/bindings.ts: link/unlink/list/update/setActive
emitting §4 EvidenceLinkCreated / EvidenceLinkUpdated /
EvidenceItemActivated
- src/binder/state/active.ts: (target, evidence, annotation) reducer
+ ActiveStateProvider + useActiveState hook
- extended engine/events/types.ts with EvidenceLinkCreated,
EvidenceLinkUpdated, FormFieldActivated payloads
T03 Rect registry (SharedContracts §7 — contract FROZEN)
- src/binder/visual-guide/rect-registry.ts: register/getRect/subscribe
+ invalidate + getVersion for useSyncExternalStore
- events.ts: scroll/resize/focus pumps via window + ResizeObserver +
IntersectionObserver, rAF-throttled
- react-hooks.ts: RectRegistryProvider, useRegisterRect(kind,id,ref),
useRectRegistryVersion
T04 Form schema + renderer
- src/app/forms/demo-schema.ts: text/textarea/date minimal schema
- src/binder/FormRenderer.tsx: renders schema, each field registers
as rect kind="field"; active field gets aria-current="true"
- placed in binder/ (not work/) because work cannot import binder per
DependencyMap.md §2 and the renderer needs the rect-registry hook;
workplan T04 was amended in-place to document this
T05 Side-by-side Forms layout + click-to-link
- src/app/forms/FormsApp.tsx + src/app/App.tsx top-bar router with
hash route #/forms/demo
- BinderProvider mounted at app root so links survive tab switching
- stage-evidence-then-click-field linking interaction with banner
+ per-field link-count chip
T06 Active-evidence cycling
- src/app/forms/ActiveEvidenceChips.tsx: chips per active target,
Tab cycles natively, first chip auto-activates on field focus,
each chip registers as rect kind="evidence-card"
- ScrollBridge in FormsApp wires activeAnnotationId to viewer scroll
- EvidenceSidebar + EvidenceStrip highlight the active item via the
new useLastActivatedEvidence hook in work/EngineContext
T07 SVG visual-guide overlay
- src/binder/visual-guide/Overlay.tsx: single fixed-positioned SVG,
draws field→card and card→highlight bezier curves for the active
triple, rAF-throttled via the registry
- src/anchor exposes getHighlightClientRects(annotationId); the
spike viewer wraps highlights in [data-highlight-id] so the helper
can locate them
- src/app/forms/HighlightRectBridge.tsx: registers the active
annotation's rect via that helper
T08 End-to-end test (PRD scenario steps 5-9)
- tests/integration/forms-overlay-e2e.dom.test.tsx: full path from
Review-mode capture through Forms-mode link to active triple +
aria-current assertions + 2 SVG paths in the overlay
- additional integration coverage: forms-link-flow + forms-active-cycling
Gates: typecheck ✓ · lint ✓ · build ✓ · 152/152 tests across 21 files.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
7.9 KiB
id, type, title, domain, repo, repo_id, topic_slug, topic_id, state_hub_workstream_id, status, owner, created, updated, depends_on_workplan, spec_refs
| id | type | title | domain | repo | repo_id | topic_slug | topic_id | state_hub_workstream_id | status | owner | created | updated | depends_on_workplan | spec_refs | |||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| CE-WP-0003 | workplan | Form binding + visual guide — EvidenceLink, rect registry, SVG overlay | citation_evidence | citation-evidence | a677c189-b4e2-4f2a-9e48-faa482c277e6 | citation_evidence_mvp | 96fa8e80-9f74-40f2-84cd-644e9747b9ec | 7b5b7235-57e3-4835-8fa6-376bb518fe2d | done | Bernd | 2026-05-24 | 2026-05-25 | CE-WP-0002 |
|
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:
- Open a form next to the document viewer.
- Drag (or click-to-link) an evidence item from the sidebar onto a form field.
- 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.
- 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
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.statusenum per SharedContracts §2.4,EvidenceLink.relationenum per §2.5,EvidenceTargetgeneric shapesrc/shared/evidence-set.ts—EvidenceSetwithactiveEvidenceItemId
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
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-backedEvidenceLinkRepositoryservices/bindings.ts—linkEvidenceToTarget,unlinkEvidence,listEvidenceForTarget,setActiveEvidencestate/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)
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—RectRegistrywithregister,getRect,subscribeper SharedContracts §7react-hooks.ts—useRegisterRect(kind, id, ref)for components to register a ref-derived rectevents.ts— registry emitsrect-changedevents 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
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:
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.tsxrenders the schema as a basic form (relocated fromsrc/work/perwiki/DependencyMap.md§2/§5 —workcannot importbinder, but FormRenderer needsuseRegisterRectfrombinder/visual-guide. The "evidence-backed form" composition belongs inbinder/;app/mounts bothworkpanes andbinderpanes side-by-side.)- Each field registers itself with the rect registry as kind
"field"with the field'sid
T05 — Side-by-side layout + link evidence to field
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:
FormRendererwith a demo schema (3 fields) - Right: viewer (reusing
ViewerShellfrom 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
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:
- Binder loads the field's evidence set.
- The first evidence item becomes active.
- The viewer scrolls to and highlights its annotation.
- Keyboard
Tab/Shift-Tabwithin the field's evidence chips cycles active evidence; viewer scrolls accordingly. - 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
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-cardandevidence-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
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:
- Navigate to
/forms/demo. - Link the previously-created evidence item to the "summary" field.
- Click the "summary" field.
- Assert the field, the evidence card, and the highlight all have an
aria-current="true"(or equivalent active marker). - Assert the SVG overlay contains exactly two
<path>elements (one field→card, one card→highlight). - 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.