Part I followed one task through the runtime. Part II separated long-lived memory ledgers from transcript. This chapter starts after that split: the word context is overloaded. In Claude Code it can mean project memory, user instructions, repository status, visible conversation messages, hidden runtime metadata, compact summaries, or API request blocks. Reading the code becomes easier once those surfaces are separated.

type ContextSurfaces = {
  durableTranscript: Message[]
  baseContext: SystemContext | UserMemory
  modelVisibleView: ProviderMessage[]
  pressureRelief: "replacement" | "collapse" | "compact"
  recoveryBoundary: "compact summary" | "resume replay"
}
Source shape: context management is easier to read once durable history, base context, and provider-visible projection are separate surfaces.

1. Context starts before the chat history

The initial user and system context are built outside the model loop. User memory can come from CLAUDE.md files and related memory sources. System context captures facts such as the working directory, git state, and environment details. Later, appendSystemContext() and prependUserContext() place those facts into the provider request in different positions.

Different Claude Code context surfaces, including memory, system context, transcript, model view, and API request
The key move is to stop treating every stored fact as something that must be sent to the model every time.

2. The model-visible view is rebuilt every turn

Inside queryLoop(), the runtime constructs the current API view. It does not blindly replay the whole transcript. It starts from the active compact boundary, applies the tool-result budget, lets HISTORY_SNIP lower historical pressure when that feature is present, runs microcompact, projects any committed Context Collapse summaries, and only then asks whether auto compact must take over.

That difference explains a common surprise: a message can be visible in local history, present in transcript storage, and still absent from the next API request. Context management is a projection problem.

3. Pressure is layered

Context pressure arrives from multiple directions. Tool results can be large. Repeated edits can create bulky replacement state. Long reasoning chains add assistant blocks. System and memory context consume prefix space before the conversation even begins. The runtime therefore has several valves, not one big summarizer.

Stack of context pressure sources: system prompt, memory, messages, tool results, replacement state, and compact boundary
When the request gets heavy, Claude Code can trim or replace different layers for different reasons.

4. Compact is a boundary, not just shorter text

A compact operation creates a new boundary in the conversation. From that point, later API requests can reason from a compacted representation instead of replaying every earlier message. This is why compact metadata matters to resume and recovery. The runtime needs to know not only the summary text, but also where the old context ended and which local state still belongs to the session.

The summary is also a model request, not an editorial afterthought. The compact path builds a no-tools summary request, installs the resulting summary as a recovery message, and can share the parent prompt-cache prefix for that side path. That is the bridge to the final chapter: summary quality matters, but request shape still decides how expensive the handoff becomes.

Compact and recovery loop from long transcript to compact boundary and resumed state
Compact changes the route by which earlier work re-enters the model-visible view.

5. Microcompact handles local pressure

Microcompact is a smaller valve. It is used to control high-pressure parts of the prompt without forcing a full user-facing compact. The source separates three cases that are easy to blur together. HISTORY_SNIP runs before microcompact and returns a tokensFreed estimate so later auto-compact checks do not overcount the surviving assistant usage.

What does snip do? It is not a clock-based deletion of the oldest prefix. If the first user message contains the task, constraints, and acceptance criteria, deleting from the beginning would lose the point of the session. Claude Code already has compact boundaries for prefix replacement. Snip is the smaller operation for selected middle ranges inside the active context.

Who decides that a range is low-value? The public code shows that Claude Code registers a feature-gated SnipTool, adds stable [id:...] tags to API-bound user messages so Claude can reference exact messages, and injects a context_efficiency attachment after enough context growth without a snip. So low-value is not a visible timestamp heuristic in the public source. It is a model-participating context-retention decision: which middle messages are no longer critical for the task, file state, tool references, or user constraints, and can therefore leave the next model-visible view.

Not this:
  U0(initial task) A1 T1 A2 T2 ... A80 T80
  -> delete the oldest N messages by age

Closer to this visible contract:
  API-bound copy:  U0[id:a1] A1 T1 U2[id:b7] ... U80[id:k9]
  snip boundary:   removedUuids = [uuid(T12), uuid(A13), uuid(T13), ...]
  next API view:   U0 A1 T1 U2 ... [middle range filtered] ... U80

Local UI / JSONL:
  original messages remain; resume replays the same removedUuids deletion
Snip's visible contract is UUID-based projection out of the model view, not age-based prefix truncation.

The feature-gated selector implementation is not present in the public TypeScript tree, so the exact SnipTool prompt, scoring rule, threshold, and selector policy are not visible. The surrounding contracts are clear, though. Model-facing paths call projectSnippedView() after the compact-boundary slice, while UI rendering can pass includeSnipped: true so scrollback still shows the full local history. Resume code records removedUuids on the snip boundary, deletes those middle ranges on load, and relinks parent pointers across the gap. Source comments also say tool-reference-bearing messages are protected from snip. In other words, snip removes selected middle ranges from the next model-visible view; it does not rewrite old tool results into a placeholder and it does not erase the append-only transcript evidence.

Time-based microcompact is the path that replaces old tool results with [Old tool result content cleared]; its own comment says this fires when the server cache is already cold, so the next request would rewrite the prefix anyway. Cached microcompact is different again: when cache editing is available, local messages stay unchanged and Claude Code first records pendingCacheEdits. Later, services/api/claude.ts, the Anthropic API adapter inside Claude Code, translates those edits into a cache_edits block while building the provider payload.

This should not be confused with ordinary public prompt caching. Public Anthropic prompt caching is centered on cache_control, cache writes, and cache reads. The source path for cached microcompact additionally requires the feature gate, model support, a firstParty main-thread request, and a cache-editing beta header. So cache_edits is best read as a first-party beta cache-editing capability expressed through the Anthropic Messages API payload, not as a stable field available to every ordinary Messages API caller.

shape-level payload fragment:
  {
    "type": "tool_result",
    "tool_use_id": "toolu_abc",
    "cache_reference": "toolu_abc",
    "content": "..."
  }

  {
    "type": "cache_edits",
    "edits": [
      { "type": "delete", "cache_reference": "toolu_abc" }
    ]
  }
cache_reference names the cached old tool result; cache_edits asks the cache-editing path to delete that reference.

The point of cache_edits is reference-based deletion. Claude Code marks cached-prefix tool_result blocks with cache_reference, usually the original tool_use_id. A cache_edits block then says to delete a specific cache_reference. The local transcript still keeps the full tool result, but the provider-side cache view can drop that old result. Inserted edits are pinned to the same user-message position and resent so the request prefix remains stable. The later cache_deleted_input_tokens usage field is used to report how many tokens the provider actually deleted. Snip can still change prompt-cache shape because the provider sees a shorter message sequence, but that is a model-view projection, not the placeholder path and not the cache-edit path.

Context Collapse is another projection layer, not simply a synonym for full compact. The shape is: archived span summary from the collapse store plus the newer incremental messages after that span. The REPL history and recovery metadata remain owned outside the API payload. This can change prompt-cache shape on the transition where the provider first sees the shorter projected prefix, but it also creates a smaller stable prefix for subsequent turns and can avoid a more destructive auto compact.

6. Recovery needs more than a summary

Resume code loads conversation messages, file history, content replacement state, compact-collapse metadata, and session metadata. That is a stronger claim than load the old transcript. The runtime wants to rebuild a state that can continue making tool calls, checking permissions, and projecting a request correctly.

Claude Code context survival map showing which state is visible, stored, compacted, or recovered
The useful question is not whether something is context, but where it survives and who can read it next.

7. The invariant

Context management is the art of preserving the right facts at the right layer. Memory influences the prefix. Transcript preserves evidence. Compact rewrites the route back into old work. Microcompact reduces local pressure. Prompt cache rewards stable request shape. Once those layers are separate, the code stops looking like a pile of special cases.

Sources

The source-code claims in this article are based on the public mirror and the linked official documentation. Server-side behavior and private feature-gate policy are treated only as client-visible request shape.