Skip to content

perf(renderer): bound the feed-debug ring by bytes - #723

Merged
Juliusolsson05 merged 3 commits into
mainfrom
perf/feed-debug-ring-byte-cap
Sep 3, 2026
Merged

Juliusolsson05 merged 3 commits into
mainfrom
perf/feed-debug-ring-byte-cap

Conversation

@Juliusolsson05

Copy link
Copy Markdown
Owner

Problem

SessionRuntime.feedDebugLog, the per-session ring behind FeedDebugPanel, is capped at 500 entries but not by bytes. Feed's visible_rows record (Feed.tsx) carries data.rows with one object per feed item, emitted on every rendered-row change, so on a long transcript each record is hundreds of KB. The two-day performance journal (renderer.session.memory.feedDebugLog, 133,564 samples) shows one session's ring at 136,132,028 bytes — about five times that session's live transcript window (28 MB) — for a diagnostic that is always on. Details in #722.

What changed

  • appendFeedDebugLog now also enforces FEED_DEBUG_LOG_MAX_BYTES (4 MiB per session). Each entry's JSON size is estimated once and cached per entry object in a WeakMap, the same shape as estimateEntryBytes in liveEntryWindow.ts, and the ring is trimmed from the head until it fits.
  • The newest entry is never evicted, even if it alone exceeds the budget, so a single oversized record cannot blank the panel; the count cap still ages it out.
  • estimateFeedDebugLogBytes is exported for tests and future gauge use.

Small records are unaffected: 500 × a few hundred bytes never approaches the budget. Only large payloads are bounded, which is exactly the case that needed it.

Design notes

  • Disk persistence (useFeedDebugPersist → main/storage/feedDebugLog.ts) keys off entry ids and is unchanged; the id counter is not affected by eviction. As before, an entry evicted before the single in-flight append IPC resolves is not written; the byte budget narrows that window for very large records. That is the existing count-cap trade-off, now documented in the module comment.
  • Shrinking the visible_rows payload itself (dropping or windowing rows) would change what debug bundles contain and is left as a separate decision.

Linked issues

Fixes #722. Refs #375 (same lesson applied to the live entry window), #103, #365.

Verification

  • NODE_ENV=test npx vitest run --project unit src/renderer/src/session-runtime/feedDebug.test.ts src/renderer/src/session-runtime/entries.test.ts — 23 tests passed. New contracts: count cap keeps the newest 500 with monotonic ids; small entries are never evicted by the byte rule; 300 KB records are trimmed to the budget from the head with contiguous newest ids; an oversized newest entry survives alone.
  • npx tsc -b --pretty false — clean.
  • After: the feedDebugLog bytesEstimate gauge should stay ≤ ~4 MiB per session on the next long run.

Plan: docs/superpowers/plans/2026-09-03-feed-debug-ring-byte-cap.md.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Kk16MNVJtWAnCeGxGRuHqa

Juliusolsson05 and others added 3 commits September 3, 2026 12:25
The two-day performance journal shows one session's feed-debug ring at
136 MB while its live transcript window was 28 MB. Record the bounded
design before touching code.

Refs #722

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kk16MNVJtWAnCeGxGRuHqa
The per-session feed-debug ring was capped at 500 entries but not by size.
Feed's `visible_rows` record carries one object per feed item, so on a long
transcript each record is hundreds of KB and the two-day performance journal
shows one session's ring at 136 MB — five times its live transcript window —
for an always-on diagnostic.

Estimate each entry's JSON size once (WeakMap-cached, like the live entry
window) and evict from the head until the ring fits a 4 MiB budget. The
newest entry always survives so an oversized record cannot blank the panel.
Disk persistence keeps the full stream; only the in-memory ring shrinks.

Fixes #722
Refs #375, #103

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kk16MNVJtWAnCeGxGRuHqa
…asurable entries

Review follow-ups: the module comment overstated disk persistence (it is
best-effort, one in-flight batch per session, and the byte budget narrows
the loss window); the memory gauge still re-sampled the ring every tick when
the budget already caches an exact per-entry estimate; a payload that
estimateJsonBytes cannot measure could ride under the budget for free, so it
is now charged a conservative floor. Tests pin minimal eviction and that an
oversized record is the next append's first casualty.

Refs #722

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kk16MNVJtWAnCeGxGRuHqa
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Self-review pass (adversarial review of the first cut) — resolved in HEAD:

  • Persistence comment overstated durability → reworded: best-effort, one in-flight batch per session, the byte budget narrows the loss window for large records.
  • Gauge still sampled the ring → renderer.session.memory.feedDebugLog now reports estimateFeedDebugLogBytes (exact, cache-hit), so the plan's ≤4 MiB verification is meaningful.
  • Unmeasurable payloads could ride free → a zero estimate is charged a 64 KiB floor.
  • Tests → minimal-eviction assertion added; the oversized-entry test now pins that it is evicted by the very next append; the small-entry test asserts a real byte range instead of a tautology.

Side effect worth noting: the pending batch shipped over IPC by useFeedDebugPersist is now bounded to ~4 MiB + one entry instead of up to 500 × 300 KB.

@Juliusolsson05
Juliusolsson05 merged commit 6b72648 into main Sep 3, 2026
2 checks passed
@Juliusolsson05
Juliusolsson05 deleted the perf/feed-debug-ring-byte-cap branch September 3, 2026 20:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf(renderer): feed-debug ring is count-capped only and reached 130 MiB in one session

1 participant