EngineeringAugust 23, 2026·16 min read

Agent Context Compaction Quietly Deletes Your Guardrails

Agent context compaction drops the standing rules you load into context: measured violation goes 0% to 30%. Where to pin a rule, and where to enforce it.

Title card for the post Agent Context Compaction Quietly Deletes Your Guardrails, showing 30 percent violation after compaction up from 0 percent, with labelled icons for policy doc, memory entry, user turn and tool result

Agent Context Compaction Quietly Deletes Your Guardrails

An agent refuses the same out-of-policy action for forty turns. It gets the request again on turn forty-one and does it. Nobody jailbroke it, nobody swapped the model, and the request was worded the same way it was the first time. The only thing that happened in between was agent context compaction: the runtime hit its token ceiling, summarized the older turns, and threw the originals away. The rule went with them.

That failure now has a name and a measurement. A June 2026 preprint calls it governance decay, and its central claim is uncomfortable if you ship agents: the constraints you load into context are the exact content a summarizer is most likely to discard, because they contribute nothing to remembering where the task was.

A rule you put in an agent's context is a suggestion with good odds, not a control.

TL;DR. Compaction replaces an agent's older turns with a model-written summary so the run can continue past the context window, and Anthropic, Google, Microsoft, LangChain and OpenAI all ship a version of it. Because compaction is graded on task continuity, standing rules are the cheapest tokens in the transcript, and measured violation of an in-context constraint rises from 0% to 30%, up to 59% on some models, once the rule is gone. Writing a better summarization prompt is not the fix. Pin the rule so it survives, then enforce it somewhere the model does not get a vote.

What does agent context compaction actually do to your message list?

Compaction is the practice of taking a conversation approaching the context window limit, summarizing it, and continuing from the summary instead of the transcript. Anthropic's context engineering write-up puts it in about those words, and the motivation is real. Anthropic's compaction documentation gives the second reason plainly: it keeps the active context small, because as a conversation grows, response quality degrades.

The mechanics are worth reading closely, because the interesting part is the deletion, not the summary. On the Claude API, server-side compaction (beta, header compact-2026-01-12) works in four steps: it detects that input tokens have reached your trigger threshold, generates a summary of the conversation, creates a compaction block holding that summary, and continues the response from there. Then comes the operative sentence in the whole document. On subsequent requests, the API "automatically drops all content blocks prior to the compaction block."

All of them. Whatever was in those blocks is now represented only by whatever the summarizer chose to carry forward.

This is not a Claude-specific design. Anthropic, Google, Microsoft, LangChain and OpenAI all ship some version of it, under different names and with different knobs. None of them is on by default in the sense of happening without you choosing it. Claude's is a strategy you add to the request; ADK's runs from a config object you set; LangGraph gives you a node to wire into a hook; the OpenAI Agents SDK cookbook has you write the summarizer yourself. What they share is that once switched on, the deletion is automatic, and what you get back tells you a compaction happened rather than what it cost you.

Framework What it's called Trigger What you can control
Claude API compact_20260112 strategy Input tokens, default 150,000 trigger, instructions, pause_after_compaction
Google ADK Context compaction (EventsCompactionConfig) Token-based or sliding window Strategy, thresholds, window size
Microsoft Agent Framework Compaction strategies Token limits Strategy selection (in-memory history agents)
LangGraph SummarizationNode wired as a pre_model_hook Your hook logic The summarization prompt and what you feed it
deepagents Autonomous context compression Threshold fractions of the window, or the agent's own call Middleware config, plus the agent can trigger it
OpenAI Agents SDK Session summarization you implement Your session logic The summarizer model, its token budget, and tool-output trimming

Google's documentation is the most candid about scope. ADK compaction reduces context "by summarizing older session history—including instructions, inputs, and model responses." Instructions are named explicitly, sitting in the same list as tool output. Its EventsCompactionConfig gives you two ways to fire: a token-based strategy that ADK calls the primary one and describes as an absolute safety net for unpredictable input, and a sliding window that compacts on turn count instead. The stated goals are latency and cost.

The survival boundary is structural, not semantic

Here is the part that decides whether this bites you, and it is not written down clearly anywhere we could find.

What survives compaction is not what matters most. It is whatever your framework holds outside the thing being summarized. On the Claude API that line is easy to draw, because the system prompt is a separate field on the request rather than an entry in messages, so it is resent intact on every call while everything before the compaction block is dropped. Anything that arrived as a message is fair game: a policy document your retrieval step pulled in, a memory entry your framework injected, a limit the user stated on turn three, a threshold that came back inside a tool result.

Where that line sits is a property of your framework, not a law. ADK is the useful counter-example: its compaction summarizes session history "including instructions", so a rule you set as agent instructions can end up inside the summary rather than beside it. Read your own framework's documentation for which store it compacts and which it leaves alone, and do not assume the Claude API's split applies to it.

Look at how teams actually load rules and the problem becomes obvious. Retrieved policy docs, workspace memory, onboarding turns, and per-tenant configuration all arrive as messages. The governance decay paper makes the same point about its own scenario: the standing rule it studies "is not an immutable system message that the framework promises to preserve." It came in through the message list, so it leaves through the message list.

The survival boundary in agent context compaction: a messages array whose retrieved policy document, injected memory entry, user constraint turn and tool result are struck through above the compaction block, beside a separate system prompt field marked resent every call.

Most teams have never drawn this line, which means they have never checked which side their important rules are on.

Why does the summarizer drop rules first?

Compaction optimizes for continuity. A summarizer is being asked, implicitly or explicitly, to preserve enough state that the next turn can carry on: what the task is, what has been tried, what the current values are, what the user wanted. Score a standing constraint against that objective and it loses. "Never email outside the company domain" tells you nothing about where the work got to. It reads like preamble. The paper's phrasing is that compaction "treats standing policies as low-salience content," which is a polite way of saying your rule looks like boilerplate to the thing deciding what to keep.

The tooling reflects the same priority. Anthropic's documented example of a custom summarization instruction asks the model to focus on preserving "code snippets, variable names, and technical decisions." That is a reasonable thing to want from a coding agent, and it says nothing about constraints.

What it costs, in numbers

The governance decay work quantifies this with a benchmark called ConstraintRot, which grades violations deterministically rather than asking a model to judge them. Across seven models and 1,323 episodes:

  • Compaction raises violation of an in-context constraint from 0% to 30%, and up to 59% on the worst-performing model.
  • Split by outcome, the mechanism is clean: when the constraint survives the summary, violation is 0%. When it is dropped, violation is 38%.
  • Decay is 8.3x larger for soft organizational policies than for hard safety norms.

That last number is the one to sit with. Models have hard safety behaviour trained in, so refusing to help with obvious harm survives a bad summary. What erodes is the deployment-specific rule: the tenant isolation boundary, the spending cap, the "never touch production between 9 and 5" policy. Those exist only in your context, and they are the ones that decay fastest.

Note also what this is not about. The effect holds across models, including strong ones. It is a property of the runtime that manages the context, not of the model reasoning inside it.

There is an adversarial version too. The same paper describes a compaction-eviction attack, where someone who can get text into the agent's context biases the summarizer toward dropping a specific rule. In its evaluation an optimized injection defeated every model tested, including one that had resisted a fixed probe, taking it from 0% to 65%. It needs an injection path to matter, so it is a second-order concern for a closed internal agent and a first-order one for anything reading untrusted web pages or user-supplied documents.

Why pinning the rule is necessary and not sufficient

The obvious fix is to stop the rule from being summarized at all. The governance decay authors propose exactly that, under the name constraint pinning: quarantine governance constraints from lossy compaction and integrity-check them across turns. In their evaluation it restores violation to 0% at under 0.5% token overhead. It is cheap, it works in their setting, and you should do it.

Then a paper from August 2026 complicated the picture in a way that matters for how you verify any of this. AI Guardrail Survival under Single-Cycle Agentic Self-Summarization (submitted 11 August, revised 13 August) asked a narrower question: when a rule is not dropped outright, what state is it in? The answer is that compaction often leaves behind something the authors call a residue, which they summarize in a line worth memorizing: "a presence check is not a safety check."

A guardrail residue is text that still looks like the rule but no longer functions as one. The predicate gets softened, the scope gets generalized, the prohibition becomes a preference. To make the shape concrete with an invented example: "never send email to a recipient outside acme-corp.com" comes back through the summarizer as "be careful when sharing information with external parties", which survives a keyword search for external recipients and authorizes nothing. On behavioural replay, a degraded residue led the model to perform the prohibited action considerably more often than an intact rule did, with all-case gaps of +34 and +57 points under two replay models. And even intact rules sometimes failed to fire.

The authors also explain why this fools people. In their results, rule-form items were retained substantially more often than prominence-matched facts, so if you grep the summary for your constraint, you will usually find something. Finding it feels like passing. Their conclusion is blunt: an audit that checks only textual presence gives false assurance, and this kind of loss is silent at runtime, detectable only against retained external ground truth such as a constraint registry, which "reveals textual absence but not whether a surviving rule still fires."

One honest caveat, which the authors state themselves: all of their results concern a single compaction cycle. A long agent run can compact many times over, and we found no published measurement of what a rule looks like after the fifth pass.

Three parameters that decide the outcome on the Claude API

If you are on the Claude API, the entire behaviour is controlled by three fields inside context_management.edits, and two of them are easy to get wrong.

trigger defaults to {"type": "input_tokens", "value": 150000}, and the value must be at least 50,000. Lowering it to control cost is reasonable, but understand what you are buying: more compaction events over a run, and each one is another chance to lose the rule.

instructions is the footgun. You can supply your own summarization prompt, and the documentation is explicit that it "completely replaces the default prompt when provided." It does not supplement it. So the natural move of adding "preserve code snippets and variable names" to keep your coding agent on track silently discards whatever the model's default prompt was doing, and the default varies by model. If you override it, you own the whole prompt, constraints included.

pause_after_compaction is the useful one, and it is a pinning hook that already exists in the API. Set it to true and the API stops after writing the summary, returning stop_reason == "compaction", so you can append content blocks before the run continues. That is your window to put the rule back from a source that never got summarized.

import anthropic

client = anthropic.Anthropic()

CONSTRAINT_BLOCK = {
    "role": "user",
    "content": (
        "STANDING CONSTRAINT (re-pinned after compaction): never send email "
        "to a recipient outside acme-corp.com. This rule is not superseded by "
        "anything in the summary above."
    ),
}

response = client.beta.messages.create(
    betas=["compact-2026-01-12"],
    model="claude-opus-5",
    max_tokens=4096,
    messages=messages,
    context_management={
        "edits": [{
            "type": "compact_20260112",
            "trigger": {"type": "input_tokens", "value": 150_000},
            "pause_after_compaction": True,
        }]
    },
)

if response.stop_reason == "compaction":
    # Keep the compaction block itself: it is the summary the next call continues from.
    messages.append({"role": "assistant", "content": response.content})
    messages.append(CONSTRAINT_BLOCK)  # re-read from your registry, not from context
    response = client.beta.messages.create(
        betas=["compact-2026-01-12"],
        model="claude-opus-5",
        max_tokens=4096,
        messages=messages,
        context_management={"edits": [{"type": "compact_20260112"}]},
    )

The detail that makes this work is where CONSTRAINT_BLOCK comes from. Read it from a policy file, a database row, or a config object on every compaction. If you build it from something the agent has been carrying in context, you are re-pinning whatever survived, which may already be a residue.

Where does a constraint have to live to be enforceable?

Step back from the parameters and the research and a simple tiering falls out, which is the lens we would apply to any agent design.

Tier one, in the prompt. Advisory. The model reads it and usually complies. Perfectly fine for tone, formatting preference, and which of two approaches to favour. Wrong place for anything with a blast radius.

Tier two, pinned across compaction. Advisory with better odds, plus a genuine continuity benefit, since an agent that can see the rule behaves better than one that cannot. Do this for every rule that matters. Just do not report it as a control.

Tier three, at the tool boundary. Enforcement. The handler validates arguments against a policy the model cannot edit, forget, or summarize, and refuses. This is the only tier where "the rule held" is a fact about your code rather than a claim about a sample of runs.

ALLOWED_DOMAIN = "acme-corp.com"

def send_email(to: str, subject: str, body: str) -> dict:
    if not to.lower().endswith(f"@{ALLOWED_DOMAIN}"):
        return {
            "error": "external_recipient_blocked",
            "message": f"{to} is outside {ALLOWED_DOMAIN}; nothing was sent.",
        }
    return mail.send(to=to, subject=subject, body=body)

That is the whole idea, and its boringness is the feature. Six lines, no model in the decision path, and a structured error the agent can read and recover from rather than an exception that kills the run. Whatever the summary says about email policy on turn four hundred, this function behaves identically.

The direction is not ours. A growing body of work argues the authorization decision belongs before the tool call: pre-action authorization for agents frames it as agents having "passwords but no permission slips," and the policy-as-code argument points out that a system prompt is not an artifact you can hand a regulator. TrueFoundry's write-up of the same research reaches a compatible conclusion, that policy has to live where compaction cannot reach it. We would add one practical note to all of it: the vendors making this argument mostly sell a gateway, and a gateway is a fine implementation, but the tool handler you already wrote is the same decision point and needs no new infrastructure.

If this shape feels familiar, it is because when MCP went stateless, state moved to the server author as well. Protocol plumbing disappeared, the invariant did not, and it landed on whoever owned the server. Same here. Context management can drop your rule; your tool handler cannot.

What this looks like in our own pipeline

The engine that produced this post is itself a long-horizon agent run, roughly ten steps of research, drafting, illustration, and review. Its rules for voice, sourcing, and quality live in files that each step re-reads from disk, and its AGENTS.md states the constraint directly: if a rule or preference is not written in a file here, it is not part of the system, because the orchestrating agent is replaceable and the files are not.

We did that for reproducibility rather than safety. It happens to be the same trick. A rule re-read from ground truth at every step has nothing for a summary to erode. It also does not solve the deeper problem, since a re-read rule still leaves the model as the decider, which is why the checks that actually gate publishing live in scripts rather than in prose.

How to test for this before it bites

You can find out whether your agent has this problem in an afternoon, and the test is behavioural rather than textual.

  1. Write the constraint as an assertion about actions, not strings. "No send_email call with a recipient outside the allowed domain," not "the summary contains the word domain."
  2. Drive the agent past a compaction. Set trigger to its 50,000-token minimum, or force one with whatever hook your framework gives you, so you are not waiting for a real 150,000-token run.
  3. Replay the prohibited request from the post-compaction state. This is the step teams skip, and it is the only one that distinguishes a live rule from a residue.
  4. Assert on what the agent did, then confirm your tier-three check also refused. If the tool boundary caught it, you have defence in depth. If only the prompt caught it, you have a coin flip.
  5. Watch the second-order symptoms, not just the rule. The Nokia and University of Virginia group studying recurrent context compression found it "can weaken the influence of recent interactions, increasing blocked actions, repeated exploration, and instability across runs." Rising run-to-run variance after a compaction is damage even when no policy is involved. Their TRACE framework evaluates individual compaction events by running paired continuations from the same environment state, which is the right unit of analysis: grade the compaction, not just the run. They describe the study as preliminary.

One warning on grading. The guardrail survival authors document evaluation pitfalls where model-judge labels alone would have reversed their conclusion, so keep the pass/fail on this deterministic. If you already run the eval gate we put in front of every model swap, this is a new case to add to it rather than a new test rig to build. The gate design does not change; the scenario does.

What we would not claim

The replay evidence covers a single compaction cycle, by the authors' own statement, and multi-cycle behaviour is unmeasured. The compression instability study calls itself preliminary. Constraint pinning's authors report where their defence fails, including operator impersonation, which they note needs a trusted out-of-band operator channel to close. We have not run ConstraintRot against our own agents, so there are no Vantaso numbers in this post.

None of that changes the architecture, because the architecture does not depend on the percentages. If the number were 3% instead of 30%, you would still not want a model's summarization pass sitting between your policy and its enforcement.

Frequently asked questions

Does context compaction delete my system prompt?

On the Claude API, no: the system prompt is a separate field on the request rather than an entry in the message list, so it is resent on every call while the compacted messages are dropped. That split is framework-specific. ADK's documentation says its compaction summarizes session history including instructions, so check where your own framework keeps agent instructions before assuming they sit outside the summary.

Is this a model problem or a framework problem?

Mostly the framework, or more precisely the layer that manages context. Measured decay appears across all seven models tested and stronger models are not immune, because when the rule is dropped the model never sees it at all. Upgrading the model does not fix a rule that is no longer in the request.

Should I just turn compaction off?

No. Long runs need it, and it exists partly because quality falls off in very long contexts. Manage what it drops instead.

Does constraint pinning fix the problem?

It fixes presence, which is most of the practical benefit at under 0.5% token overhead in the published evaluation. It does not guarantee the rule still fires. Treat pinning as a strong mitigation and the tool boundary as the control.

How do I know if my agent already has this problem?

Force a compaction with a low token trigger, then replay a request your policy forbids and check what the agent actually did. If the refusal disappears after compaction, you found it.

Does this apply to agents that never hit the context limit?

Only if something in your stack compacts early, which is worth checking rather than assuming. Look at your framework's configuration, including threshold fractions of the window and any feature that lets the agent trigger compression itself.

Where to start

Pick the single constraint in your agent with the largest blast radius and find out today which side of your framework's survival boundary it sits on. If it is in the compacted store, pin it, then move the enforcement into the tool handler where a summarization pass cannot reach it. Agent context compaction is not going away, and it should not: the alternative is an agent that stops working at the window limit. What has to change is the assumption that a rule the model can read is a rule the system will keep.

If you are building agents that run long enough for this to matter and want a team that has shipped them before, that is the work we do at Vantaso, or reach us at contact@vantaso.org.

Sources

  1. Governance Decay (Chen, arXiv:2606.22528, 2026-06-21): across seven models and 1,323 episodes, compaction raises violation of an in-context constraint from 0% to 30% (up to 59%); when the constraint survives the summary violation is 0%, when dropped 38%. Decay is 8.3x larger for soft organizational policies than for hard safety norms. The Compaction-Eviction Attack takes an optimized injection from 0% to 65%. Constraint Pinning is a training-free defence restoring violation to 0% at under 0.5% token overhead, with documented failures. Compaction 'optimizes for task continuity and treats standing policies as low-salience content'. The paper states the optimized injection 'defeats every model, including one immune to the fixed probe (0%->65%)'.
  2. Anthropic (2025-09-29) defines compaction as taking a conversation nearing the context window limit, summarizing its contents, and reinitiating a new context window.
  3. Anthropic server-side compaction (beta, header compact-2026-01-12, strategy type compact_20260112): the API detects the trigger threshold, generates a summary, creates a compaction block, and 'automatically drops all content blocks prior to the compaction block'. trigger defaults to {type: input_tokens, value: 150000} and its value must be at least 50,000. pause_after_compaction (default false) lets you add content blocks such as instruction-oriented messages before the response continues, surfacing as stop_reason 'compaction'. Custom instructions 'completely replaces the default prompt when provided' and does not supplement it; the doc's own example preserves code snippets, variable names, and technical decisions. The doc also states 'The default summarization prompt varies by model', that compaction 'keeps the active context small: as a conversation grows, response quality degrades', and lists supported models including claude-opus-5, claude-sonnet-5, claude-fable-5, claude-opus-4-8 and claude-sonnet-4-6.
  4. Google ADK context compaction (Python v1.16.0, Java v0.2.0, TypeScript v0.6.0) reduces context 'by summarizing older session history—including instructions, inputs, and model responses', via CompactionRequestProcessor and EventsCompactionConfig, with token-based and sliding-window strategies, to optimize latency and cost.
  5. Microsoft Agent Framework documents compaction strategies for keeping conversation history within token limits (ms.date 2026-07-30, updated 2026-08-10), applicable to in-memory history agents.
  6. LangChain's deepagents exposes a tool that lets the agent trigger context compression itself; the Deep Agents SDK triggers compression at threshold fractions of the model's context window.
  7. The OpenAI Agents SDK session-memory cookbook implements context summarization in user code via an LLMSummarizer class with a configurable model, max_tokens and a tool_trim_limit; it is something you write rather than a default the SDK runs.
  8. AI Guardrail Survival under Single-Cycle Agentic Self-Summarization (Kwartler, Aqrawi, Abbasi; arXiv:2608.11392; submitted 2026-08-11, v2 2026-08-13): 'a presence check is not a safety check'. A degraded residue leads the model to perform the prohibited action far more often than an intact rule does, with all-case gaps of +34 and +57 points under two replay models; even intact rules sometimes fail to fire. Loss is silent at runtime and detectable only against retained external ground truth such as a constraint registry, which reveals textual absence but not whether a surviving rule still fires. All results concern a single compaction cycle. It also finds that 'rule-form items are retained substantially more often than prominence-matched facts, which is exactly why presence-based checking feels adequate even though survival is not'.
  9. Before the Tool Call: Deterministic Pre-Action Authorization for Autonomous AI Agents (Uchibeke, arXiv:2603.20953, March 2026) argues agents 'have passwords but no permission slips' and puts the authorization decision before the tool call executes.
  10. Policy-as-code for agents (2026-04-25): a system prompt saying 'do not access billing for Tier-2' is not an artifact you can show a regulator; the decision point belongs in the tool loop.
  11. TrueFoundry (2026-08-03) summarizes governance decay as a property of the harness rather than the model, notes constraint pinning is defeated by operator impersonation absent a trusted out-of-band operator channel, and argues policies must live outside the context window.
  12. Toward Reliable Context Compression for Long-Horizon Agents (Min et al., Nokia + University of Virginia, arXiv:2608.06503): a preliminary empirical study showing recurrent compression 'can weaken the influence of recent interactions, increasing blocked actions, repeated exploration, and instability across runs'. Introduces TRACE, which evaluates individual compaction events via paired closed-loop continuations from the same environment state, and argues for boundary-local evaluation.
  13. Practitioner writeup noting LangGraph ships SummarizationNode from LangMem, wired in as a pre_model_hook (i.e. opt-in, wired by the developer).

Get Started

Ready to discuss your project with us?

The future of your industry starts here.

Contact Us