feat(timeline): med-mal chronology view backed by event log
From the PR description
Summary
Phase 3 of the med-mal records platform. Adds a Timeline view at /projects/[id]/timeline/[docId] that surfaces the Phase 2 document_events log as a fast, citation-anchored chronology - satisfying the roadmap §Phase 3 verification gate ("+ Medical Chronology on a fully-extracted document takes < 2s, no LLM call; non-extracted docs fall back to existing flow").
- New route reads the existing
GET /extraction/:documentId/events; zero backend changes, no migrations. + Medical ChronologyonProjectPageroutes to Timeline when ≥1 PDF is ready: single PDF → direct, multiple → picker modal, zero PDFs → falls back to the existing tabular-create modal.MedMalDocumentEventwidened to exposeprovider,provider_role,event_time,episode_of_care,key_date_role,event_date_text(the backend already returns these viaselect *; the frontend type just hadn't kept up).- Defense-in-depth:
mental_health_144_293records hidden by default with a header toggle, even though the backend already filterspeer_review_145_64server-side.
Why Rail-A-only
Earlier drafts of docs/PLAN_med_mal_phase3_integration.md included a "Rail B" - tag schemas as eventLogBacked and populate tabular_cells from event-log SQL. Reading the actual MAR schema during planning surfaced that every event-log-backed schema is per-encounter (one row per medication administration / vital sample / line item), while tabular_cells is keyed (review, doc, column) - one row per doc. Collapsing N encounters into a single cell loses sort/filter/per-row inspection - i.e., it isn't really a tabular review anymore.
Rail-A-only ships the chronology Timeline as a separate surface and leaves tabular reviews for what they're good at (per-doc summaries). MAR / vitals / labs views are deferred as Phase 3.5 follow-ups - same Timeline pattern, different lens.
Test plan
- Manual e2e on an extracted med-mal doc - agent did not run this; needs a running stack + a project with
document_extractions.status='complete'.- Click
+ Medical Chronologyon an extracted doc → Timeline route loads in <2s (browser devtools network panel). - No
/chat/*or/tabular-review/*/generatetraffic during load. - Click an event row → PDF preview scrolls to
source_page, bbox highlight overlays the citation. - Toggle "Show § 144.293 records" header checkbox → mental-health events appear/disappear.
- Click
- Manual e2e on a non-extracted doc -
+ Medical Chronologyeither falls back to the tabular modal (zero PDFs) or routes to a Timeline that shows the "No completed extraction" empty state with a link to/projects/[id]/extraction. - Backend
npm run buildclean (untouched). - Frontend
npm run buildclean; new route listed (/projects/[id]/timeline/[docId]). - Frontend
npm run lintclean for the new file. (Pre-existing lint errors in unrelated files are out of scope.)
Our analysis
Ship a chronology timeline view as Phase 3 of med-mal extraction — read the full analysis →
Think the analysis missed something the PR description covers?
Capture this PR into my fork
Download a Markdown prompt that tells Claude how to port every
commit in this PR into your working tree. Run it via
claude -p < capture-pull-3.md from
inside the repo you want the changes in.