# 4. Unified event log; tagged-map Event envelope
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`; later `permission_decision`) are assigned a per-Session monotonic
`seq`, 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.