Skip to content

[FEATURE] Rotate session JSONL files at compaction boundaries #27505

@w0lf69

Description

@w0lf69

Preflight Checklist

  • I have searched existing requests and this feature hasn't been requested yet
  • This is a single feature request (not multiple features)

Problem Statement

Session .jsonl files grow without bound because compaction (/compact and auto-compact) appends a compact_boundary marker to the same file and continues writing. For long-running or frequently-resumed sessions, this creates files that exceed V8's heap limit, causing SIGABRT crashes, OOM kills, and multi-minute startup hangs.

Real data from a production session:

Metric Value
File size 386 MB
Compaction boundaries 142
Time span 11 days (Feb 10–21, 2026)
Average segment between compactions 2.7 MB
Runtime memory (current) 386 MB loaded at startup
Runtime memory (with rotation) ~3 MB (current segment only)
Memory reduction 99%

This session crashed Bun with a SIGABRT after ~5 hours of runtime due to memory pressure from the accumulated JSONL.

The core issue: compaction already creates the semantic boundary (the model's context window is reset, the summary carries forward), but the file doesn't rotate. Everything before the boundary is dead weight that will never be read during normal operation — yet it's loaded into memory on every startup and resume.

Proposed Solution

Rotate the JSONL file when a compact_boundary event is written. Start a new file for the post-compaction session, linking back to the parent via metadata.

Current behavior

session-abc.jsonl:
  [messages 1-500]
  {type: "system", subtype: "compact_boundary", compactMetadata: {...}}
  {type: "user", message: {content: "<compaction summary>"}}
  [messages 501-1000]
  {type: "system", subtype: "compact_boundary", compactMetadata: {...}}
  {type: "user", message: {content: "<compaction summary>"}}
  [messages 1001-1500]
  ... (file grows forever)

Proposed behavior

Option A — Segment numbering (recommended):

abc123.jsonl:          (segment 0: messages 1-500, frozen after first compaction)
abc123.1.jsonl:        (segment 1: boundary + summary + messages 501-1000)
abc123.2.jsonl:        (segment 2: boundary + summary + messages 1001-1500)

Benefits: Session identity preserved. Latest segment found via glob(abc123*.jsonl) | sort | last. No chain-walking needed for --resume. Backward compatible (no suffix = segment 0).

Option B — New UUIDs with metadata linking:

abc123.jsonl:          (original, frozen)
def456.jsonl:          (segment 1, metadata: {parentSegment: "abc123", segmentIndex: 1})
ghi789.jsonl:          (segment 2, metadata: {parentSegment: "def456", rootSession: "abc123"})

Benefits: Consistent with existing UUID naming. Each segment is a standalone file. Requires chain-walking for history but --resume only needs the latest.

Design details

  1. On compaction: Write the compact_boundary marker as the last entry in the current file. Create a new JSONL file. The first entry in the new file is the compaction summary (the user message containing the context). Continue writing to the new file.

  2. Metadata in the new file's header (or a dedicated segment_header entry):

    {
      "type": "system",
      "subtype": "segment_header",
      "parentFile": "abc123.jsonl",
      "rootSession": "abc123",
      "segmentIndex": 2,
      "timestamp": "2026-02-21T21:04:51.661Z"
    }
  3. --resume behavior: Load only the latest segment file. The compaction summary at the start of that file already contains everything the model needs. Full history is available by walking the chain backward via parentFile if needed.

  4. sessions-index.json: The index entry for the session points to the latest segment. Old segments are not indexed (they're archived, not active).

  5. Startup: Only the latest segment per session is scanned. Old segments are inert files on disk — no memory cost, no parsing cost, no V8 heap pressure.

  6. Backward compatibility: Sessions without segments work exactly as they do today. Rotation only activates on the next compaction.

Why this is better than alternatives

Approach Limitation
Size-based file cap (e.g., 50MB) Arbitrary. Doesn't align with model context boundaries. May split mid-conversation.
Retention policies (delete old sessions) Destroys data. Doesn't fix root cause (single-session growth).
Session cleanup plugins (#26328) After-the-fact cleanup. Crash/OOM happens before cleanup can run.
Streaming/lazy parsing Reduces but doesn't eliminate O(n) cost. Significant engineering complexity.
SQLite storage Major architectural change. Breaks JSONL tooling and third-party integrations.
Compaction-boundary rotation (this proposal) Zero data loss. O(1) memory. Aligns with existing semantics. Minimal change. Backward compatible.

Impact

This single change would resolve or significantly mitigate 6 open issues:

All report the same root cause: a single JSONL file that grows without bound. Rotation at compaction boundaries eliminates this class of problem entirely.

Use Case Example

Power users running long-lived sessions — multi-day coding sprints, resumed sessions across weeks, heavy subagent usage — hit this wall consistently.

Today's workaround: manually move large .jsonl files out of ~/.claude/projects/, which breaks --resume and loses session history.

With rotation:

  1. User works normally. Sessions compact as usual.
  2. Each compaction creates a new ~2-5MB segment file.
  3. Memory usage stays constant regardless of session age.
  4. Full history is preserved on disk for analysis, audit, or replay.
  5. --resume loads only the latest segment (~3MB instead of hundreds of MB).

Concrete scenario: A developer resumes the same session daily for 2 weeks. Today: 20+ compactions accumulate into a 200MB+ file that eventually crashes. With rotation: 20 small segment files, each ~5MB, latest one loaded in milliseconds.

Alternative Solutions

The current workaround is a manual extraction tool that reads compaction boundaries from the JSONL, exports summaries to a separate file, and lets users start a fresh session with their memory/environment files carrying the context forward. This works but requires manual intervention and loses the --resume chain.

Community PRs exist for adjacent problems (#27140 memory-bridge for context consolidation, #26328 session-manager for cleanup) — this proposal addresses the root cause those plugins work around.

Additional Context

  • Compaction summaries are already self-contained. They include everything the model needs to continue. Pre-compaction content is archived context, not active state. Rotation makes file structure match the semantic structure that already exists.
  • The compact_boundary entry already marks the exact rotation point. No heuristics or size thresholds needed — the system knows when to rotate.
  • Multi-agent sessions are the worst case. Parent sessions log all subagent output ([BUG] Session files grow unboundedly, causing OOM crash on startup ("Aborted (core dumped)") #20367), causing exponential growth. Rotation caps each segment regardless of subagent volume.
  • I'm happy to contribute implementation work if the team aligns on the approach and can point me to the relevant source files.

Priority

Critical — Blocking my work

Feature Category

Performance and speed

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions