feat: event-log kernel — durable Event sidecar + replay-capable consumer#2
Merged
feat: event-log kernel — durable Event sidecar + replay-capable consumer#2
Conversation
Adds a new durable artifact: every session now writes a typed Event log to ~/.reasonix/sessions/<name>.events.jsonl as a sidecar to the existing ChatMessage / transcript files. Foundation for future replay / projection / multi-agent consumers reading kernel events instead of LoopEvent. Architecturally a pure addition: no edits to loop.ts, transcript.ts, EventLog, log-frame, replay, dashboard server, or any existing test. LoopEvent stays as the internal protocol between loop and immediate consumers — events.jsonl is a parallel kernel artifact, not a migration target. - src/core/eventize.ts: pure stateful translator. Holds monotonic event id seq, last-turn tracker (synthesizes model.turn.started), and a callId stack (correlates tool.intent → tool.dispatched → tool.result chains since LoopEvent.tool_start doesn't carry call.id). - src/adapters/event-sink-jsonl.ts: concrete EventSink port impl — append-only JSONL with append/flush/close lifecycle. eventLogPath helper derives the sidecar path from a session name (mirrors src/session.ts:sessionPath). - src/cli/ui/App.tsx: ~30 lines additive — refs for sink + eventizer opened on session mount (skipped for ephemeral sessions), pipe attached inside the existing for-await consumer alongside writeTranscript, sink.close on unmount, session.opened emitted at open time. - tests/eventize.test.ts: 11 unit tests pinning the mapper — turn-start synthesis, delta channel split, tool callId correlation, error detection, warning categorization, control- marker passthrough. 1737 tests pass (was 1726 + 11 new). Zero existing-test edits.
Closes the loop on the v0.14 kernel architecture: the events.jsonl sidecar from the prior commit is now demonstrably reduceable into a useful projection. Synthetic LoopEvents → Eventizer → JsonlEventSink → disk → JsonlEventSource → reducers.replay() → ProjectionSet round- trips correctly, conversation messages reconstructed in order, tool result correlation preserved, replay() deterministic across runs. This is the architecture-value tipping point: events are no longer just write-only artifacts, they're the source of truth a consumer can rebuild state from. Future work — UI re-rendering from a projection, time-travel, multi-agent event trees — has a working foundation. - src/adapters/event-source-jsonl.ts: EventSource port impl. Pure read side: parses jsonl, skips malformed lines silently (best- effort replay over a possibly-truncated live writer). - tests/event-sink-jsonl.test.ts: 5 tests pinning the sink mechanics (append-per-line round-trip, re-open on existing file, parent dir creation, flush no-op, port-shape conformance). - tests/event-replay.test.ts: 5 tests pinning the architecture promise — synthetic LoopEvents reduce to expected projections, error-shaped tool results land with ok=false, replay determinism, missing-file safety, malformed-line tolerance. 1747 tests pass (was 1737 + 10 new). Zero touches to existing production code.
This was referenced Apr 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Builds the event-log kernel as a new durable artifact alongside the existing LoopEvent stream, NOT as a migration target. Future replay / projection / multi-agent consumers read kernel events from the sidecar; the working LoopEvent path stays untouched.
This is the D approach chosen after evaluating the big-bang LoopEvent removal: instead of ~250 mechanical edits across 29 files (and the regression risk that comes with it), we add 7 new files and a 40-line additive wire-up. Architecture value is tangible immediately (an event log exists and is reducible) without forcing a migration.
f0ca5bc):Eventizertranslator +JsonlEventSink+ App.tsx wire-up. Every session writes typed Events to~/.reasonix/sessions/<name>.events.jsonlas a sidecar.f50de58):JsonlEventSourcereader + round-trip test proving that synthetic LoopEvents → Eventizer → sink → disk → source → reducers → ProjectionSet reconstruct the conversation correctly.Architectural rationale
Reframed the question from "how do we migrate off LoopEvent" to "what's the smallest correctness-preserving change that builds the kernel artifact and lets new consumers exist":
Files
New (7):
src/core/eventize.ts— translator state machinesrc/adapters/event-sink-jsonl.ts—EventSinkport impl (write side)src/adapters/event-source-jsonl.ts—EventSourceport impl (read side)tests/eventize.test.ts— 11 mapper teststests/event-sink-jsonl.test.ts— 5 sink mechanics teststests/event-replay.test.ts— 5 round-trip + projection testsModified (1, additive only):
src/cli/ui/App.tsx— ~40 lines: refs for sink + eventizer, open on session mount, pipe inside the existing for-await consumer alongsidewriteTranscript, close on unmountUntouched:
loop.ts,transcript.ts,replay.ts,EventLog.tsx,log-frame.tsx,LiveRows.tsx, dashboard server, subagent — and every existing test file.Test plan
npm run verify— 1747 tests pass (was 1726 + 21 new)npm run lint— cleannpm run typecheck— cleanreasonix codefor a session, confirm~/.reasonix/sessions/<name>.events.jsonlappears with parseable JSONL — one event per line, monotonic ids, correct turn numbers,model.turn.startedat every turn boundary,tool.intent→tool.dispatched→tool.resultchain with matching callIdsWhat this unlocks (future PRs, not this one)