Skip to content

0003. Durable, sequenced thread event log

Context: feega currently renders a thread from chat_messages, resumes classic turns from chat_jobs.partial, resumes Agent Kit turns from agent_kit_runs.partial, and joins Kit streams through Realtime chunks with ChunkPosition. These paths have different cursors and recovery rules.

Decision: introduce one append-only event log per thread. Every event receives a monotonic, gap-free seq inside the same transaction that changes the thread state. The client and server use one ordered reducer for refresh, live join, and resume. Realtime publishes only the thread id and latest sequence; the event query is authoritative.

Progress is represented by durable absolute snapshots keyed by runId, not by text or reasoning offsets. A progress snapshot is a projection input, not a message. A terminal event and the final assistant message are committed together. chat_messages remains a materialized read model during and after the migration; it is no longer the live/resume source.

Retries use a producer sourceKey unique within a thread. A duplicate key returns the existing event; a conflicting payload fails. The sequence counter and event insert share the transaction, so rollback consumes no sequence. Missing notifications, duplicate notifications, process death, and client reconnects are handled by reading seq > cursor.

The event reducer never calls tools or replays external effects. Tool idempotency, retries, ambiguous outcomes, and reconciliation remain the responsibility of the ExternalEffect ledger from task #99. Events may carry an effect id and status reference for audit, never executable effect input.

Migration is incremental: define and test the reducer, add the writer and backfill, dual-write all thread mutations, shadow-compare projections, switch refresh and live delivery behind a flag, then retire partial mirrors, position handling, and payload-carrying Realtime events. Existing clients continue receiving the legacy stream until the new reader is enabled for all supported clients.

The detailed schema, event catalogue, retention/compaction contract, backfill procedure, and acceptance tests live in Notion task #100: https://app.notion.com/p/3cb2944ee2a781f2bf8ff5de271865a8a?pvs=204