Repository navigation
feat(session): flag-gated append-only session event log - #1406
Conversation
|
No changes needed from you. A maintainer will review this pull request. |
db346f5 to
69f3fcd
Compare
69f3fcd to
011deb3
Compare
Single append-only session event stream (UUIDv7 id + parentId linkage, one JSON line per event) that coexists with the segmented turns.jsonl writer behind the settings.sessionEventLog / CORBITS_SESSION_EVENT_LOG flag. No read-path change: the module is inert unless the flag is on, and nothing in the existing write/read path imports it. - createEventStreamWriter: append-only writer with append, flush, readFrom, latestCursor, projectTurns, projectPrompt, backfillFromLegacy; a reopened writer continues the on-disk line count - appends serialized with withResolvedDirLock; seq derives from the on-disk complete line count inside the lock, so concurrent writers cannot interleave lines or duplicate seqs - torn tails (unterminated partial lines) are dropped on read and truncated before the next append, keeping seq contiguous - backfillFromLegacy is idempotent via a per-line content hash - openEventStreamWriter: flag-gated seam returning null unless the flag is enabled; explicit "off" env values win over settings - unit tests: append, order, linkage, reopen, cursor reads, concurrency, torn-tail recovery, projections, legacy backfill, flag gating
011deb3 to
a24b123
Compare
TheGreatAxios
left a comment
There was a problem hiding this comment.
Review · Request changes
Flag-gated append-only session event log, unwired from the read path. ASCII tests pass. Torn-tail repair corrupts any prior non-ASCII line and then reuses seq.
Findings
-
src/session/event-log.ts:154—dropTornTailpasses a UTF-16 string index tofs.truncate, which takes a byte length. A complete record whose payload containsé, followed by a partial line with no newline, loses the previous record's newline. The next append is glued onto that line,readFromthrows a JSON parse error, and the following append is also assignedseq: 1. The existing torn-tail test is ASCII, so it stays green. Truncate by byte length. A permanent test should use a non-ASCII complete line plus a torn tail and assert two parseable lines and distinct seqs. -
src/session/event-log.ts:120— backfill idempotency hashes only the raw line. Two identicalturns.jsonllines backfill as one event, so a repeated user turn is dropped and the parent chain skips it. Key by hash plus occurrence index. -
src/session/event-log.ts:257— backfill reads onlyturns.jsonl. A rolled segment (turns-0001.jsonl) is skipped. WalklistSegmentFilesin order. -
src/session/event-log.ts:287— backfill stores the legacy object atpayload.record, whileprojectPromptonly readspayload.text. After backfilling{"role":"user","text":"one"},projectPrompt()is"". Store the same shapeprojectTurnsalready emits forturn.userevents. -
src/session/event-log.ts:205—appendrecordspendingwith both handlers swallowing rejection, soflushresolves when the append it covers rejects.appendof a circular payload rejects onJSON.stringify, andawait flush()still resolves. Surface that rejection fromflush.
No production file imports the module. That matches the flag-gated, inert design and is not a defect. The commit body walks the implementation and two lines exceed 72 characters. Not a behavior defect.
Summary
Prototype for CL-9459: a single append-only session event stream that coexists with the segmented
turns.jsonlwriter behind a flag. No read-path change — nothing in the existing write/read path imports this module, and the writer is inert unless the flag is on.Slice covered
src/session/event-log.ts:createEventStreamWriter(dir, opts)→{ append(event), flush(), readFrom(cursor), latestCursor(), projectTurns(), projectPrompt(), backfillFromLegacy() }. Append-only, one JSON line per event inevent-log.jsonl, UUIDv7id(via the existinggenerateSessionIdpattern) + optionalparentIdlinkage, monotonicseqassigned in append order; a reopened writer continues the on-disk line count.settings.sessionEventLogwith env overrideCORBITS_SESSION_EVENT_LOG=1; explicit "off" env values (0,false,no,off, empty) win over settings.openEventStreamWriteris the gated seam — returnsnullwhen off, so nothing is written.Follow-ups (not in this PR)
sessionEventLogin the shared settings schema (src/config/settings.ts).Design doc is tracked on the Linear issue (the writer lands behind a flag; nothing 0.4.x-adjacent is force-migrated).
Verification
bun test src/session/event-log.test.ts: 18 pass, 0 fail — the 14 writer-suite tests plus the 4 edge tests (dir-lock serialization, torn-tail drop, backfill idempotency, flag gating)bun run typecheckpasses;oxfmt --checkandoxlintpass on the two changed filesopenEventStreamWriterreturnsnullwhen the flag is off and creates nothing on disk; explicit "off" env values win oversettings.sessionEventLogseqstays contiguousbackfillFromLegacyafter a partial backfill appends no duplicates (content-hash keying)