Date: 2026-05-29 Status: Accepted
Context
The event bus (the D-06 "spine") is the seam between the core and every front-end, and
the per-Session Log is the single source of truth (ADR 0003). We need one Event
shape that works for both live display and durable replay. The Kimojo reference keeps
two things — durable messages and a separate ui_event stream — which is the source
of its documented partial-vs-final call_id duplication.
Decision
There is one Event type, a plain tagged map built via a constructor module, with a
common envelope: {id, session_id, seq, ts, type, data}.
- Canonical events (
user_message,assistant_message,tool_call,tool_result; laterpermission_decision) are assigned a per-Session monotonicseq, appended to the Log, and define History (a fold over them). - Ephemeral events (
text_delta,reasoning_delta,status) are broadcast on the bus for live display and never persisted.
The Log is a per-Session append-only NDJSON file; the envelope serializes 1:1 to a
line. Streaming partials are never written as canonical events — only the final
assistant_message is.
Consequences
- One contract for live UI, persistence, replay, and fork — no two-store sync.
- Deterministic replay/fold via
seq(and file append order as backstop). - Avoids Kimojo's dedupe bug by construction (partials are ephemeral).
- Front-ends stay thin — a renderer is just a subscriber that pattern-matches on
type; new front-ends need no core changes. - Cost: the canonical type set is a small shared vocabulary that must be curated deliberately; adding a canonical type is a schema change to the Log.