The event model

The append-only event log is the source of truth. Everything else — the conversation, the costs, the diffs, the phases — is a fold over it.

The envelope#

{"v":1,"id":"01J8…","sessionId":"01J8…","seq":42,"ts":1765400000000,
 "type":"tool.completed","payload":{…}}
  • seq is gapless and assigned at append time; one complete line per write, never interleaved.
  • A torn final line (crash mid-write) is quarantined to a .torn file and truncated away on open — corruption anywhere else refuses to resume rather than silently repairing.
  • tool.requested inputs are canonicalized (sorted keys) so a replayed history reproduces identical bytes on every rebuild.

The 40 durable types, by family#

FamilyTypes
Sessionstarted · resumed · renamed · model-changed · git.baseline-recorded
Usermessage-created · steering-queued · steering-injected · interrupted
Modelrequest-started · text/thinking-block-completed · response-completed · request-failed
Toolsrequested · approved · started · completed · failed · cancelled
Permissionsrequested · granted · denied
Files & processesfile.read · file.modified · process.started · process.exited
Contextpruned · compacted
Completionverification-requested · task.completed · task.failed
Phases (v2)phase.entered · phase.artifact · phase.approved · phase.rejected
Verify / memory / subtasks (v2)verify.result · memory.written · subtask.created · subtask.completed

Six ephemeral types (streaming deltas, live tool output) flow on the bus but are never persisted — the log records what settled, not the typing noise.

Pairing safety#

Every tool_use must meet its tool_result — across interruptions, crashes, and compactions. Resume closes dangling calls with synthetic cancellations; a compaction cut is validated so it can never orphan a pair. This single invariant is why the API replay of any KHAELOR session is always well-formed.

Everything is a fold#

QuestionFold
What does the model see?buildConversation(events) — deterministic, byte-stable, prune/compaction re-applied from their recorded payloads.
What did this cost?buildUsageTotals(events) — sums only response-completed usage. Nothing else may contribute.
What changed on disk?buildFileChangeSet(events) — baseline + file.modified, attribution-aware.
What phase am I in?foldPhaseState(events).
What should happen next?foldTurnState(events)deriveNext — the kernel itself.

Why audit falls out for free#

Because state is never anywhere else, explaining any behavior — including a daemon run at 3 AM — is reading a file. /fork is a file-prefix copy. /replay is re-driving the user turns. /sdiff is two folds side by side. None of these are features bolted on; they are corollaries of the event model.