4.9 KiB
Memory Regression Budget
Status: active guardrail Updated: 2026-04-18
This document defines the current memory regression budget for jcode.
The goal is not to freeze memory usage forever. The goal is to make memory changes:
- measurable
- reviewable
- intentionally justified
Where possible, budgets below are tied to counters and caps already exposed by the codebase rather than guessed RSS numbers.
How to collect the metrics
Use existing debug surfaces instead of ad hoc instrumentation:
- TUI aggregate memory profile:
:debug memory - TUI memory sample history:
:debug memory-history - Markdown cache profile:
:debug markdown:memory - Mermaid cache profile:
:debug mermaid:memory - Agent/session memory profile via debug socket:
agent:memory
Primary sources in code:
src/tui/app/debug_cmds.rssrc/tui/memory_profile.rssrc/session.rssrc/tui/markdown.rssrc/tui/mermaid.rssrc/runtime_memory_log.rs
Budget model
We use two kinds of budgets:
- Hard caps
- These are explicit limits already enforced by caches.
- Regressions here mean the code changed its bound or bypassed it.
- Ratchet expectations
- These are expected relationships between memory counters.
- Regressions here are allowed only with explanation and updated docs/tests.
Hard caps
Markdown cache budget
Source: src/tui/markdown.rs
| Metric | Budget | Why |
|---|---|---|
highlight_cache_entries |
<= 256 |
Explicit cache cap (HIGHLIGHT_CACHE_LIMIT) |
Required review action if violated:
- explain why the cache limit changed
- update this doc
- update any affected tests or benchmarks
Mermaid cache budget
Sources:
src/tui/mermaid.rssrc/tui/mermaid_cache_render.rs
| Metric | Budget | Why |
|---|---|---|
render_cache_entries |
<= 64 |
Explicit render-cache cap (RENDER_CACHE_MAX) |
image_state_entries |
<= 12 |
Explicit protocol-state cap (IMAGE_STATE_MAX) |
source_cache_entries |
<= 8 |
Explicit decoded-source cap (SOURCE_CACHE_MAX) |
active_diagrams |
<= 128 |
Explicit active-diagram cap (ACTIVE_DIAGRAMS_MAX) |
cache_disk_png_bytes |
<= 50 MiB |
Explicit on-disk cache cap (CACHE_MAX_SIZE_BYTES) |
cache_disk_max_age_secs |
<= 259200 |
3-day expiry (CACHE_MAX_AGE_SECS) |
Required review action if violated:
- document the new limit and reason
- verify eviction still works
- verify no unbounded growth path was introduced
Ratchet expectations
Session and transcript memory
Source: src/session.rs, src/tui/memory_profile.rs
These are not strict caps yet, but they are expected relationships.
| Metric relationship | Expectation |
|---|---|
provider_messages_cache.count vs messages.count |
Should remain in the same order of magnitude for a single session, and normally track the transcript closely |
session_provider_cache_json_bytes vs canonical_transcript_json_bytes |
Should remain comparable for normal chat flows, not explode independently |
transient_provider_materialization_json_bytes |
Should return to zero or near-zero outside active materialization-heavy paths |
display_large_tool_output_bytes |
Large values require explanation because they usually mean raw tool output is being retained too aggressively in the UI |
Required review action if violated:
- show before/after memory profiles
- explain which retention path grew
- prefer fixing duplication before raising any budget
Runtime memory log expectations
Source: src/runtime_memory_log.rs
Runtime memory logs are the regression detection mechanism, not just a debug feature.
Expected behavior:
- server/client logs should be sufficient to explain large changes in:
- session/transcript totals
- provider cache totals
- TUI display totals
- side panel totals
- new large memory owners should emit attributable signals instead of appearing only as unexplained RSS growth
Required review action if violated:
- add or improve attribution before accepting the memory increase
Review checklist for memory-affecting changes
When changing memory-heavy code, capture and include:
- Which counters changed?
- aggregate
:debug memory - targeted
:debug markdown:memory/:debug mermaid:memory agent:memorywhen session/provider cache behavior changes
- Was a hard cap changed?
- if yes, explain why the old cap was insufficient
- Did duplication increase?
- canonical transcript
- provider cache
- materialized provider view
- display copy
- side-panel copy
- Did observability remain adequate?
- if memory grew, can logs/profiles explain where?
Current initial budget summary
These are the concrete enforced limits today:
- Markdown highlight cache entries: 256
- Mermaid render cache entries: 64
- Mermaid protocol image-state entries: 12
- Mermaid decoded source-cache entries: 8
- Mermaid active diagrams: 128
- Mermaid on-disk PNG cache: 50 MiB, max age 3 days
Any intentional change to those limits must update this document in the same PR.