Chapter 3: Managing Long Histories: Truncation, Summarization, and Compaction

Learning Objectives

Pre-Reading Check — Truncation Strategies

1. What is the defining behavior of a sliding-window truncation strategy?

2. Why does every serious implementation treat truncation as selective rather than blindly dropping the oldest messages?

3. What is the difference between a hard limit and a soft limit in history management?

Truncation Strategies

Key Points

Truncation is the bluntest instrument in the toolkit and, precisely because it is blunt, the one every engineer reaches for first. It means removing older content from the conversation history to make room for new content. There is no cleverness involved — you simply delete messages until history fits the budget. The subtlety lies entirely in what you delete and how you measure when to stop.

The simplest strategy, drop-oldest, sends only the last N messages and discards everything before them. A more disciplined version, the sliding window, maintains a fixed-size buffer that advances as the conversation progresses. A useful analogy is the conveyor-belt sushi bar: plates ride past on a belt and you can only reach the few in front of you; as new plates arrive, older ones roll out of reach and eventually leave the room. This gives two great virtues — predictable token usage and predictable cost — but its fatal flaw is that it loses early context entirely. A dietary restriction mentioned in turn 2 is gone by turn 15.

Animation slot: a conveyor belt of message "plates" — new turns slide into the fixed window on the right while the oldest turn falls off the left edge into a "Discarded" bin.

Figure 3.1: Sliding-window truncation — new turns enter the fixed-size buffer while the oldest turns fall off the end.

flowchart LR New["New turn arrives"] --> Window subgraph Window["Fixed-size window (last k turns)"] direction LR T3["Turn n-2"] --> T2["Turn n-1"] --> T1["Turn n (newest)"] end Old["Turn n-k (oldest)"] -.->|"pushed out of reach"| Dropped["Discarded — gone forever"] Window --> Old

Naive drop-oldest has an obvious bug: the very first message you delete is the system prompt — the instructions that define your assistant's personality, rules, and role. The standard pattern therefore separates must-have content (current user message, core instructions, system prompt) from optional content (prior history). Must-haves are always included; optional history is appended only if space remains. This also plays to the "lost-in-the-middle" finding: models perform worse on information buried in the middle than at the very beginning or end, so protecting both ends positions your important content where the model attends best.

Finally, the more robust approach truncates by token count, not message count. A "keep the last 20 messages" rule is fragile because one turn might be a two-word "yes" and the next a 4,000-token stack trace. Two thresholds matter: the hard limit is the model's absolute context size (exceed it and you get context overflow — a failed request), while the soft limit is a self-imposed threshold below it where you begin managing history. It is the fuel gauge's warning light versus running the tank dry on the highway.

Key Takeaway

Review Check — Truncation Strategies

1. What is the defining behavior of a sliding-window truncation strategy?

2. Why does every serious implementation treat truncation as selective rather than blindly dropping the oldest messages?

3. What is the difference between a hard limit and a soft limit in history management?

Pre-Reading Check — Summarization of History

4. What is the Markovian property that makes recursive summarization efficient?

5. In salience-driven memory formation, which content is most important to preserve for a hyper-personalized assistant?

6. What is "summary drift," and why is it a consequence of the Markovian property?

Summarization of History

Key Points

Truncation's core problem is that deleted context is gone forever. Summarization attacks that directly: instead of throwing older messages away, you compress them into a digest the model can still reference. You trade a little cost (an extra LLM call) and accept some lossy compression in exchange for keeping the gist.

A rolling summary maintains a continuously updated digest of older messages, keeping the most recent turns verbatim and compressing the rest when token counts exceed a limit. The canonical implementation is LangChain's ConversationSummaryBufferMemory: recent messages stay verbatim in a buffer, and when the buffer exceeds max_token_limit, the oldest messages are folded into a running summary. Recursive summarization goes further — it iteratively updates a single memory from (previous memory + current session), so if earlier memory captured "enjoys walking" and a new session mentions the gym, the integration yields "enjoys walking; recently joined a gym." Because memory depends only on the current session and the previous memory, it has a Markovian property that keeps it cheap. Think of it as a running journal: each night you read yesterday's summary, add today's events, and write a new consolidated entry — growing in coverage while staying constant in length.

Figure 3.2: The recursive-summarization loop — memory is updated from the previous memory plus the current session, never from raw history.

flowchart TD Init["Initialize memory: empty"] --> Session["New completed session content"] Session --> Update{"Update memory: previous memory + current session"} Update --> Memory["Updated memory (compact)"] Memory --> Respond["Generate response: recent memory + current dialogue"] Memory -.->|"feeds back as previous memory (Markovian)"| Update Respond --> Session
Animation slot: a "telephone game" demo — a fact passes through successive summaries, each step subtly distorting it, to visualize summary drift versus a verbatim-preserved copy.

The heart of good summarization is deciding what deserves verbatim treatment. This is salience — the property of being important enough to remember. Rather than compressing everything uniformly, salience-driven systems identify the specific facts, preferences, and patterns worth keeping. For a personalized assistant the salient content is the personalization payload: durable facts ("vegetarian," "prefers metric units"), open commitments, and decisions already made. Production systems formalize this with a multi-level hierarchy — immediate working memory (current session), episodic memory (important past moments), and semantic memory (general facts).

Summarization has two characteristic failure modes. Fact loss is when details vanish in compression — "discussed travel plans" has silently discarded the date, destination, and budget. Summary drift is subtler: because each new memory is generated from the previous summary, small errors compound over iterations like a game of telephone, leaving the assistant confidently wrong. The defenses are twofold: keep recent turns verbatim, and extract truly critical facts into a stable semantic-memory store rather than trusting them to survive repeated summarization.

Key Takeaway

Review Check — Summarization of History

4. What is the Markovian property that makes recursive summarization efficient?

5. In salience-driven memory formation, which content is most important to preserve for a hyper-personalized assistant?

6. What is "summary drift," and why is it a consequence of the Markovian property?

Pre-Reading Check — Context Compaction

7. What makes context compaction different from ordinary chat summarization?

8. What does setting pause_after_compaction: true allow you to do?

9. Why is a structured summary generally more reliable than a free-text summary for compaction?

Context Compaction

Key Points

Context compaction is summarization applied at scale to agentic workflows. Agentic tools like Claude Code accumulate an enormous amount into a single continuous prompt: files read, commands executed, grep results, diffs, and error messages all pile up. Without intervention the assistant hits context overflow — crashing or going "off the rails" as its instructions get buried. Compaction is the automatic release valve.

Compaction fires on a token threshold. Claude's server-side compaction activates when input tokens reach a configured threshold — default 150,000 tokens, minimum 50,000 — then generates a summary, creates a compaction block containing it, and removes all content blocks before that block. The Claude Code CLI exposes the user-facing /compact command, which fires automatically at roughly 95% of the window limit. One billing subtlety: compaction adds a sampling iteration to your bill (visible in the usage.iterations array), and pairing it with prompt caching recovers much of that cost.

Figure 3.3: The compaction-trigger decision flow — the API compacts only when input tokens cross the configured threshold.

flowchart TD Start["Incoming request"] --> Check{"Input tokens >= threshold? (default 150K, min 50K)"} Check -->|"No"| Continue["Continue normally — no compaction"] Check -->|"Yes"| Summarize["Generate summary of older conversation"] Summarize --> Block["Create compaction block with summary"] Block --> Remove["Remove all content blocks before compaction block"] Remove --> Resume["Continue response with compacted context"]

The defining requirement of compaction — the thing that separates it from ordinary chat summarization — is that it must preserve task state. If an agent is halfway through refactoring a module and compaction wipes out which files it already changed and why, it will flail afterward. Two mechanisms guard against this. Pinning via pause_after_compaction halts execution after the summary is generated, letting you manually preserve specific recent messages (e.g., the last ~3) before continuing. On-disk checkpoints persist critical state outside the conversation: Claude Code's CLAUDE.md survives every compaction because it is reloaded from disk as part of the system prompt. The lesson is concrete — move critical rules into a file that reloads after compaction, and create checkpoints like TODO.md so the agent can re-read its own progress. Think of compaction as a mountaineer's base camp: you record your altitude, route, and next objective on a card, then leave the rest behind.

Animation slot: a token gauge filling toward the 150K threshold; when it crosses, pre-compaction blocks collapse into a single "compaction block" while a pinned recent-messages tail and an on-disk CLAUDE.md icon stay intact.

Compaction summaries come in two flavors. A free-text summary is a natural-language paragraph — flexible but prone to omitting fields inconsistently. A structured summary imposes a schema: distinct slots for open tasks, files modified, decisions made, and user preferences. Structure is what makes state survive, because a schema reminds the summarizer what to capture every time. You steer this via the instructions field (or Claude Code's compactPrompt); structured-distillation approaches report roughly 11x token reduction while preserving retrievable information.

Key Takeaway

Review Check — Context Compaction

7. What makes context compaction different from ordinary chat summarization?

8. What does setting pause_after_compaction: true allow you to do?

9. Why is a structured summary generally more reliable than a free-text summary for compaction?

Pre-Reading Check — Hybrid Strategies in Practice

10. In the "verbatim recent + summarized distant" hybrid, how do the memory tiers map to the conversation?

11. In a personalized assistant, which content are the natural candidates for pinning?

12. Why should you track dropped-segments metadata alongside per-call token counts?

Hybrid Strategies in Practice

Key Points

No single strategy wins outright, which is why production systems almost always combine several. Before assembling a hybrid, it helps to see the four core strategies side by side.

StrategyHow it worksCostFidelityBest forKey weakness
Truncation (drop-oldest)Delete oldest messages until history fitsCheapest (no LLM call)Lowest — early context lostSimple, short interactionsSilently loses early instructions and preferences
Sliding windowFixed-size buffer of last k turns advances forwardCheap, very predictableLow — distant history goneNaturally bounded conversationsNo memory of distant turns
SummarizationCompress older turns into a running digest; keep recent verbatimModerate (extra LLM call)Medium-high — gist preservedLong personalized chatFact loss and summary drift
CompactionThreshold-triggered structured summary that preserves task stateModerate (adds an iteration)Medium-high, task-state focusedLong agentic sessions with toolsCan drop reasoning/snippets unless pinned

The workhorse hybrid — and the pattern to reach for by default — is verbatim recent plus summarized distant. Recent exchanges stay raw; older content is folded into summary form; and the very oldest, most durable facts may be extracted into a long-term store. This mirrors the tiered memory model directly: short-term verbatim history (working), medium-term compressed summaries (episodic), and long-term extracted facts (semantic). The tiers are complementary — verbatim recency keeps the immediate conversation coherent, while summaries and extracted facts give access to older information without paying full token cost.

Figure 3.4: The hybrid tiered-memory layout — verbatim recent turns, compressed distant history, and extracted durable facts assembled into a single prompt.

flowchart TD History["Full conversation history"] --> Recent["Recent turns: kept verbatim (working memory)"] History --> Distant["Distant turns: compressed summary (episodic memory)"] History --> Facts["Durable facts extracted to long-term store (semantic memory)"] Recent --> Prompt["Assembled prompt within token budget"] Distant --> Prompt Facts --> Prompt
Animation slot: three colored memory tiers (verbatim / summarized / extracted facts) flowing into one assembled prompt bar, with a "pin" icon locking an allergy fact so it never enters the compressible stream.

Hybrids need a way to say "this message is too important to compress." That is pinning — marking specific messages as protected so they always survive truncation and compaction. It separates must-have content (current message, core rules, pinned facts) from optional history, always including the former. More sophisticated systems perform dynamic context selection, using keyword matching or semantic-similarity scoring to surface relevant history. In a personalized assistant the natural candidates for pinning are the personalization primitives: allergies, accessibility needs, hard constraints ("never email my boss"), and explicit instructions. When in doubt whether a fact is safe to compress, pin it.

Finally, you cannot manage what you do not measure, and every strategy here deliberately throws information away. Two practices make that loss visible: track dropped-segments metadata alongside token counts (a spike in re-clarifications like "I already told you..." correlating with a compaction event signals lost salience), and use token-counting tooling (e.g., /messages/count_tokens) to confirm your soft-limit logic behaves as intended. Choosing among strategies is ultimately a decision tree: bounded conversation → sliding window; long personalized chat → rolling summarization; long agentic task → compaction with pinning and checkpoints; and when relevant history grows huge, reach for retrieval (RAG).

Key Takeaway

Review Check — Hybrid Strategies in Practice

10. In the "verbatim recent + summarized distant" hybrid, how do the memory tiers map to the conversation?

11. In a personalized assistant, which content are the natural candidates for pinning?

12. Why should you track dropped-segments metadata alongside per-call token counts?

Your Progress

Answer Explanations