ADR-0003: A chat of its own¶
Status: Accepted Date: 2026-10-12 Deciders: Alex Nodeland
Records decision P3 of lattice RFC-0002, portalr, the portal.
Context¶
The app's center is a chat: a thread's messages, the agent's streaming output, tool calls and approvals, and proposals inline. artifactr's thread protocol already defines all of it, with replay, resumption from a sequence number, and commands carried out once per id. A chat kit would bring its own model of messages and runs, to be mapped onto that protocol.
Decision¶
- portalr's chat is its own, and thin, in
@portalr/ui: the message list, streaming Markdown, the composer, tool-call and approval cards, proposal cards with accept and reject, and run status. - It renders what
@portalr/client's stores hold, and knows nothing of the transport. The stores follow artifactr's protocol directly, through one reducer per protocol over the generated types.
Options considered¶
| Option | For | Against |
|---|---|---|
Our own, thin, in ui, over artifactr's protocol (chosen) |
One model of a thread: artifactr's. Proposals, approvals and notices render as the protocol has them | More to build than a kit, and chat UX is a deep problem |
| assistant-ui, through its external-store runtime | A mature chat, with streaming, branching and attachments | A second model of messages and runs to keep in step with artifactr's, for features artifactr's protocol doesn't have |
Consequences¶
- Easier: a new event in artifactr's protocol is one case in the reducer, which the compiler's exhaustiveness check asks for.
- Harder: composing, streaming and scrolling well are ours to get right.