Decisions ADR-0004 2026-08-29

Every write is an event; everything else is a projection

Every write in the core emits an immutable event carrying who, what, when and in which context. The dossier, the timeline, auditing, replay and metrics are read from that log — none of them is written separately.

What was on the table

Five needs each ask for a record of what happened on a demand: debugging (replaying a demand step by step), auditing (who authorised this push, with which credential), the dossier generated stage by stage, security forensics, and metrics — interventions, rework, time to green. One record has to serve all five.

The paths we weighed

One store per consumer — a dossier table, an audit trail, a metrics pipeline. Rejected: several writes, guaranteed divergence, and no replay.

An unstructured text log. Rejected: auditing in a multi-tenant system needs fields, not grep.

What we chose, and why

Every write in the core emits an immutable event into an append-only log, in the same transaction as the state change. The envelope carries the event’s id, the account, the aggregate it belongs to and a readable aggregate key (account-created, pr-delivered), the type, the payload, the time — and the call’s context: who acted (user, agent or platform), the request, the session, the caller.

Everything else is a projection. The dossier, the timeline, auditing, replay, metrics and the attention box are reads of the log, never writes of their own. An action with no event is a defect.

What it cost

The discipline of emitting everywhere. Volume that grows with the fleet, handled by partitioning and retention in the persistence layer.

Since then

The envelope grew its context fields — aggregate key, actor, request, session, caller — on 2026-09-13, with the dead-letter path that carries the full envelope when a consumer fails. Findings, cost metering and notifications are event types rather than mechanisms, and caller verification exists so that an event’s authorship is worth something.