Thirteen Rewrites a Day
Lesson 2 ended with a repair: on 2026-10-18 the Chief's doorway line left the system prompt, because every doorway switch had been re-writing 330K to 415K tokens of cached prompt. The repair was right, and it was not enough. On 2026-10-22 a review of the Master Chief (Coop todo #597) decrypted the Chief's ground-truth log and counted about thirteen turns a day whose first request wrote 280K to 500K tokens to the cache again. A rewrite pays the cache-write price on the whole prompt instead of the read price, and Dad waits for that write before the first word of the answer. Most of the thirteen had the same shape as the bug lesson 2 fixed: the turn came through a different doorway than the turn before.
The review had to find that out the hard way. Nothing recorded what a turn's prefix had been, so it named the causes by decrypting the log and reasoning backwards from the bill.
Five Ways the Doorway Still Reached the Prefix
A Claude request begins with the tools it advertises, then the system prompt, then the history replay, and the provider cache matches that prefix byte for byte. The origin line was gone from it. Five other things still followed the doorway into it:
| What differed | How |
|---|---|
| The tool list | The WebUI sent its per-brain toggle list, the Pippa app the whole surface with Bash, Telegram the whole surface without it. The function that fixed it names this 10 of the 41 rewrites the review measured. |
| An app doorway's host | A turn from inside a sibling app added that app's host tools to the tool block and its rules to the system prompt. |
| A client's own addendum | Joined the system prompt for that client's turns only. |
| The composer's effort | A WebUI turn sent at medium named medium in the vessel meta, the first lines of the system prompt. |
| Referenced artifacts | Appended to the system field for the one turn that named them, so the next turn wrote the prefix again. |
None of the five was a mistake on its own. Each doorway had a reason to send what it sent. The mistake was that a conversation shared by every doorway let each one decide its own prefix.
One Posture, One System Prompt
The fix, landed the same day (commit a5605c92, review finding M1), makes the prefix a property of the Chief instead of the doorway:
- One tool list.
chief_tool_posturegives every Chief turn the Chief's whole surface: tools on, Bash on, no allowlist, with the app host tools added from every doorway. A doorway's per-tool toggles no longer narrow the Chief; the Pippa app never sent them, so they had never held there either. A turn with tools switched off entirely keeps them off. - Restrictions moved to the call. A doorway's explicit Bash switch (Telegram's
/bash, off by default) still holds, but as aCallGaterefused when the model calls Bash, not as a shorter list. The Claude adapter's PreToolUse hook and every other vessel's tool bridge askcall_refusal, and the refusal tells the model what to do instead. - One system prompt. It carries the Chief addendum alone. A client's addendum, the app doorway's rules and a Coop escalation's brief become turn notes (
TurnState.notes), rendered after the origin line in the turn's own context. - Standing effort. In a Chief turn the vessel meta names the vessel's standing reasoning level (
chief_reasoning); when the turn runs at another level, a sentence in its context says so, and any action record the turn writes carries the real level. That takes the level out of the system prompt's text, not out of the request: the real level still goes to the Claude SDK as the turn's effort setting, and the provider's cache treats a changed effort as a changed prompt from the history on (on some models from the very start). So a WebUI turn at medium between turns at high still writes the replayed history again. - Artifacts on the message. Referenced artifacts ride the current message in every conversation, Chief or not.
Look at what the second bullet trades. The model now sees a tool that this doorway will refuse, and learns that only when it tries. That costs a refused call now and then. A shorter list cost a full rewrite of a prompt of several hundred thousand tokens on every switch. Advertising is part of the prefix; refusing is not.
Measured, Not Guessed
The other half of the work (finding m6, 51b5cdd3) made the next review unnecessary. services/prompt_cache_watch.py fingerprints every Claude turn's prefix: hashes of the tool block (with the MCP servers behind it) and of the system prompt, plus the model, effort, thinking and fast mode. The fingerprint rides the turn's first pippa_usage event in the log. When a first request writes 50K tokens or more, the watcher compares the fingerprint with the same conversation's previous turn and names the cause on the [turn-provider] line:
rewrite=tools,system(or model, effort, thinking, fast): that part of the prefix changed;ttl: nothing changed, and the previous turn is more than an hour old, so the cache entry had expired;replay: nothing changed within the hour, so the history itself moved (a compaction, an edit, a healed row);first-seen: no earlier turn of this conversation since the server started.
Admin → Health lists today's rewrites by cause and conversation. The counts live in memory, so a restart starts them again, and the Health line says that too.
What Still Rewrites, by Design
One prefix does not mean a prefix that never moves. The design doc lists what still rewrites on purpose: the first turn after an hour's quiet (the cache entry's life), a compaction, a vault file edit inside the prompt (the engine rebuilds within five minutes), a different model, a thinking switch, a look tool or On Air effects added to one turn, and the rare spoken turn whose voice is not Dad's, which loses its acting tools. One more belongs there and is missing from the list: a turn at a different effort from the turn before, since the real effort still rides the request. Each of these changes what the turn is or what it can do. A doorway switch on its own changes neither, which is why it no longer may; a doorway that also sends another effort still pays for the effort.