Core Concepts
Caching & Compression
How LeanCTX caches files, compresses shell output, and manages the context window to achieve 60–90% savings per read, and up to 99% on cached re-reads.
LeanCTX uses a multi-layered caching and compression system to minimize token usage. Understanding these layers helps you get the most out of the system.
Storage Layout (XDG)
Since v3.8.x LeanCTX follows the XDG Base Directory spec and splits its files across four categories so the config directory can be mounted read-only. A fresh install resolves to:
| Category | Path | Holds |
|---|---|---|
| Config | $XDG_CONFIG_HOME/lean-ctx | config.toml, shell hooks, env.sh |
| Data | $XDG_DATA_HOME/lean-ctx | sessions, vectors, graphs, knowledge, archives, stats.json |
| State | $XDG_STATE_HOME/lean-ctx | events, journals, logs, ledgers, tee recovery output |
| Cache | $XDG_CACHE_HOME/lean-ctx | semantic cache, models, learned patterns |
Existing legacy (~/.lean-ctx) and pre-split mixed ($XDG_CONFIG_HOME/lean-ctx)
installs keep working unchanged in single-dir mode; a custom LEAN_CTX_DATA_DIR
(non-standard path) still forces one directory, while a LEAN_CTX_DATA_DIR set to the
standard $XDG_DATA_HOME/lean-ctx is a data-only pin that keeps config/state/cache on
their own XDG dirs (#594). Run lean-ctx doctor --fix to split a legacy/mixed install into the XDG dirs.
Per-category overrides: LEAN_CTX_CONFIG_DIR, LEAN_CTX_DATA_DIR,
LEAN_CTX_STATE_DIR, LEAN_CTX_CACHE_DIR.
Session Cache
Every file read via ctx_read is stored in a per-session in-memory cache with a BLAKE3 content hash.
When the same file is read again:
- Content unchanged: Returns a compact cache-hit stub (~13 tokens) instead of the full file
- Content changed: Returns the full new content and updates the cache
- Different mode requested: Re-reads with the new mode
Cache Lifecycle
# 1. First read: full content cached
ctx_read src/auth.ts
→ F1=auth.ts 123L [full content...] (~450 tokens)
# 2. Second read: cache hit
ctx_read src/auth.ts
→ F1=auth.ts cached 2t 123L (~13 tokens, 97% saved)
# 3. File was edited externally, third read: detects change
ctx_read src/auth.ts
→ F1=auth.ts 125L [new full content...]
# 4. Force bypass cache
ctx_read src/auth.ts fresh=true
→ F1=auth.ts 125L [re-read full content...] Cache Management
| Command | Effect |
|---|---|
ctx_cache action="status" | Show cached files, sizes, and hit rates |
ctx_cache action="clear" | Clear entire session cache |
ctx_cache action="invalidate" path="..." | Invalidate a specific file |
ctx_read fresh=true | Bypass cache for a single read |
The in-memory cache clears after 1 hour of inactivity (default memory_cleanup = "shared"),
but a re-read stays cheap anyway: stub eligibility is persisted to a small on-disk index
and survives idle clears and daemon restarts (see below).
Re-read Reliability v3.8.14
The ~13 token "unchanged" stub is the single biggest re-read
saving, so LeanCTX makes it both correct and
durable. A stub is served only when it is genuinely safe, and
it keeps working across the events that used to silently force a full re-read.
| Guarantee | Behavior |
|---|---|
| Conversation-scoped | A stub is only returned inside the same conversation that received the full content. Open a new chat and the first read returns the full file again — you never inherit another conversation's "unchanged" claim for content that isn't in your context window. Disable with LEAN_CTX_CONVERSATION_SCOPE=0. |
| Survives restarts & idle | Stub eligibility (path, content hash, mtime, line count, conversation) is persisted to $XDG_DATA_HOME/lean-ctx/read_cache/stub_index.json — never the file content. After a 5-minute idle clear or a full daemon restart, a re-read still returns the stub via a "cold" hit, provided the file is byte-identical on disk (hash + mtime) and the conversation matches. Disable with LEAN_CTX_STUB_PERSIST=0. |
| Subagent-aware | Each subagent (Task) gets its own conversation scope derived from its task id, so it reuses cheap stubs for its own consecutive reads but never receives a stub for content delivered to its parent or a sibling agent. |
| Compaction-safe | When the host compacts or summarizes the conversation, delivery flags and the persisted index are reset, so the next read returns full content — the summary no longer holds the file verbatim. |
A stub is withheld (full content returned instead) whenever the
file's hash or mtime changed on disk, the conversation differs, or
ctx_read fresh=true is set — a re-read is never stale, only cheaper
when it is provably safe. Auto-mode re-reads honor the stub correctly too (a
regression where they bypassed it was fixed in v3.8.14).
Predictive Coding v3.6.8
Inspired by Rao & Ballard's Predictive Coding theory from neuroscience, LeanCTX transmits only prediction errors (structural deltas) when a file is re-read in the same session. Instead of resending the full content, only changes since the last read are sent, dramatically reducing token usage for iterative development workflows.
How It Works
- First read: Full content is delivered and stored as the "prediction" baseline
- Subsequent reads: Only lines that differ from the baseline are sent as a delta
- Large refactors: If changes exceed 60% of lines, the system falls back to full output
# First read: full output (450 tokens)
ctx_read src/auth.ts mode="signatures"
→ pub fn authenticate(...) → Result<Token>
pub fn validate_jwt(...) → bool
pub fn refresh_token(...) → Token
# After editing: only delta sent (~30 tokens instead of 450)
ctx_read src/auth.ts mode="signatures"
→ [delta:signatures] unchanged:2
+ pub fn revoke_token(token_id: &str) → Result<()> Token savings: 90-97% for typical edit cycles where 1-3 functions change between reads.
Hebbian Co-Access Cache v3.6.8
Based on Hebb's rule ("neurons that fire together, wire together"), LeanCTX tracks which files are accessed together and strengthens their association over time. This enables intelligent cache eviction that preserves working-set coherence.
Co-Access Tracking
Files accessed within the same burst window (tool calls in rapid succession) build associative strength. When cache pressure requires eviction, files with strong associations to the current working set are preserved, while isolated files with weak associations are evicted first.
Boltzmann Temperature Eviction
Cache eviction uses a Boltzmann distribution from statistical physics. Each cache entry has an "energy" score based on recency, access frequency, association strength, and graph centrality. Memory pressure acts as "temperature":
- Low pressure (high T): Lenient eviction — even moderate-value entries survive
- High pressure (low T): Deterministic eviction of lowest-energy entries
This prevents both premature eviction of potentially useful entries and cache bloat from unused files.
Predictive Prefetch v3.6.8
Using the Free Energy Principle from neuroscience, LeanCTX learns file access transition patterns and proactively pre-loads files predicted to be needed next. This eliminates cold-read latency for common workflows.
How It Works
- Observation: Every file access updates a transition probability matrix
- Prediction: After accessing file A, the model predicts B is next (based on learned patterns)
- Prefetch: High-confidence predictions trigger background pre-loading
- Feedback: Hit/miss tracking adjusts confidence thresholds via Active Inference
The system's "free energy" (prediction error rate) is continuously minimized, when predictions are wrong, confidence thresholds increase; when predictions are correct, thresholds decrease for more aggressive prefetching.
Homeostatic Memory Guard v3.6.8
Inspired by biological homeostasis, LeanCTX maintains system equilibrium through a multi-level graduated response system. Rather than hard limits that cause abrupt failures, the memory guard applies proportional responses to resource pressure:
| Level | Trigger | Action | Effect |
|---|---|---|---|
| Nominal | <70% memory | None | Normal operation |
| Elevated | 70-85% | Trim outputs | Compress oversized responses |
| High | 85-93% | Evict entries | Remove low-energy cache entries |
| Critical | 93-95% | Unload indices | Drop search indices (rebuildable) |
| Emergency | >95% | Emergency drop | Aggressive cleanup to prevent OOM |
A feedback loop tracks whether actions were effective. If pressure persists after intervention, the system escalates to the next level. When pressure subsides, it resets to nominal, no permanent degradation occurs.
File References (F1, F2, ...)
Each file read in a session gets a persistent short ID: F1, F2, etc.
These IDs survive across the entire session and can be used instead of full paths to save tokens.
F1=auth.ts 123L → Use "F1" instead of "src/auth/service.ts"
F2=server.rs 262L → Use "F2" instead of "src/http/server.rs"
F3=db.ts 64L → Use "F3" instead of "src/database/db.ts"
In TDD mode, even longer identifiers within file content are mapped to short symbols
(α1, α2...) for further compression.
Shell Output Compression
ctx_shell applies pattern-based compression to the output of 60+ recognized developer tools.
Each tool has a specialized compressor that preserves actionable information while stripping boilerplate.
How It Works
- Command Detection: Identifies the tool from the command string (git, npm, docker, etc.)
- Pattern Matching: Applies the tool-specific compression pattern
- Structured Output: Returns only the essential information with token savings count
- Fallback: Unrecognized commands get generic compression (ANSI stripping, empty line removal)
Compression Examples
| Command | Raw Output | Compressed | Savings |
|---|---|---|---|
git status | ~600 tokens | ~80 tokens | 87% |
npm install | ~300 tokens | ~85 tokens | 71% |
npm test | ~2000 tokens | ~200 tokens | 90% |
docker compose ps | ~400 tokens | ~100 tokens | 75% |
kubectl get pods | ~800 tokens | ~200 tokens | 75% |
Error Recovery (Tee)
When a command fails (non-zero exit code), the full uncompressed output is automatically saved
to the state dir at $XDG_STATE_HOME/lean-ctx/tee/ (legacy single-dir installs:
~/.local/state/lean-ctx/tee/). Use lean-ctx tee last to recover the full output.
This ensures compression never hides error details.
Tool Result Archive
When enabled, the archive system stores full tool results to disk when they exceed a token threshold.
The compressed response includes an [ARCHIVE: <id>] reference that the agent
can use with ctx_expand to retrieve the full content on demand.
Flow
- Tool result exceeds threshold (default: 800 characters)
- Full result stored in the data dir at
$XDG_DATA_HOME/lean-ctx/archives/(legacy:~/.lean-ctx/archives/) - Compressed response + archive ID sent to the agent
- Agent calls
ctx_expand id="..."when full detail is needed - Archived entries auto-expire after
max_age_hours(default: 48 hours)
Configuration
# config.toml
[archive]
enabled = true
threshold_chars = 800
max_age_hours = 48 Zero-Loss Archive (ctx_expand)
The archive system stores large tool outputs to disk so they never consume context window space -
but unlike simple truncation, nothing is lost. The full content is always available
on demand via ctx_expand.
How It Works
- A tool result exceeds the configured token threshold
- The full output is written to
$XDG_DATA_HOME/lean-ctx/archives/with a unique ID - The model receives a compact hint instead of the full output:
[ARCHIVE:a7f3c2] auth.ts analysis (2,847 tokens) - 14 functions, 3 classes Key exports: AuthService, TokenManager, validateJWT Use ctx_expand id="a7f3c2" for full content - The agent calls
ctx_expand id="a7f3c2"only when full detail is actually needed
Configuration
# config.toml
[archive]
enabled = true
threshold_chars = 800 # Archive results larger than this (chars)
max_age_hours = 48 # Auto-expire after 48 hours
max_disk_mb = 500 # Disk space limit for archives
ephemeral = true # Replace large results with summary + ctx_expand ref | Option | Default | Description |
|---|---|---|
enabled | true | Enable/disable the archive system |
threshold_chars | 800 | Minimum output size (chars) to trigger archiving |
max_age_hours | 48 | Maximum age of archived entries before cleanup |
max_disk_mb | 500 | Maximum total disk usage for the archive |
ephemeral | true | Replace large results with a summary + ctx_expand reference |
Enforced disk budget (#417): archive cleanup now actually enforces both
max_age_hours and max_disk_mb — it prunes the content files (.txt)
and the FTS index together (no orphaned blobs), runs at MCP start and periodically off the hot path, and
lean-ctx cache prune reclaims the archive on demand (alongside the BM25, graph and knowledge
stores). Before this fix the limits were never applied, so the store could grow unbounded on disk.
Secret Masking
LeanCTX scans tool output for common secret patterns (API keys, JWT tokens, connection strings,
private keys) and replaces them with [REDACTED:type] placeholders. Redaction is enabled
by default for all non-admin roles, so sensitive data never persists in archives or the context window.
Detection patterns are configurable under the [secret_detection] section.
Cache-Safe Guarantee
LeanCTX provides a cache-safe guarantee: content already present in the model's context window is never mutated or corrupted by LeanCTX operations. This is a critical invariant that prevents subtle bugs from stale or inconsistent data.
What This Means
- No silent overwrites: Once a file is cached as F1 with a specific hash, the F1 reference always points to that exact content until explicitly invalidated
- Hash-based validation: Every cache hit verifies the BLAKE3 content hash - if the file changed on disk, the cache entry is invalidated and a full re-read occurs
- Immutable archive entries: Archived content (
ctx_expandIDs) is immutable once written - the same ID always returns the same content - No partial reads: If a read fails mid-stream, no partial content enters the cache
Doctor Cache-Safety Check
lean-ctx doctor includes a cache-safety validation step that verifies:
- All cached file hashes match current disk content
- No archive entries have been externally modified
- Session state is consistent with the file reference table
- No orphaned cache entries exist from crashed sessions
lean-ctx doctor
→ Cache safety: ✓ All 12 cached files verified
Archive integrity: ✓ 8 entries, 0 corrupted
Session state: ✓ Consistent
Orphaned entries: ✓ None found Proxy History Pruning & Build/Test Fidelity (v3.8.6)
When the API proxy is enabled it prunes conversation history in a cache-aware way and compresses request bodies deterministically, so the provider prompt-cache prefix stays byte-stable (preserving the ~90% Anthropic / ~50% OpenAI cache discount). Critically, it never compresses away build or test signal:
- A generic or foreign shell
tool_resultthat looks like a build failure or test run is preserved verbatim at the wire — compiler errors, panics and test summaries are kept intact (#361). - Vendor-prefixed tools (
forge_read,pi.shell, …) are classified by name segment, so a foreign source read is protected while a foreign shell log is compressed.
Context Compaction
ctx_compress creates a checkpoint of the current session state for long conversations.
It summarizes all cached files, their signatures, and the session context into a compact format
that can survive context window truncation.
When to use: After 15-20 tool calls, or when approaching context window limits. LeanCTX auto-triggers checkpoints at configurable intervals.
ctx_compress
→ Session checkpoint created:
12 files cached (F1-F12)
3 signatures preserved
Session context: 2 tasks, 1 workflow
Checkpoint size: ~800 tokens (vs ~15000 tokens for full state)