You told the agent not to touch src/generated. Forty minutes later, after the window filled and the session shrank itself, it edited that folder anyway. The model did not get bold. The line was gone.
Claude Code compaction drops instructions when the shrink step replaces old turns with a fresh paragraph. A rewrite can keep the vibe of “be careful with generated files” and lose the path, the exception, and the exact error you needed on the next edit. A keep-or-drop pass, the kind several plugins shipped this week, tries to leave the original sentence in place. I did not call an API for this. I priced both paths off published rates, and I scored a 12-line transcript with the rules those plugins publish.
Why does Claude Code compaction drop instructions

A coding agent has no memory between calls. Every turn resends the conversation. There is a ceiling on how much text fits in that send. That ceiling is the context window (the max text the model can hold at once). When the transcript crowds it, something has to shrink.
That shrink step is compaction (throwing out or rewriting old turns so the next call still fits). The usual version asks a model to write a summary (a new paragraph that stands in for the old turns). The summary is shorter. It is also a different piece of text. A file path, a “never edit this” line, or a stack frame can fall out even when you would still want them.
This is not a rare settings bug. The token overhead test already showed why the room runs out: a bare Claude Code session spends on the order of 26,000 tokens before you type, and caching makes that prefix cheaper without making it smaller. When the suitcase is full, compaction is how the agent makes space. The loss happens in that rewrite.
I want to be precise about what I have not seen. I do not have a trace that names which model writes the built-in summary on Claude Code 2.1 this week. If your session is on the Opus 5.5 default, and compact uses that same model, the rewrite is priced like Opus. If compact uses a smaller model, the sticker drops and the rewrite risk does not. The default-model piece is why the assumption matters. Check /status before you treat my dollar table as your invoice.
What a keep-or-drop pass keeps instead
Before the product names, the job. You have a list of old messages. You want a shorter list. You do not want a paraphrase.
Two public plugins aim at that job with a decision model (a model that picks from answers you listed, and does not write the reply). fast-jev-compaction pairs each tool call with its result, pins the first message and the newest few, and asks TypeSafe’s Jev whether the pair is still needed. Jev is the hosted picker. Input is billed, output is free. Simon Willison’s Sept 21 note put that input at $0.042 per million tokens. The plugin’s own defaults, from the published config, are a keep threshold of 0.5, six recent messages left alone, a request to compact around 60% full, and a refusal to swap history unless the cut is at least 25%.
claude-jev describes a stricter rubric on the same idea. Drop harness noise and one-word replies locally. On everything else, ask five yes/no questions. Does this row state a user constraint? Does it record a decision and the reason? Does it hold an exact error? Does it name work that is still open? Would running the command again print the same output? The keep score is the strongest of the “keep” answers. A high constraint or a high error stays as the original text, unless the row is rerunnable, in which case a short head plus a pointer replaces the dump. The README says planted user constraints survived 100% of the time in their run, and that the pass took under a second instead of the 30 to 60 seconds a summary often takes. I did not rerun that corpus. Treat 100% as their claim about their plants, not as a property of your repo.
User and assistant sentences stay in order. Nothing in that design is allowed to “clean up” your wording. That is the whole difference. Verbatim here means the bytes you typed, not a polite restatement.
I scored 12 lines by hand
Plugins like these do nothing useful on a short chat, because the newest six messages are pinned and the first message is pinned. The dangerous line is the one you said once, in the middle, and then talked past.
So I wrote that situation down. Twelve rows. preserveRecentMessages at the published default of 6. First row pinned. Newest six pinned. Rows 2 through 6 are the only ones a scorer is allowed to touch. I marked them myself, using the five questions above. This is a person applying a rubric, not a model call.
| Row | Who decides | The line | Mark | Why |
|---|---|---|---|---|
| 1 | pin | Look at the checkout test. | keep | First message stays |
| 2 | score | Never edit src/generated. If a type is wrong, edit src/types.ts only. | keep verbatim | User constraint |
| 3 | score | Understood. | drop | Ack, no fact |
| 4 | score | Full read of src/generated/api.ts | stub | You can read the file again |
| 5 | score | TypeError at src/checkout.ts:88, plus a long log | keep the error line | Exact error, log is rerunnable |
| 6 | score | ok | drop | One-word ack |
| 7 to 12 | pin | Later edits, a green test, “ship it” | keep | Newest six stay |
Row 2 is the post. A summary is allowed to turn it into “user said to be careful with generated code.” The rubric is not. The path and the types.ts exception are the instruction. The mood is not.
Row 4 is the limit. The rule does not hunt inside a file dump for a policy you never typed. If the only copy of “don’t edit generated” lived in a comment in that file, the stub throws it out. Say the constraint as a user line, or put it where the session reloads it. A stub is a pointer back to the file, not a second copy of the file.
And do not ship the 0.5 keep threshold because a config file did. On a different decision task, the Laya piece already showed a 0.5 cutoff turning a real ranking into a coin flip, with the useful cutoff up near 0.97. Same dial. If you let 0.5 delete row 2, you bought the plugin and kept the bug.
What the compact call costs if the check is right
Rates I used, and nothing else. Opus 5.5 at $4 per million input tokens and $20 per million output. Sonnet 5 at $2 and $10. Cache reads at $0.20 per million on both, from the lineup notes published Sept 24. Jev input at $0.042 per million, output free. The scene is one shrink of 80,000 input tokens and, for a summary, 1,200 output tokens.
I ran that arithmetic. I did not run the session.
| Who writes the shrink | Cost of that call | Notes |
|---|---|---|
| Opus 5.5 summary | $0.344 | 80k in at $4, 1.2k out at $20 |
| Sonnet 5 summary | $0.172 | Same lengths, half the rates |
| Jev keep-or-drop | $0.0034 | 80k in at $0.042, no output bill |
If the summary really is Opus 5.5, the picker is about 100 times cheaper on that one call ($0.344 divided by $0.00336). Eight of those on a heavy day is about $2.75 against about 3 cents. That gap is real, and it is also the small number. You compact because the window is full, not because the summary invoice scared you. Prompt caching already made the repeated prefix cheap. The overhead test is the longer version of that sentence.
One caveat on the cheap cell. A write-up of the same plugin says Jev’s window means long chats get chunked, and that it sees the tool call more clearly than the full tool output. An 80,000-token transcript is not one tidy request. Chunking can bill the overlap more than once. It will not climb from a third of a cent to 34 cents unless something else is wrong.
Can I replace Claude Code compaction with Jev
Sometimes, and not as the first move.
The Claude-side README is confident: constraints kept, a second instead of a minute, and if the key is missing the built-in summary still runs. The Codex port of the same idea carries a warning in its own README: the author does not recommend it for real work. The note points at a critique from Theo, posted on X, with three objections that are about the shape of the history, not the sticker price.
One, a probability threshold on tool calls throws away reasoning the model never showed you. The API does not hand you the scratchpad. You cannot keep a thought you were never sent. Two, these models are trained on their own compaction format. A pruned transcript is a different dialect. The constraint can survive in the text while the next edit gets worse, because the history no longer looks like the histories the model practiced on. Three, any edit to old history breaks the cached prefix.
Hold the third one up to the light before you let it kill the plugin by itself. Official compaction rewrites history too. A summary is an edit. It also misses the cache on the next turn. The cache bill is an argument about compacting at all, not a special tax on Jev.
Here is that bill, for a session sitting on Opus 5.5. Eighty thousand tokens of prefix, read from cache, cost $0.016. The same 80,000 read cold cost $0.320. One miss costs $0.304. After that miss, the new prefix caches again. You pay the premium once per rewrite, not on every later turn.
Put the two charges together. Swapping an Opus summary for Jev saves about $0.34 on the shrink call. The next turn then spends about $0.30 to refill the cache, whichever rewriter you used. If you were hoping the plugin would “pay for itself” against a summary, the cache refill eats most of that hope on the very next call, and you still did the right thing if you needed the room. If you were hoping it would pay for itself against not compacting, it does not. Not compacting is cheaper until the window actually hurts.
The dialect objection is the one I cannot price. A 100% survival rate on planted sentences does not tell you the following patch was as good. If you install this, judge it on the next diff, not on whether the forbidden path is still quoted.
There is a boring version that avoids the plugin. Put the line in CLAUDE.md (the project file the session reads when it starts), so the transcript is not the only copy. That file has its own failure mode. The systemd canary is what happens when the instruction lives in a file the agent never opens. A line in the transcript and a line in the file are two chances. One of them is how people get surprised.
If you do install a hook, know which switch you flipped. Mods versus plugins is the difference between a gate you can name and a pile of files the session might not load. A compaction hook that silently fails open to the built-in summary will look like “it didn’t help” when it never ran.
Plant one line, then compact
Ten minutes, on a repo you can throw away.
Never edit src/generated. If a type is wrong, edit src/types.ts only.- In an empty git folder, start Claude Code and type this as its own message, not as a comment in a file (the block above).
- Make it do real work. Read files, run a test, edit something that is allowed. Keep going until the window is uncomfortable, or just run
/compact. - Ask:
What files did I tell you not to edit? Quote my sentence. - If the quote matches, including
src/types.ts, you do not have this bug today. Stop. A plugin will not earn its place on a passing quote. - If it paraphrases and the path is gone, write the same sentence into
CLAUDE.mdand start a fresh session. Ask for the quote again before you touch a third-party hook. - Only if the file copy also fails, try a keep-or-drop plugin on a branch you can delete. Log whether the hook ran. After the next edit, look at the diff before you look at the quote. A surviving sentence with a bad patch is still a failed test.
Skip any router that wants the base Laya checkpoint to make these keep-or-drop calls. That checkpoint’s own table sits near chance until you fine-tune, which is the point of the Laya post. A fast wrong “drop” is worse than a slow summary.
Common questions about Claude Code compaction
Why does Claude Code compaction drop instructions?
Because the built-in shrink asks a model to write a new paragraph in place of old turns. A shorter paragraph can sound right and still omit a path, an exception, or an error string. The model is then careful about a policy you no longer actually sent.
Can I replace Claude Code compaction with Jev?
You can try, on the tool-call rows, if you accept a history shape the model was not trained to resume. The published Claude plugin claims planted constraints survived and that a missing key falls back to the normal summary. The Codex port’s author says not to use the approach for real work. Price is the easy part. The next diff is the test.
Does compaction bust the prompt cache?
Yes. Any rewrite of older turns changes the prefix, so the next call pays full input price on that prefix once. A summary does this too. On an 80,000-token Opus 5.5 prefix the miss is about $0.30. Later turns cache the new prefix again.
Should I lower the keep threshold below 0.5 to save more room?
Not because 0.5 was in a sample config. A threshold you did not fit will delete the constraint you installed the tool to save. Free room by stubbing rerunnable logs first. Those are the long rows. Your one-sentence rule is not.
If you try one thing after this page, make it the quote test on a throwaway repo. If the sentence survives /compact, leave the plugins alone. If it dies, put the sentence where a new session will read it, and only then argue about keep-or-drop.