The section surface (app#568) and the CCB chassis (app#569) shipped the substrate. This ticket gives it the shape counsel actually reads in: one slim strip, the document flanked by two gutters, and a dossier where the machine keeps everything it knows.
Two voices, two gutters, one dossier. The machine speaks in the LEFT margin, the team speaks in the RIGHT one, and neither ever writes on the paper. Everything the machine knows about the document — its read, its findings, its outline, its identity, its history — lives in one place: the Insights lens.
The permalink used to spend a doc ribbon (64px), a Download/Permalink/Details sub-ribbon (56px) and a viewer toolbar (44px) before the document began — plus a 340px "What matters here" sidebar taking the width. All of it is gone.
| Was | Is |
|---|---|
| Doc ribbon + sub-ribbon + toolbar (~164px) | ONE 46px strip: name · state · pages · lens tabs · one clustered control · ⋮ |
| "Details" button → right-edge sheet | No button. Details is a block in the Insights dossier. |
| 340px key-facts sidebar | Gone. "What matters here" is a dossier block. |
| Section outline aside beside the page | Gone. The outline is a dossier block; a row lands on Original AT that section. |
| Gutter popover over the page | Banned. The finding cards expand IN the left gutter. |
| Floating counsel bar on the paragraph | Gone. The selection publishes to the CCB, which already lives in the shell. |
| "Machine copy" / "PROCESSED" | "Text" / the ADR-128 words: PROCESSING · INDEXED · REGISTERED. |
An annotation the machine proposed is a finding: it is the machine asking counsel to rule on something, so it rides the LEFT gutter with the curation the substrate can actually record (Keep / Dismiss, optimistic). The moment counsel keeps it, it stops being a finding and becomes a note — AGENT-badged, in the RIGHT gutter, with the team's. A brief punch card is an insight: real, span-pinned, but with no dismissal contract upstream, so its card offers only its door into Insights, never a button that would lie about being recorded.
proposed agent annotation -> LEFT (contradiction, curatable) brief punch card -> LEFT (insight, read-only) counsel note -> RIGHT (conversation) kept agent note -> RIGHT (conversation, AGENT badge) dismissed -> the auditable dismissed index (never deleted)
This is buildSectionGutters in
sections-presentation.ts, and it is why the CCB's
contradiction verb — which had no producer until now — finally
fires.
| Concern | File |
|---|---|
| The two-voice split, by range containment (D10) | viewer/sections-presentation.ts (buildSectionGutters, sectionSignals, firstBlockOfSection) |
| The slim strip + its ADR-128 state vocabulary | viewer/document-strip.tsx, viewer/strip-presentation.ts |
| Mirrored rails on the page | viewer/section-rails.tsx |
| Left gutter: pills → expand-in-gutter cards | viewer/section-machine-gutter.tsx, viewer/section-finding-card.tsx, viewer/section-gutter-marks.tsx |
| Right gutter: note stack, collapse group, composer | viewer/section-note-stack.tsx, viewer/document-note-composer.tsx |
| The Original lens (page + rails + both gutters) | viewer/document-original-lens.tsx, viewer/document-original-pane.tsx |
| The Insights dossier | viewer/document-insights-dossier.tsx + viewer/insights/dossier-*.tsx |
| Focus mode + page/zoom seam | viewer/reading-mode-context.tsx, viewer/focus-exit-chip.tsx, viewer/document-permalink-shell.tsx |
| The dossier's Audit block (new seam) | lib/audit/gateway.ts → api/internal/audit/matters/[matterId]/documents/[documentId]/route.ts → useAppDocumentAuditTimeline |
Ruling 1 says the matter shell (ribbon, work nav, rails) is untouched, and only
focus mode may hide it at runtime. So focus mode edits nothing: the
viewer takes the frame (fixed inset-0) while it is on, retracts
both gutters, and gives the frame back on Esc. The shell is still
mounted and none of its components know this happened. The CCB stays — reading
is not silence — and a slim hover edge at the top brings the strip back without
leaving focus, so counsel can turn a page and keep reading.
"+ Add a note" can be pressed without dragging a passage. The composer then
anchors to the section's first block — its real extracted
text, from the same run the note will be byte-verified against
(firstBlockOfSection). When no block resolves, the affordance is
withheld. A synthesized quote would be a span the record cannot
verify, and provenance-is-law does not bend for a UI convenience.
document_card.v1 rides an
event into the matter's context bundle, and no endpoint serves it back. So the
block renders the brief's punch cards (lead · consequence ·
span pin) — the machine's real, pinned points — rather than a fabricated fact
table. A fake row wearing a real pin would be the worst of both. When a card
read API lands, this block swaps to it; the frame's label/value shape is the
target.
pnpm lint (0 errors),
pnpm test (1130 passed, incl. new
buildSectionGutters / toStripStatus /
firstBlockOfSection suites), pnpm build,
pnpm check:routes (38 surfaces fresh),
pnpm storybook:check. Every new component ships a co-located
story, and the animated ones (gutter expand/collapse, note-group open/fold,
focus chip) ship an interactive story that demonstrates enter AND exit inside
AnimatePresence.
81 file(s), +3927 / -3537 lines
| Path | Role | + | - |
|---|---|---|---|
| messages/en.json | Config | +43 | -53 |
| messages/hi.json | Config | +45 | -55 |
| openapi/app-api.json | Config | +1 | -1 |
| src/app/[locale]/(workspace)/matters/[matterId]/record/doc/[documentId]/insights/page.tsx | App route | +47 | -13 |
| src/app/[locale]/(workspace)/matters/[matterId]/record/doc/[documentId]/layout.tsx | App route | +28 | -55 |
| src/app/[locale]/(workspace)/matters/[matterId]/record/doc/[documentId]/page.tsx | App route | +14 | -9 |
| src/app/api/internal/audit/matters/[matterId]/documents/[documentId]/route.ts | App route | +46 | -0 |
| src/components/documents/ccb/ccb-presentation.ts | Component | +4 | -2 |
| src/components/documents/viewer/agent-note-card.tsx | Component | +0 | -46 |
| src/components/documents/viewer/citation-landing-pill.tsx | Component | +0 | -44 |
| src/components/documents/viewer/document-badges.tsx | Component | +1 | -66 |
| src/components/documents/viewer/document-control-bar.stories.tsx | Component | +0 | -48 |
| src/components/documents/viewer/document-counsel-bar.stories.tsx | Component | +0 | -36 |
| src/components/documents/viewer/document-counsel-bar.tsx | Component | +0 | -82 |
| src/components/documents/viewer/document-details-sheet.stories.tsx | Component | +0 | -51 |
| src/components/documents/viewer/document-details-sheet.tsx | Component | +0 | -165 |
| src/components/documents/viewer/document-fact-rail.stories.tsx | Component | +0 | -77 |
| src/components/documents/viewer/document-fact-rail.tsx | Component | +0 | -166 |
| src/components/documents/viewer/document-fact-row.tsx | Component | +0 | -88 |
| src/components/documents/viewer/document-gutter-mark.tsx | Component | +0 | -60 |
| src/components/documents/viewer/document-insights-dossier.stories.tsx | Component | +171 | -0 |
| src/components/documents/viewer/document-insights-dossier.tsx | Component | +100 | -0 |
| src/components/documents/viewer/document-insights-live.tsx | Component | +152 | -9 |
| src/components/documents/viewer/document-insights-view.stories.tsx | Component | +0 | -36 |
| src/components/documents/viewer/document-insights-view.tsx | Component | +0 | -174 |
| src/components/documents/viewer/document-machine-copy-view.tsx | Component | +1 | -6 |
| src/components/documents/viewer/document-margin-note.tsx | Component | +0 | -48 |
| src/components/documents/viewer/document-note-item.tsx | Component | +0 | -46 |
| src/components/documents/viewer/document-notes.stories.tsx | Component | +0 | -46 |
| src/components/documents/viewer/document-original-lens.tsx | Component | +265 | -0 |
| src/components/documents/viewer/document-original-pane.tsx | Component | +101 | -303 |
| src/components/documents/viewer/document-permalink-shell.stories.tsx | Component | +0 | -134 |
| src/components/documents/viewer/document-permalink-shell.tsx | Component | +152 | -218 |
| src/components/documents/viewer/document-processing-stages.tsx | Component | +0 | -63 |
| src/components/documents/viewer/document-reading-surface.tsx | Component | +0 | -309 |
| src/components/documents/viewer/document-strip.stories.tsx | Component | +110 | -0 |
| src/components/documents/viewer/document-strip.tsx | Component | +279 | -0 |
| src/components/documents/viewer/document-viewer-header.tsx | Component | +0 | -104 |
| src/components/documents/viewer/focus-exit-chip.stories.tsx | Component | +54 | -0 |
| src/components/documents/viewer/focus-exit-chip.tsx | Component | +40 | -0 |
| src/components/documents/viewer/insights/dossier-assertions.tsx | Component | +92 | -0 |
| src/components/documents/viewer/insights/dossier-audit.tsx | Component | +69 | -0 |
| src/components/documents/viewer/insights/dossier-block.tsx | Component | +64 | -0 |
| src/components/documents/viewer/insights/dossier-contradictions.tsx | Component | +85 | -0 |
| src/components/documents/viewer/insights/dossier-details.tsx | Component | +115 | -0 |
| src/components/documents/viewer/insights/dossier-notes.tsx | Component | +68 | -0 |
| src/components/documents/viewer/insights/dossier-outline.tsx | Component | +65 | -0 |
| src/components/documents/viewer/insights/dossier-read.tsx | Component | +48 | -0 |
| src/components/documents/viewer/insights/dossier-what-matters.tsx | Component | +55 | -0 |
| src/components/documents/viewer/live-presentation.ts | Component | +7 | -54 |
| src/components/documents/viewer/reading-mode-context.tsx | Component | +46 | -0 |
| src/components/documents/viewer/section-finding-card.stories.tsx | Component | +48 | -0 |
| src/components/documents/viewer/section-finding-card.tsx | Component | +101 | -0 |
| src/components/documents/viewer/section-fixtures.ts | Component | +45 | -42 |
| src/components/documents/viewer/section-gutter-marks.stories.tsx | Component | +45 | -0 |
| src/components/documents/viewer/section-gutter-marks.tsx | Component | +86 | -0 |
| src/components/documents/viewer/section-machine-gutter.stories.tsx | Component | +63 | -0 |
| src/components/documents/viewer/section-machine-gutter.tsx | Component | +100 | -0 |
| src/components/documents/viewer/section-mark-popover.stories.tsx | Component | +0 | -90 |
| src/components/documents/viewer/section-mark-popover.tsx | Component | +0 | -228 |
| src/components/documents/viewer/section-note-stack.stories.tsx | Component | +75 | -0 |
| src/components/documents/viewer/section-note-stack.tsx | Component | +219 | -0 |
| src/components/documents/viewer/section-rails.stories.tsx | Component | +75 | -0 |
| src/components/documents/viewer/section-rails.tsx | Component | +61 | -0 |
| src/components/documents/viewer/sections-presentation.test.ts | Component | +92 | -47 |
| src/components/documents/viewer/sections-presentation.ts | Component | +107 | -78 |
| src/components/documents/viewer/strip-presentation.test.ts | Component | +46 | -0 |
| src/components/documents/viewer/strip-presentation.ts | Component | +75 | -0 |
| src/components/documents/viewer/types.ts | Component | +13 | -70 |
| src/components/documents/viewer/use-document-gutters.ts | Component | +148 | -0 |
| src/components/documents/viewer/use-document-sections.ts | Component | +10 | -41 |
| src/components/documents/viewer/use-section-labels.ts | Component | +31 | -17 |
| src/components/documents/viewer/viewer-fixtures.tsx | Component | +0 | -183 |
| src/gen/schemas/bundleSection.json | Generated contract | +1 | -1 |
| src/gen/schemas/contextBundle.json | Generated contract | +1 | -1 |
| src/gen/schemas/documentCard.json | Generated contract | +1 | -1 |
| src/gen/types/DocumentCard.ts | Generated contract | +1 | -0 |
| src/gen/zod/documentCardSchema.ts | Generated contract | +74 | -67 |
| src/lib/audit/gateway.ts | Library | +26 | -0 |
| src/lib/kubb-client.ts | Library | +11 | -0 |
| src/lib/surface-registry/route-registry.ts | Library | +4 | -4 |