Back to Onyx

Mobile Chat 9a — Citations & Cited Sources — PR Roadmap

docs/mobile-chat/9a-citations/05-pr-roadmap.md

4.4.05.9 KB
Original Source

Status: active · Task: 9a-citations · Source plan: 04-implementation-plan.md

Mobile Chat 9a — Citations & Cited Sources — PR Roadmap

Overview

Single PR (owner decision at GATE 3 — ship 9a as one chunk rather than splitting the ~1150 LOC into two). It exceeds the ~500-700 target band but is one coherent vertical feature; the owner opted to review it together.

PRTitleEst. LOCDepends onKey deliverable
1feat(mobile): chat citations + cited sources~1050-1250Type + process citation/document packets; inline [N] (and answer links) open in the in-app browser; a "Sources" button + bottom sheet lists cited/found documents. Establishes the reusable processing + source-UI foundation for 9b.

Sequence (internal build order within the one PR)

1. contracts/documents.ts + streamingModels.ts   (types)
2. messageProcessor.ts                            (pure processor · FOUNDATION)
3. usePacketDisplay.ts + registry.ts              (host processor, `processed` channel · FOUNDATION)
4. openSource.ts + StreamingMarkdown + MessageTextRenderer   (inline tap-routing)
5. citations.ts + SourceIcon + SourceRow          (source layer · FOUNDATION)
6. CitedSources.tsx + MessageRow wiring           (Sources bar & sheet · 9a UI)
7. fixtures + tests

Build the pure/foundation layers before the UI that consumes them. 9b (agent timeline) later extends messageProcessor with grouping and reuses SearchDoc/SourceRow/SourceIcon/openSource.


PR 1 — feat(mobile): chat citations + cited sources

  • Goal: Ship the full 9a slice — process the citation/document packets already arriving on node.packets, make inline [N] markers (and answer links) tappable → in-app browser, and render a "Sources" button under completed answers that opens a bottom sheet listing the source documents.
  • Scope (in):
    • Packet contracts + types (SearchDoc, CitationInfo, document packets, MessageStart.final_documents).
    • The incremental messageProcessor (citationMap / citations[] / documentMap / completion), hosted in usePacketDisplayprocessed; MessageRendererProps carries processed.
    • openSource/openUrl + onLinkPress passthrough in StreamingMarkdown, wired in MessageTextRenderer.
    • selectSources selector; SourceIcon / SourceRow; CitedSourcesBar + CitedSourcesSheet; footer wiring in MessageRow.
    • Fixtures + unit/component tests.
  • Out of scope (deferred): inline chip components / hover cards (platform-blocked); turn/tab packet grouping + timeline steps (9b); a per-connector source-logo set; an in-app document preview for file sources (they degrade to a toast/no-op); a live mid-stream citation counter.
  • Files:
    FileNew/ModifiedSlice
    mobile/src/chat/contracts/documents.tsnewSearchDoc / StreamingCitation / CitationMap
    mobile/src/chat/streamingModels.tsmodified3 packet types + MessageStart.final_documents + ObjTypes
    mobile/src/chat/messageProcessor.tsnewProcessedMessageState, createInitialState, processPackets
    mobile/src/chat/openSource.tsnewdocumentTarget / openUrl / openSource
    mobile/src/chat/citations.tsnewselectSources + domainOf / faviconUrl
    mobile/src/hooks/usePacketDisplay.tsmodifiedhost processor; return { renderer, packets, processed }
    mobile/src/components/chat/renderers/registry.tsmodifiedMessageRendererProps → { packets, processed }
    mobile/src/components/chat/renderers/MessageTextRenderer.tsxmodifiedprocessed.isComplete; onLinkPress
    mobile/src/components/chat/StreamingMarkdown.tsxmodifiedonLinkPress passthrough
    mobile/src/components/chat/SourceIcon.tsxnewfavicon / file-text fallback
    mobile/src/components/chat/SourceRow.tsxnewtappable source row
    mobile/src/components/chat/CitedSources.tsxnewCitedSourcesBar + CitedSourcesSheet
    mobile/src/components/chat/MessageRow.tsxmodifiedread processed; render Sources footer + sheet
    mobile/src/chat/__tests__/fixtures.tsmodifiedmakePacket / makeCitationPacket / makeSearchDoc(+Packet)
    mobile/src/chat/__tests__/messageProcessor.test.tsnewdedup/order, documentMap, final_documents, reset, cursor, isComplete
    mobile/src/chat/__tests__/citations.test.tsnewselectSources split/order/dedup + helpers
    mobile/src/chat/__tests__/openSource.test.tsnewdocumentTarget branches
    mobile/src/components/chat/__tests__/SourceRow.test.tsxnewrender + onPressopenSource
    mobile/src/components/chat/__tests__/CitedSources.test.tsxnewsheet sections + bar visibility
  • Est. size: ~1050-1250 LOC (production + tests).
  • Depends on:
  • Feature-flag state: N/A — additive; main stays releasable.
  • Tests on merge: unit (jest) + RN Testing Library. Directly covered: citation/document state building from a mocked packet stream (messageProcessor), the selectSources split, documentTarget routing, a SourceRow tap → openSource, and the sheet's section rendering. Not exercised by the unit suite (pending on-device verification): the native onLinkPress event shape and the inline-marker → openUrl plumbing through StreamdownText, and the MessageRow completed-answer footer gating. Gate: bun run typecheck, bun run lint, bunx jest.
  • Drift checkpoint: Before/at implementation, device-verify the react-native-enriched-markdown onLinkPress event shape (expected { url }) — the one platform unknown the inline path rests on (unit tests mock it; a dev build confirms it). Also confirm whether [[n]]() file markers render tappable (the sheet is the reliable fallback regardless).