PR: #379: feat(matter-detail): rebuild matter nav chrome to locked IA [Story-1490] by @ankitmaheshwari-com
View PR →
Story 1490 · Matter Detail · MATTER-NAV-CHROME

Three navigations, one matter

The matter page had drifted: a stale 7-tab subnav, a 10-icon right rail that tried to own every verb, and an Inbox crammed into a 44px strip where a daily work queue cannot live. This Story rebuilds the matter page's chrome to the locked IA of 2026-06-20 — three proper-named navigations, each with one job: the Matter Ribbon (know + reach), the Matter Worknav (choose surface + scope), and the Matter Action Rail (persistent tools). No chrome duplication. It is the foundation every Work Mode body builds on. The Worknav follows the Option-B layer order (Triage → Think → Evidence → Act), and the document Work Mode is renamed Record — the Evidentiary Record — with its route moved to /matters/[id]/record.

🛰️ Satellite — why a matter page needs three navigations, not one tab strip

A litigator's matter is three different questions at once, and a single tab strip answers none of them well. "What matter am I in and how urgent is it?" is identity — it changes nothing. "What facet of the work am I reading, and through which proceeding?" is surface + scope — it changes what's on screen. "What fast action do I fire?" is a verb — it opens a tool. Fold all three into one row and every control fights for the same space; the Inbox — the assigned counsel's actual daily queue of agent proposals, routed emails, and unassigned docs — gets exiled to a rail icon with a tiny badge, where it can never have the list + preview + filters it needs.

The naive fix is "add more tabs / more rail icons." That is exactly the drift this Story reverses: it is how you end up with a 10-icon rail nobody can scan and an Inbox that is structurally too small to use. The locked answer is separation by role: Ribbon = know + reach, Worknav = choose surface + scope, Action Rail = persistent tools. Each region owns one verb, so nothing duplicates and the Inbox gets the full body it always needed.

✈️ Airplane — the one enum that runs the Worknav

The Inbox is the trickiest call. It is not a Work Mode (those are facets of the matter's substance — Brief, Documents, Proof…); it is a queue of "what needs me," a different category entirely. But it also must not be a rail panel, a notification drawer, or an agent-panel subview — those are all too small and break the single-source rule. So it becomes a Triage Mode: leftmost in the Worknav, behind a divider, carrying the pending count badge — modeled as one more value in a single active-mode enum.

WorknavMode = "inbox" | "brief" | "strategy" | "record"
            | "chronology" | "proof" | "filings" | "hearings"

Option-B layers (visible separators between each):
  Inbox ┃ Brief·Strategy ┃ Record·Chronology·Proof ┃ Filings·Hearings
  Triage ┃   Think        ┃      Evidence            ┃     Act

The one analogy that holds all the way down: the Proceeding Lens is a camera, and each mode tells it how to behave. On Triage (Inbox) the camera is off — the view is always matter-wide, so the lens chip is hidden (a live scope chip that does nothing is a lie); per-item proceeding chips carry scope instead. On Switch modes (Brief / Strategy) the camera swaps lenses — you get the proceeding-specific brief, a distinct artifact, not a filtered one. On Narrow modes (Documents…Hearings) the camera zooms — same body, filtered to the scoped proceeding, with a crimson scope strip and a one-click reset.

🔬 Microscope — the seams that make it real

Region per file (storyable in isolation)

The header splits into matter-ribbon.tsx (row 1) and matter-worknav.tsx (row 2), composed by matter-detail-header.tsx. WHY split: each navigation is a region with its own locked lexicon and its own states, so each gets its own Storybook file and stays small — the header is now a thin sticky wrapper, not a 150-line do-everything client.

The Worknav enum + lens taxonomy (presentation, not the client)

presentation.ts exposes WORKNAV_MODES (the ordered list, each tagged triage or work, divider after Inbox) and LENS_TYPE (the triage/switch/narrow map). WHY in presentation: AGENTS.md keeps mode order, class tags, and lens behavior out of the client file — the Worknav just renders the map and decides "is the lens hidden?" from LENS_TYPE[activeMode] === "triage".

The lens chip's three scope treatments

When shown (Switch + Narrow), the lens chip renders one of three token-backed treatments: main (default Main-arbitration scope, plain), scoped (a named proceeding — crimson border-left + crimson dot), wide (explicit Matter-wide aggregate — dashed). WHY crimson only for scoped: crimson is a status hue (design system), so it marks the one state that narrows what counsel sees — a proceeding filter is active and must be visible at a glance.

The Action Rail drops to four

RailIconId collapses from ten ids to intake | notes | audit | settings — Inbox left for the Worknav, and the redundant mark-filed / log-call / mira / marginalia / parties verbs go with it. Intake is the polymorphic Add (the +); admin is renamed settings and still opens the Matter administration panel. WHY four: the rail owns only always-available matter tools; mode-local actions (Inbox triage chips, Proof open-links) live inside the active work surface, not the rail.

Record ≠ documents — rename the container, not the contents

The document Work Mode becomes Record (tab label) and the page is the Evidentiary Record (heading / empty state). The route moves /matters/[id]/documents → /matters/[id]/record (old path deleted, no back-compat redirect — proxy.ts localizes bare paths and there are no external links). The Worknav Record mode navigates there; internal links (revalidate, business-action route template, page-help map, gateway route tags, the legacy sub-nav) all repoint. WHY only the container: the items inside the Record are documents — so the documents DB table, the document_type catalog, the register's "Document Type" column, and every backend /app/.../documents path and S3 key stay exactly as they are. Record is the shelf; documents are the books.

Scope boundary. This Story ships the chrome — Ribbon, Worknav, Action Rail — the route-mode enum, and the Record rename + route move. The Evidentiary Record register content is owned by Story-1527 (merged): this Story mounts it at the new /record route and does not rework it. Other Work Modes render a placeholder body. The landing rule reads role + pending count — neither is in the payload yet, so the shell defaults to Brief (the documented safe landing) with an inbox count of 0; wiring real role/pending data is the follow-up. The /record route still wears the legacy matter chrome (sub-nav) under Story-1527's mount; unifying it under this locked-IA chrome is a tracked follow-up.

What shipped

Acceptance criterionWhere
Matter Ribbon — 5 locked elements + practice areamatter-ribbon.tsx (crumb · title · practice area · Urgency Pill · ⌘K search · Agent Capsule)done
Matter Worknav — 8-mode enum, Option-B layers (Inbox ┃ Brief·Strategy ┃ Record·Chronology·Proof ┃ Filings·Hearings) with visible separatorsmatter-worknav.tsx + WORKNAV_LAYERSdone
Record rename + route move /documents→/record (no redirect); links repointed; backend untouchedroute dir record/, WorkMode record, matter-subnav, gateway/route-template/page-help/revalidate; "Evidentiary Record" heading + empty statedone
Single active-mode enum (Inbox is a route mode, not a panel)WorknavMode in types.tsdone
Proceeding Lens — hidden on Inbox; main / scoped / wide treatmentsMatterWorknav lensScope + LENS_TYPEdone
Action Rail = 4 icons (Intake · Notes · Audit · Settings)rail-icon-strip.tsx, RailIconId, matter-right-rail.tsxdone
Lens taxonomy wired (Triage / Switch / Narrow)LENS_TYPE map drives lens-hidden + chip variantsdone
Storybook per region (Ribbon · Worknav · Rail, 4 icons)matter-ribbon.stories, matter-worknav.stories, rail-icon-strip.stories, matter-detail-header.storiesdone
i18n (en + hi) · tokens · a11yMatterDetail.* both locales; semantic tokens only; ARIA-labelled tabs / rail buttonsdone
Evidentiary Record register contentowned by Story-1527 (merged) — mounted at /record; not reworked hereout of scope

Verify: pnpm lint (0 errors), tsc --noEmit ✓, pnpm test (matter-detail suites pass), pnpm storybook:check ✓ (drift + build), i18n en/hi parity ✓, color-literal scan clean.

Files changed

27 file(s), +629 / -276 lines

Path Role + -
messages/en.jsonConfig+10-11
messages/hi.jsonConfig+10-11
src/app/[locale]/(workspace)/matters/[matterId]/{documents => record}/loading.tsxApp route+0-0
src/app/[locale]/(workspace)/matters/[matterId]/{documents => record}/page.tsxApp route+0-0
src/app/[locale]/(workspace)/matters/actions.tsApp route+1-1
src/components/documents/register/saved-views-register.stories.tsxComponent+1-1
src/components/documents/register/saved-views-register.tsxComponent+1-1
src/components/matter-detail/matter-detail-header.stories.tsxComponent+27-17
src/components/matter-detail/matter-detail-header.tsxComponent+38-124
src/components/matter-detail/matter-ribbon.stories.tsxComponent+47-0
src/components/matter-detail/matter-ribbon.tsxComponent+91-0
src/components/matter-detail/matter-right-rail.stories.tsxComponent+1-3
src/components/matter-detail/matter-right-rail.tsxComponent+8-10
src/components/matter-detail/matter-worknav.stories.tsxComponent+61-0
src/components/matter-detail/matter-worknav.tsxComponent+118-0
src/components/matter-detail/presentation.tsComponent+60-7
src/components/matter-detail/rail-icon-strip.stories.tsxComponent+29-0
src/components/matter-detail/rail-icon-strip.tsxComponent+18-41
src/components/matter-detail/types.tsComponent+47-20
src/components/matters/live-matter-documents-page.tsxComponent+3-0
src/components/matters/live-matter-shell.tsxComponent+43-16
src/components/matters/matter-subnav.tsxComponent+6-4
src/lib/kb/page-help-map.tsLibrary+1-1
src/lib/matters/business-actions.tsLibrary+1-1
src/lib/matters/gateway.tsLibrary+4-4
tests/unit/lib/business-action-log.test.tsTest+2-2
tests/unit/matter-documents-route.test.tsxTest+1-1