The Record is one dataset. What changes is how counsel looks at it — and that way of looking is a thing you can name, save, and come back to. The GitHub-Projects model, worded for lawyers.
A view = columns + grouping + row order + filters, saved under a name. Narrowing the register is not a mode you fall out of; it is a draft of a view, and the register offers to keep it.
There were two registers, and the features this Story asked for were already built — in the wrong one.
documents/register | shared/register | |
|---|---|---|
| Wired to a route | Yes — /matters/[id]/record | No — stories only |
| Data | Real API + RSC bootstrap | fixtures.ts |
| Saved views | Server-persisted (document_saved_views) | — |
| Filter bar / peek / arriving strip / pan | Missing | All present |
The prototype's own header said it: "Prototype gate: mock data only …
live wiring is founder-gated." It had zero importers.
Its filter bar rendered its placeholder as a <span> — it
could never be typed into. This is the exact failure AGENTS.md names: done at
the component boundary, never at the system boundary.
Fixtures lie in your favour.
| Conflict | Frame said | Shipped |
|---|---|---|
| Side tokens | 3 (Client's / Opponent's / Other) | All four — the backend enum has ours | opposing | court | others, and a court order is not "Other". The frame's three predate the fourth. |
| Arrival stages | Received → Scanned → Read → On the record | All four, but each pinned to a real pipeline signal. No stage is animated on a timer. A progress bar that lies is worse than none. |
Rule applied: the frame loses to the live contract.
lib/documents/register/filters.ts — the filter bar's algebra.
One box, no query language.
side:ours, get the same chip the typeahead makes.Side: Ours + Side: Opposing would match nothing and the register
would go permanently blank. There is a test named for exactly this.
The saved-view contract already carried an opaque filters jsonb
(it was holding the slice value and the hierarchy toggle). The filter bar's
query + chips ride the same door.
viewStateToFilters(view) → { hierarchy, sliceValue?, query?, chips? }
So "a view = columns + grouping + order + filters" is true end-to-end, persisted server-side, per matter, shared with the team — and the Zustand fallback the brief allowed for was never needed. A malformed chip from an older client is dropped, not fatal: a saved view must always open.
ours → opposing → court → others, not alphabetical. Counsel reads a record as a confrontation: our own case first, then what we answer, then the bench. Alphabetical opened on "court", an order nobody asked for.sliceBy was tags, which pinned the slice rail open on every first paint. It is now null, so the rail appears only while slicing — as the frame says.| Surface | Note |
|---|---|
| Filter bar + Narrow-by | A real input, with typeahead, chips, "N of M", / hint. Narrow-by shows only while already filtering — a quiet register gets no extra chrome. |
| Header | 9 documents · 14.2 MB + the amber N need attention chip. Attention = needs_review | unreadable | failed only. A still-processing row is not attention, it is patience; a chip that always shows is a chip nobody reads. |
| Arriving strip | Optimistic upload rows above the settled record. A row leaves the strip the moment its documentId appears in the corpus — the only honest definition of "on the record". |
| Side peek | Slides over the table on space. Shows what the row actually carries; the frame's summary/provenance/notes do not exist on the corpus contract, so it links to the permalink (Story-2294) where that dossier already lives — rather than rendering invented placeholders. |
| Horizontal pan | Table exceeds its container; select + format + Document name stay pinned. Losing which document a row is while reading its far-right columns is the failure this prevents. |
| Row hover cluster | Preview · copy permalink · download · details. Stays mounted and fades in, so the row never reflows under the pointer. |
| Keyboard | j/k · space · enter/o · / · v · esc. Suppressed inside inputs, so typing into the filter box does not page the register underneath the caret. |
The Import drawer and the register are siblings under the matter shell; the
drawer owns the upload queue. The in-flight items cross through a React
context the shell hosts (matter-arrivals.tsx).
Deliberately not HR-23 Zustand: in-flight uploads are ephemeral and must never persist. A rehydrated "arriving" row after a reload would be a ghost document that never lands. The store is for client state worth keeping; this is client state worth forgetting.
The Story-2281 family collapse (parent_document_id) is untouched:
the bundle filter still runs before the engine, so group summaries and
slice counts describe what counsel is actually looking at. The filter bar was
inserted at the same point in the pipeline, for the same reason.
pnpm lint (0 errors) · pnpm test 1151 passed
· pnpm build · pnpm storybook:check ·
pnpm check:routes (38 surfaces fresh). i18n parity en/hi verified
programmatically. No colour literals in the diff.
68 file(s), +2598 / -2820 lines
| Path | Role | + | - |
|---|---|---|---|
| messages/en.json | Config | +55 | -2 |
| messages/hi.json | Config | +55 | -2 |
| src/components/documents/register/presentation.ts | Component | +22 | -0 |
| src/components/documents/register/register-arriving-strip.stories.tsx | Component | +104 | -0 |
| src/components/documents/register/register-arriving-strip.tsx | Component | +152 | -0 |
| src/components/documents/register/register-document-peek.stories.tsx | Component | +76 | -0 |
| src/components/documents/register/register-document-peek.tsx | Component | +221 | -0 |
| src/components/documents/register/register-document-row.tsx | Component | +97 | -16 |
| src/components/documents/register/register-filter-bar.stories.tsx | Component | +130 | -0 |
| src/components/documents/register/register-filter-bar.tsx | Component | +288 | -0 |
| src/components/documents/register/register-header.stories.tsx | Component | +36 | -0 |
| src/components/documents/register/register-header.tsx | Component | +60 | -0 |
| src/components/documents/register/register-table.tsx | Component | +55 | -12 |
| src/components/documents/register/register-view.tsx | Component | +146 | -23 |
| src/components/documents/register/saved-views-register.tsx | Component | +48 | -11 |
| src/components/documents/register/use-register-cursor.ts | Component | +54 | -0 |
| src/components/documents/register/use-register-keyboard.ts | Component | +104 | -0 |
| src/components/matters/import-into-matter-sheet-v2.tsx | Component | +7 | -1 |
| src/components/matters/live-matter-documents-page.tsx | Component | +24 | -7 |
| src/components/matters/matter-arrivals.tsx | Component | +98 | -0 |
| src/components/matters/matter-intake-drawer.tsx | Component | +32 | -2 |
| src/components/matters/matter-shell-chrome.tsx | Component | +10 | -4 |
| src/components/matters/use-matter-import-v2.ts | Component | +1 | -0 |
| src/components/shared/register/arriving-row.stories.tsx | Component | +0 | -53 |
| src/components/shared/register/arriving-row.tsx | Component | +0 | -100 |
| src/components/shared/register/badges.stories.tsx | Component | +0 | -67 |
| src/components/shared/register/badges.tsx | Component | +0 | -137 |
| src/components/shared/register/copy.ts | Component | +0 | -175 |
| src/components/shared/register/document-peek.stories.tsx | Component | +0 | -52 |
| src/components/shared/register/document-peek.tsx | Component | +0 | -163 |
| src/components/shared/register/filter-bar.stories.tsx | Component | +0 | -58 |
| src/components/shared/register/filter-bar.tsx | Component | +0 | -162 |
| src/components/shared/register/fixtures.ts | Component | +0 | -216 |
| src/components/shared/register/grid.ts | Component | +0 | -11 |
| src/components/shared/register/grouping.ts | Component | +0 | -25 |
| src/components/shared/register/modified-view-bar.stories.tsx | Component | +0 | -42 |
| src/components/shared/register/modified-view-bar.tsx | Component | +0 | -72 |
| src/components/shared/register/presentation.ts | Component | +0 | -83 |
| src/components/shared/register/record-empty-state.stories.tsx | Component | +0 | -18 |
| src/components/shared/register/record-empty-state.tsx | Component | +0 | -26 |
| src/components/shared/register/register-group-header.tsx | Component | +0 | -50 |
| src/components/shared/register/register-header.tsx | Component | +0 | -32 |
| src/components/shared/register/register-row.stories.tsx | Component | +0 | -50 |
| src/components/shared/register/register-row.tsx | Component | +0 | -166 |
| src/components/shared/register/register-table.stories.tsx | Component | +0 | -23 |
| src/components/shared/register/register-table.tsx | Component | +0 | -123 |
| src/components/shared/register/register-view.stories.tsx | Component | +0 | -109 |
| src/components/shared/register/register-view.tsx | Component | +0 | -182 |
| src/components/shared/register/tag-browse-rail.stories.tsx | Component | +0 | -20 |
| src/components/shared/register/tag-browse-rail.tsx | Component | +0 | -114 |
| src/components/shared/register/types.ts | Component | +0 | -90 |
| src/components/shared/register/use-register-keyboard.ts | Component | +0 | -77 |
| src/components/shared/register/view-menu.stories.tsx | Component | +0 | -23 |
| src/components/shared/register/view-menu.tsx | Component | +0 | -117 |
| src/components/shared/register/view-tabs.stories.tsx | Component | +0 | -23 |
| src/components/shared/register/view-tabs.tsx | Component | +0 | -66 |
| src/lib/documents/register/arrivals.ts | Library | +86 | -0 |
| src/lib/documents/register/filters.test.ts | Library | +219 | -0 |
| src/lib/documents/register/filters.ts | Library | +202 | -0 |
| src/lib/documents/register/index.ts | Library | +23 | -0 |
| src/lib/documents/register/saved-view.test.ts | Library | +46 | -0 |
| src/lib/documents/register/saved-view.ts | Library | +36 | -2 |
| src/lib/documents/register/types.ts | Library | +10 | -0 |
| src/lib/documents/register/view-edits.test.ts | Library | +3 | -2 |
| src/lib/documents/register/view-edits.ts | Library | +36 | -0 |
| src/lib/documents/register/view-mutations.test.ts | Library | +2 | -0 |
| src/lib/documents/register/view-state.test.ts | Library | +6 | -2 |
| src/lib/documents/register/view-state.ts | Library | +54 | -9 |