Repository navigation
Description
src/daemon/session-event-log.ts writes events.ndjson per session with pure appendFile, no size or rotation cap — MAX_EVENT_LIMIT = 500 is only a page-size cap for readSessionEventLog, not a file bound. A long-lived session grows this file without bound (unlike src/utils/app-log-files.ts, which already caps app logs at DEFAULT_MAX_APP_LOG_BYTES = 5MB / DEFAULT_MAX_ROTATED_FILES = 1, env-overridable via AGENT_DEVICE_APP_LOG_MAX_BYTES / AGENT_DEVICE_APP_LOG_MAX_FILES).
Naively reusing the app-log rotate-then-truncate approach is not safe here: the sole reader, the events CLI/RPC command (src/commands/observability/index.ts, backed by readSessionEventLog), pages with a cursor that is an absolute line-offset since the file's creation (normalizeCursor/consumeEventLogLine in session-event-log.ts), which callers are expected to persist and resume across repeated calls over a session's lifetime — exactly the long-lived-session scenario motivating a cap. Truncating the live file on rotation resets line indices to 0, so a resumed cursor silently reads the wrong (or no) events instead of erroring — a correctness regression, not just data loss.
The good news: no other subsystem depends on events.ndjson completeness. Replay/heal use session.actions, not the event journal; ADR-0018 (docs/adr/0018-unified-event-journal.md, Proposed/unimplemented) explicitly states the journal is "not a source of truth" and rejects rebuilding state from it. So a cap is compatible with documented guarantees — but making it correct for the cursor-paging reader needs design work beyond a copy-paste of the app-log rotation, e.g. a stable per-entry sequence number that survives rotation (content-addressed cursor instead of position-addressed) plus an explicit "requested cursor precedes retained history" signal instead of a silent empty/wrong page.
Filed per #1781 (B5). See also the ADR reference above for the "no state from events" invariant that makes a cap acceptable in principle.
Activity
thymikee commentedon Aug 17, 2026
Adversarial re-check of this finding (2026-08-17). Core claims hold: the writer is a bare
appendFilewith no size check anywhere; the only content reader is theeventshandler; the cursor is a bare line offset from byte 0 (normalizeCursor/consumeEventLogLine) and the entry record carries noseq;app-log-files.tsrotation has no cursor-bearing consumer, so it is not prior art for this reader.Two tightenings:
cursoris a plain positional/RPC field. The point stands becausenextCursoris returned for resumption, so any correct bound must honour it; just don't cite an existing caller.session-close.ts) never deletes the session directory, soevents.ndjsonoutlives every closed session forever. Deleting (or truncating) it on close bounds the common case with zero cursor impact; a real cap is then only needed for long-running open sessions. Consider doing that first and scoping the seq-based design to the remaining case. A count-cap with tail retention + a reader-sidetruncatedBeforesignal is a smaller alternative to a new sequence field.Write volume for sizing: 2–3 JSON lines (~150–400 B) per command (
request.started/finished+action.recorded) → hundreds of KB to low MB per hour in a busy session.fix(daemon): cap events.ndjson with cursor-safe rotation (#1867)