The injection channel: the rule underneath Claude Code
TL;DR. The deepest habit in Claude Code's design is one sentence: never edit the prompt,
always append to it. The system prompt and tool schemas stay byte-stable for the life of a
session, and everything dynamic the harness must tell the model mid-turn (todo state, a file
edited behind its back, newly loaded tools, the date, hook output, memory recalls) arrives as
harness-injected content in the user channel, rendered as <system-reminder> blocks and
recorded in the transcript as typed attachment records. This chapter dissects that channel to
the bottom: a census of this machine's transcripts finds 22 distinct injection types doing
that work, and a continuity measurement across 8,086 consecutive billed turns shows the cached
prefix surviving intact on 98.1 percent of them, which is the mechanical source of the 96.8
percent hit rate the ledger measured. The same rule then becomes a
lens on the open-source ecosystem: it is why Headroom ships a CacheAligner, why Letta's editable
core memory pays a cache bust per edit, and why a proxy that "helpfully" stamps metadata into your
system prompt is quietly the most expensive middleware you can run.
Contents
- The invariant, stated precisely
- The three instruction channels
- The census: 22 ways Claude Code whispers
- The proof: prefix continuity across 8,086 turns
- Why the tail placement also works for the model
- The same rule as a lens on open source
- The senior playbook
- Further reading
- Takeaways
Chapter 17 mapped what is in the window; Chapter 24 proved one changed byte upstream re-bills everything downstream. Put those together and a harness that runs for hours faces a genuine engineering dilemma: it constantly has new things to tell the model (your todos changed, a file was edited outside the session, the clock rolled past midnight, a hook fired, ten new tool schemas loaded), and the naive places to say them (the system prompt, the tool list) are exactly the places whose bytes must never change. Claude Code's answer to that dilemma is the thing experienced users eventually reverse-engineer from transcripts and this chapter lays out directly.
The invariant, stated precisely
Every request Claude Code sends has the shape from Chapter 6: tools, then system prompt, then the conversation. The invariant is about who may write where:
- The prefix is write-once. The system prompt and the initially loaded tool schemas are rendered at session start and never edited afterward. Not for the date, not for your todo list, not for a mode change. Anything that would tempt an engineer to interpolate a variable into the system prompt is expressed some other way.
- All dynamism flows through the conversation's tail. New information enters as appended
content: tool results (the model's own reads), and harness injections, blocks of
operator-authored text the harness slips into user turns alongside (or instead of) anything
the human typed. In the live request they appear as
<system-reminder>...</system-reminder>blocks; in the transcript on disk they are recorded as typedattachmentrecords. - Even your
CLAUDE.mdobeys it. The project memory is not part of the system prompt; it is delivered through this channel at the start of the conversation, which is why Chapter 17 could show it in the Messages region of/context. EditingCLAUDE.mdmid-session therefore does not silently re-bill the whole window; the new text simply arrives as new appended content. - Growth is also append-only. When deferred MCP tools or skills load mid-session (Chapter 17), their schemas are appended rather than spliced into the original tool list, because a splice would change byte zero of the prefix and void everything (Chapter 24's experiment D, avoided by construction).
The payoff of the invariant is arithmetic. An edit at prefix position $p$ re-bills every token after $p$ at write rates on the next request; an append of $k$ tokens at the tail bills $k$ tokens once and extends the cache. For a 200,000-token session, a 50-token "helpful" edit to the system prompt costs roughly 200,000 tokens of cache rewrite; the same 50 tokens appended cost 50. The invariant is nothing more than always choosing the second column, enforced everywhere.
The three instruction channels
Once you see the invariant, Claude Code's instruction architecture resolves into three channels with different authority, different cache behavior, and different security properties:
| Channel | Who writes it | Cache effect | Authority and trust |
|---|---|---|---|
| System prompt + tool schemas | The harness, once, at session start | The cached prefix itself | Operator authority; static by contract |
<system-reminder> blocks in user turns | The harness, any turn | Pure append; cache-neutral | Harness context. The model is told these are injected by the harness, not written by the user, but they are still text in the user channel: anything that can write into that channel could imitate one, so they carry guidance, not hard security guarantees |
role: "system" messages inside messages[] | The application, mid-conversation (Claude Opus 4.8 and the API feature from Chapter 24's further reading) | Pure append; cache-neutral | True operator authority: a message role, not text inside someone else's turn, so it cannot be forged by content |
The middle channel is the workhorse and the one this chapter is about. It is how the harness gets the benefits of "updating the system prompt" (fresh operator guidance, visible late in the context) with none of the costs (no cache bust, no re-render). The third channel is the API formalizing the same pattern: when Anthropic shipped mid-conversation system messages, the stated rationale was exactly this trade, deliver operator instructions mid-session without invalidating the cached prefix. The injection channel is the same idea implemented in userland, years of production agent-running distilled into a message-shape convention.
Don't be confused. Three things sound alike and are not. The system prompt is the static preamble at position zero: maximal authority, frozen bytes. A system reminder is harness text appended inside a user turn: cache-free, recency-placed, but ultimately content, not a role. A system message (
role: "system"inmessages[]) is a real role the API understands, appended mid-conversation: operator authority and cache safety. When you build your own harness, that is also your decision ladder: frozen prefix for identity and rules that never change, role-system appends for mid-session policy where the model supports it, reminder text where it does not.
The census: 22 ways Claude Code whispers
The claim that "everything dynamic goes through the channel" is checkable, because the
transcripts record every injection as a typed attachment line. The lab walks every transcript
on this machine and counts them, then runs the continuity measurement of the next section:
"""The injection channel, measured from real Claude Code transcripts.
This book's claim about Claude Code's deepest design rule is testable:
Nothing dynamic is ever EDITED into the prompt. The prefix (system
prompt, tool schemas) stays byte-stable, and everything the harness
needs to tell the model mid-session (todo state, file edits, new
tools, the date, hook output) is APPENDED into the conversation as
harness-injected content in the user channel.
If the rule holds, two things must be visible in the transcripts under
~/.claude/projects/:
1. A CENSUS of harness injections: transcript lines of type "attachment"
(the recorded form of the injections) with their own vocabulary of
types, separate from the human's actual messages.
2. PREFIX CONTINUITY in the usage blocks: on consecutive billed turns,
this turn's cache_read_input_tokens should equal the PREVIOUS turn's
cache_read + cache_creation (the cache accretes; nothing upstream
changed). Every violation is a cache reset, and there should be few.
This script measures both. Standard library only:
python3 injection_census.py [projects_dir]
"""
import json
import sys
from collections import Counter
from pathlib import Path
def turns_of(path):
"""Yield one (usage, requestId) per billed request, plus attachment types.
A single API response is written as SEVERAL assistant lines (one per
content block) that share a requestId and carry the same usage block,
so we dedupe by requestId to avoid counting one request many times.
"""
attachments = Counter()
turns = []
seen_req = set()
for line in path.open(errors="replace"):
try:
d = json.loads(line)
except json.JSONDecodeError:
continue
if d.get("type") == "attachment" and "attachment" in d:
attachments[d["attachment"].get("type", "?")] += 1
elif d.get("type") == "assistant" and not d.get("isSidechain"):
u = (d.get("message") or {}).get("usage")
rid = d.get("requestId")
if u and rid and rid not in seen_req:
seen_req.add(rid)
turns.append(u)
return attachments, turns
def main():
root = Path(sys.argv[1]) if len(sys.argv) > 1 else Path.home() / ".claude/projects"
census = Counter()
extends = resets = pairs = 0
biggest_reset = 0
for f in sorted(root.rglob("*.jsonl")):
if f.parent.name == "subagents":
continue # sidechains have their own prefixes; measure mains only
att, turns = turns_of(f)
census.update(att)
for prev, cur in zip(turns, turns[1:]):
prev_total = (prev.get("cache_read_input_tokens", 0)
+ prev.get("cache_creation_input_tokens", 0))
cur_read = cur.get("cache_read_input_tokens", 0)
if prev_total < 10_000:
continue # ignore tiny warm-up turns; they prove nothing
pairs += 1
if cur_read >= 0.95 * prev_total:
extends += 1 # the accretion signature: cache grew intact
elif cur_read < 0.5 * prev_total:
resets += 1 # the prefix broke (clear/compact/edit)
biggest_reset = max(biggest_reset, prev_total - cur_read)
print(f"=== Injection census from {root} ===")
print("Harness 'attachment' records (dynamic state APPENDED, never edited in):\n")
for t, n in census.most_common():
print(f" {t:<28}{n:>6}")
print(f"\n=== Prefix continuity across {pairs:,} consecutive billed turns ===")
print(f" cache EXTENDED intact (read >= 95% of prior read+write): "
f"{extends:,} ({extends / max(pairs,1) * 100:.1f}%)")
print(f" cache RESET (read < 50% of prior): "
f"{resets:,} ({resets / max(pairs,1) * 100:.1f}%)")
print(f" largest single reset: {biggest_reset:,} tokens re-billed as writes")
print("\nReading: the extended share is the byte-stable-prefix rule holding in")
print("production. The resets are the sanctioned breaks (/clear, /compact,")
print("setup edits), each of which re-bills the window once at write rates.")
if __name__ == "__main__":
main()
Running it against this machine's real history (a snapshot from the day of writing; the counts grow as the machine works, and the session writing this chapter is itself adding rows):
=== Injection census from /Users/s0x/.claude/projects ===
Harness 'attachment' records (dynamic state APPENDED, never edited in):
todo_reminder 918
edited_text_file 290
queued_command 150
deferred_tools_delta 69
agent_listing_delta 61
skill_listing 56
task_reminder 53
file 50
hook_additional_context 46
date_change 37
directory 16
hook_success 10
compact_file_reference 9
command_permissions 7
auto_mode 6
plan_mode_exit 6
nested_memory 6
plan_mode 5
plan_file_reference 5
task_status 4
opened_file_in_ide 3
pdf_reference 1
=== Prefix continuity across 8,086 consecutive billed turns ===
cache EXTENDED intact (read >= 95% of prior read+write): 7,932 (98.1%)
cache RESET (read < 50% of prior): 151 (1.9%)
largest single reset: 991,121 tokens re-billed as writes
Read the census as a design document, because each row is a dilemma resolved the same way:
todo_reminder(918) is the most frequent injection on the machine, and it is the drift control: the current todo list re-surfaced near the end of the context, where attention is strongest (next section), instead of a "current tasks" section edited into the prompt where every tick would cost a cache rewrite.edited_text_file(290) is the staleness defense: when a file changes outside the model's own edits (you, a linter, another session), the harness appends the fresh snippet rather than letting the modelEditagainst a stale mental copy. Notice what this really is: cache coherence for the conversation, implemented through the same channel.deferred_tools_delta(69),skill_listing(56),agent_listing_delta(61) are the append-only growth rule: capability arrives as a delta at the tail, never a splice at position zero. This is the same design the API's tool-search feature uses, and for the same stated reason: discovered tool schemas are appended so the prompt cache is preserved.date_change(37) is Chapter 24's experiment B, institutionally avoided. The single most common caching bug in user-built agents (datetime.now()in the system prompt) cannot happen here, because the date lives in the channel and updates only when it actually changes, 37 times in this whole history rather than once per request.hook_additional_context(46) andhook_success(10) are the extension story: your hooks (Chapter 19) speak to the model through the same door as the harness's own machinery. A hook that emits context is doing a system-prompt edit's job at an append's price, and it is worth internalizing that every byte a hook prints is paid conversation tokens on every later turn.compact_file_reference(9) andnested_memory(6) are compaction and the memory hierarchy (Chapter 18) leaving their fingerprints: pointers to what was folded away, injected so the model knows where the detail went.
The proof: prefix continuity across 8,086 turns
The census shows the mechanism exists; the continuity number shows it works. The measurement
leans on Chapter 23's accretion signature: if nothing upstream changed,
this turn's cache_read_input_tokens must equal the previous turn's cache_read + cache_creation. Across every consecutive pair of billed main-thread turns on this machine
(warm-up pairs below 10k tokens excluded):
- 98.1 percent of turn pairs extended the cache intact. Eight thousand opportunities for a stray byte to break the prefix; it broke on 151.
- The 1.9 percent of resets are the sanctioned breaks, not bugs:
/clear,/compact(which by design rewrites history, Chapter 11), settings and model changes. The largest single reset re-billed 991,121 tokens as writes, essentially a full 1M window paid once, which is simultaneously the cost of one compaction event and the amount the invariant saves on every other turn by not editing.
That pair of numbers is the quantified version of this book's central Claude Code claim. The ledger's 96.8 percent cache hit rate is not luck and not a provider gift; it is 98.1 percent turn-level discipline compounding, purchased by routing all dynamism through an append-only channel.
Why the tail placement also works for the model
Cache economics explain why injections must not go at the front. They do not explain why the harness wants them at the back, and that half is about attention. Long-context models retrieve information best from the beginning and end of the window and worst from the middle, the "lost in the middle" result (Liu et al., 2023), and instruction adherence drifts as a session grows: a rule stated once, 400,000 tokens ago, competes with everything since.
The injection channel turns that weakness into a placement strategy. The rules that define the
agent sit at the very front (position bias favors them, and they are cached); the state that
must steer this turn, todos, fresh file contents, "the user just toggled auto mode", arrives
at the very end, inside the recency window, re-asserted as often as it changes. A 918-count
todo_reminder row is what instruction maintenance looks like when you cannot afford to edit
the prompt and cannot trust the middle of the window: say it again, cheaply, at the position
the model actually reads. When your own agent "forgets" a constraint deep into a long session,
this is the fix that works: do not make the system prompt louder
(Chapter 20's overtriggering lesson); re-inject the constraint at the
tail when it becomes relevant.
The same rule as a lens on open source
Hold the invariant up to the ecosystem from Chapters 15, 26, and 27 and the projects sort themselves by how they answer the same question: where does dynamic context enter the prompt?
- Headroom's
CacheAligner(Chapter 27) is the invariant sold as a product: it reorders request blocks stable-first and volatile-last, mechanically producing the shape Claude Code maintains by convention. If your hand-rolled agent cannot adopt the discipline, this is the retrofit. - Letta (MemGPT) is the instructive counter-example. Its signature feature, editable "core
memory" blocks that live inside the system prompt, deliberately violates the rule: every
core_memory_replacerewrites the prefix and voids the cache for the whole conversation. That is not a bug but a priced trade: Letta buys always-visible, operator-authoritative memory at the front (position bias working for it) and pays a full cache rewrite per edit. On a 200k-token session, one memory edit costs more input-side than fifty turns of Claude Code's reminder injections. If you build on Letta, batch core-memory edits and keep the frequently changing facts in its archival memory (retrieved and appended, cache-safe) instead. - Mem0, Zep, Graphiti, and every RAG layer (Chapter 9, Chapter 10) are natural citizens of the channel: retrieval results enter as tool results or appended context at the tail. The mistake to refuse is the tempting "personalization" pattern of interpolating retrieved user facts into the system prompt, which converts a cache-free append into a per-request prefix rewrite, Chapter 24's experiment B wearing a memory costume.
- Aider's repo map is volatile by nature (it re-ranks with the task), and aider places it in the chat as a message it refreshes deliberately rather than baking it into the static prompt; Serena (Chapter 27) goes further and delivers code context exclusively through tool results, the most channel-native design possible for an MCP server. When you write your own MCP server, that is the standard: results are injections, so make them terse, stable where possible, and never timestamped decoratively.
- LangGraph encodes the split structurally if you let it: a frozen system template, state flowing through the message list. The anti-pattern it will happily let you build is a node that re-renders the system prompt from state each turn; now you know exactly what that costs and where the state should go instead.
- Proxies and gateways (LiteLLM and friends, Chapter 26) sit on the request path, which means they can mutate prompts, and any middleware that prepends a request id, a routing tag, or a "processed by" stamp into the system prompt is a silent invalidator installed at infrastructure level, poisoning every application behind it. The audit is the same two-call test as always (Chapter 24): identical request twice through the proxy; if the second is not a cache read, the middleware is editing where it should be appending (or not touching the prompt at all).
The senior playbook
The rules that fall out, in the order they save money:
- Freeze the prefix like a contract. System prompt and tool list are rendered once and never touched mid-session. Anything "dynamic" you are about to interpolate into them goes to rule 2.
- Append, in one of two shapes. Operator policy mid-session: a
role: "system"message on models that support it, a clearly delimited reminder block in the user turn otherwise. State and data: tool results and tail-injected context. Both are cache-neutral. - Re-assert instead of amplifying. Adherence drift deep in a session is fixed by re-injecting the constraint at the tail when it matters, not by shouting in the prefix.
- Treat resets as purchases.
/clear,/compact, a settings change, a Letta core-memory edit: each re-bills the window once. Buy them deliberately (the gauges chapter tells you when they are worth it), never accidentally. - Audit the whole path, including middleware. Your code, your hooks, your proxy, your framework: any of them can edit where they should append. The continuity measurement in this chapter's lab is the audit, and it runs on data you already have.
- Hold your extensions to the harness's own standard. Hook output, MCP results, and skill bodies all travel the channel and are all re-billed every turn they remain in history; write them like telegrams.
Further reading
- Liu et al., "Lost in the Middle: How Language Models Use Long Contexts" (arxiv.org): the positional-attention result behind the tail-placement strategy.
- Anthropic's prompt-caching and mid-conversation system message documentation
(
platform.claude.com/docs): the API-level formalization of the append-not-edit trade this chapter describes in the harness. - Claude Code hooks (
code.claude.com/docs): the supported way to write into the injection channel yourself, with Chapter 19 for the configuration surface. - Letta's memory documentation (
docs.letta.com) read side by side with Chapter 24: the clearest ecosystem example of the trade-off taken the other way, eyes open.
Takeaways
- Claude Code's deepest rule: never edit the prompt, always append. The prefix is write-once;
every dynamic fact travels the injection channel as
<system-reminder>content in user turns, recorded as typedattachmentrecords in the transcript. - The census found 22 injection types on this machine, each a dilemma resolved the same way: todo drift control, file-staleness defense, append-only tool growth, the date without the silent invalidator, hooks and memory speaking through the same door.
- The proof is quantitative: across 8,086 consecutive billed turns, the cache extended intact on 98.1 percent; the 1.9 percent of resets are sanctioned breaks, the largest re-billing a 991k-token window once. That discipline, compounded, is the ledger's 96.8 percent hit rate.
- Placement is doing double duty: frozen rules at the front where position bias and caching both reward them, volatile state re-asserted at the tail where recency wins. Re-injection, not amplification, is the fix for adherence drift.
- The invariant sorts the ecosystem: CacheAligner productizes it, Serena and the memory layers live natively in the channel, Letta prices the deliberate violation, and a prompt-stamping proxy is the most expensive middleware you can run. Audit any of them with two identical requests and the usage fields.
👉 You now hold the rule the rest of the harness serves. One chapter remains in this part: the extension surfaces that write into this channel (skills, hooks, agents, MCP) and the very different token contracts each one signs. Continue to The extension surfaces.