
Sipcode
Keep Claude Code's context clean for sharper answers
176 followers
Keep Claude Code's context clean for sharper answers
176 followers
Context hygiene for Claude Code. Caps verbose tool output and dedupes same-session re-reads so the model sees signal, not noise. Anthropic measures 29% quality lift from cleaner context. Proof: 62.6% median tool-output savings on a locked 20-task benchmark. MIT.








Sipcode
the session_id keying for subagents is exactly the part I was worried about, good that the default errs toward letting the read through. One follow-up: when a read does get deduped, does the model see an explicit 'unchanged since last read' signal, or is it just served the cached bytes transparently? I'd want the agent to know it's looking at a cached version so it doesn't re-reason over the same file assuming it's fresh.
@axlerodd The content-hash choice makes sense. The part I’d want to test on a big repo is latency: do repeated reads re-hash large files every time, or do you short-circuit with size/mtime first and only hash when those change?
The same-session re-read dedup is the part I'd test first — re-reading unchanged files 6-8x is exactly what quietly poisons a long Claude Code session. How do you decide a file is "unchanged" between reads: mtime, content hash, or git state, and does that still hold when a subagent reads the same file in its own context branch? Caps on npm/tsc output make sense too, but can the model pull the full uncapped log on demand when it actually needs it?
Sipcode
@hi_i_am_mimo Great question, and the right thing to test first.
"Unchanged" is decided by content hash, not mtime or git. Every read is SHA-256 hashed and deduped only if the current on-disk hash matches the per-session cache. mtime is too weak (checkouts and formatters change it without changing content) and git misses unstaged edits. The hash is recomputed against disk on every hit, so a stale read is impossible by construction. Changed by one byte, it passes through.
On subagents: the cache is keyed by session_id, so a subagent reading in its own branch is not deduped against the parent. That is deliberate, it genuinely needs the bytes in its own context. The default everywhere is the same: when in doubt, let the read through. A missed dedup costs tokens, a wrong dedup costs trust.
On uncapped output: yes. The caps only inject a limit when the model did not set one. If Claude passes its own head_limit I never override it, so it can always pull the full set. The cap is a default, not a ceiling.
Thanks — content hash over mtime/git is the right call, and "a missed dedup costs tokens, a wrong dedup costs trust" is a solid default to design around. The cost I'd watch is re-hashing on every read: on a big repo with large files, does computing SHA-256 each hit add measurable latency, or do you short-circuit by size/path first? And is the hash itself ever cached by (path, mtime) so an unchanged file isn't re-read off disk just to be re-hashed?
Foyer
The context window management problem in Claude Code is real. Long sessions accumulate dead weight fast, old tool outputs, abandoned approaches, redundant file reads, and once the context gets bloated the model starts hedging more and the answers get muddier. Curious whether Sipcode is doing something principled to decide what to prune (like deprioritizing failed attempts or stale file state) or whether it's more of a manual curation layer where you're telling it what to keep. Also wondering if there's any handling for cases where something that looked like a dead end earlier in the session turns out to be relevant again.
Sipcode
@fberrez1 Florent, sharp question. The distinction you are drawing is real.
Honest answer: Sipcode operates at the mechanical layer, not the semantic one. It does NOT currently decide "this approach was abandoned" or "this file is stale." That kind of semantic curation needs an LLM in the loop (kills the privacy story) or a structured intent trace (research territory).
What Sipcode does today:
1. Reads: dedup by file path + content hash. If Claude already read it and disk has not changed, the re-Read short-circuits. Original content stays in context.
2. Verbose tool output (git log, npm install, grep, find): cap volume via parameter injection. Static rules, not semantic.
On your dead-end-becomes-relevant-again case: Sipcode does not remove what is already in context. It catches DUPLICATIVE reads only. If something seemed irrelevant earlier and matters now, Claude still has the original bytes and can re-engage.
The real edge case: if Sipcode caps a verbose output (grep at 100 results) and result #500 was the one you needed. That is a failure mode. Every rewriter declares an integrity score on each fire so over-stripping is visible in sipcode why.
Semantic curation (deprioritize failed attempts, drop stale state) is the right next layer. Honest pre-commitment: it requires an architecture I have not figured out yet, or a privacy compromise I am not willing to make. Thinking on it.
Context bloat is my #1 frustration with Claude Code in long sessions. You watch it re-read the same files and re-print npm install walls of text and by the end of a complex session the answers are noticeably worse. The 40% agent error reduction stat is the one that got my attention - quality lift is nice but errors are the thing that actually breaks workflows. The PreToolUse hook approach is smart because it intercepts before the context gets polluted rather than trying to clean up after. Installing this today. Does it handle situations where Claude Code genuinely needs to re-read a file because it changed, or does it dedupe those too?
Sipcode
@galdayan Thanks Gal, that 40% number is exactly why I lean on it over the quality lift in the copy.
To your question: no, changed files are never deduped. On every potential dedup hit, the proxy compares cached bytes against current disk bytes after LF and BOM canonicalization. If they differ by even one byte, the read goes through untouched. The cost is one stat + hash per re-read, the benefit is I never feed Claude stale content. Designed it that way because a wrong dedup is worse than no dedup at all.
Timbal AI
The dogfooding story sells this more than any benchmark — discovering your own drift tool read 624,940 tokens wasted while --stats credited 7,553 saved, then root-causing it to uncached mid-session installs and shipping Warm-Fill in 24h. Most launches would've quietly buried that. And the 38% duplicate-Read finding finally names that "why does this session feel sluggish" sensation I could never explain.
One question on the dedup: you canonicalize LF and BOM before the byte comparison. For files where whitespace carries meaning — Python, Makefiles, YAML — can that normalization ever flatten a real change into a false no-op, or is it strictly newline/BOM and never touches interior whitespace?
Sipcode
@david_vilalta Great question, and it is the exact thing I was paranoid about when I wrote it
.
It is strictly line-ending plus a leading BOM, and it never touches interior whitespace. The whole canonicalizer is two operations: strip one leading U+FEFF, then replace CRLF and lone CR with LF. That is it. No tab/space folding, no indentation collapsing, no trailing-whitespace trimming. So a real change in a Python block's indentation, a tab-vs-space edit in a Makefile, or a re-nest in YAML all survive as genuine byte differences and the read passes through. They are never flattened to a false no-op.
The only thing it does flatten is a pure line-ending change or a BOM toggle with no other edit. For Python, Make, and YAML that is a semantic no-op anyway, so deduping it is the correct call rather than a risk. The one theoretical exception is a file whose meaning literally depends on CRLF bytes, like a fixture testing newline handling, but that is not Claude re-reading source for understanding, and even then the cost is one redundant read, never a wrong one.
Design rule I held to: when unsure, let the read through. A missed dedup costs tokens. A wrong dedup costs trust.
Claude Code users know how quickly context gets polluted with logs, repetitive outputs, and tool noise 😅 The idea of treating context as a limited resource rather than an infinite one really resonates. Curious... what was the most surprising source of context bloat you discovered while building Sipcode?
Sipcode
@harini_mukesh Thanks Harini. Honest answer: it was not the verbose tool output, even though that is the biggest absolute number. It was watching Claude re-read the same file three times in a single task because each tool call thinks it is starting fresh.
I built a quick counter expecting maybe 5-10% of reads to be duplicates. The real number on a 4-hour refactor session was 38%. More than a third of every Read was the model looking at bytes it had literally just seen. Not a model failure, a memory architecture failure, the agent does not have a cheap way to remember "I already loaded this", so it just re-fetches and pays the token cost.
The surprising part is that this is invisible. You feel like the session is slowing down, you blame the model, you switch to a smaller context. You do not realize you are paying 800 tokens to re-read the same file Claude saw 90 seconds ago.
The npm install walls and tsc dumps are the obvious wins. The re-read pattern is the one that quietly eats half your context window before you notice.
What does your context-pollution profile look like when you actually measure it?
The re-read dedup looks like a clear win! QQ - when you inject a head_limit on a grep Claude ran without one, does the model see a "truncated, N more matches exist" marker? Does it read the capped list as the full set? Overall, very well done!
Sipcode
@artstavenka1 Good question, and it gets at the exact risk I worried about with this rewriter.
Key thing: I inject the native head_limit parameter rather than truncating the output myself. So whatever Claude Code normally surfaces when a grep is capped is preserved untouched, I'm setting the same param a user could set by hand, not post-processing the result and stripping a marker. I never hide matches behind the model's back.
The honest residual risk is the one you're pointing at: if a real query genuinely needed more than the cap, the model works from the capped set. That's exactly why v1.6.16 raised the cap from 50 to 100. My dogfood data showed native-grep was the highest-volume and lowest-integrity rewriter, and 50 was clipping real symbol lookups across larger codebases. 100 covers the vast majority of real Claude Code greps while still bounding pathological ones. The rewriter declares a 0.78 integrity score precisely to keep that residual honest in the stats.
Two guardrails: it never reorders, it keeps ripgrep's native ordering for the first N, and if Claude sets its own head_limit I leave it alone and don't override. So the model can always opt out by being explicit.
Appreciate you reading down to this level.