# Prompt assembly: stable-prefix caching (token learning-curve)

Anthropic prompt caching is **prefix-based**: the longest unchanged leading span of a request is served from cache at a large discount on repeated calls. As the pipeline accumulates durable knowledge about a repo (conventions, learnings brief, repo evidence), that knowledge should become a near-free cached prefix instead of a growing per-run token cost. This is the mechanism that makes "knowing the repo better" literally cheaper each run.

## The rule

Assemble every phase prompt as two spans, in this order:

1. **Stable prefix (cacheable, repo-stable, changes rarely):**
   - system / role instructions for the phase
   - the repo profile (`learnings-ledger.mjs profile` -> `<repo-profile>`)
   - extracted conventions (Phase 1c) and the design/analysis doc when applicable
   - repo structural evidence (the reuse-first buckets, Code Connect index)
2. **Volatile suffix (task-specific, changes every run):**
   - the specific task / issue / diff
   - the task-ranked memory (`learnings-ledger.mjs brief --task ...` -> `<task-relevant-memory>`, and any `triage-memory.mjs query` prior art)
   - the current file(s) under edit
   - per-run state and the immediate instruction

## Why durable knowledge is TWO blocks

Ranking the ledger against the task makes the injected knowledge relevant. It also makes it different on every run, and anything that differs cannot be a cached prefix. Those two goods are in direct conflict if there is one block, so there are two:

- `profile` is task-INDEPENDENT: that, and only that, is what makes it cacheable. Identical bytes across runs, so it sits at the head and is served from cache, and it grows as the repo is learned - the mechanism that makes knowing the repo cheaper rather than more expensive. Its order (confidence, then kind, then statement by code point, never recency) is a separate choice, made for truncation: when `--max` or `maxChars` cuts the block, the entries that survive should be the ones held with most confidence. The cost of that choice is that a new entry lands mid-block rather than at the end, so slightly less of the block's own prefix survives the run that adds it.
- `brief --task` is ranked against the task in hand and goes AFTER the task text, where a per-run difference costs nothing that was not already volatile.

Both blocks end each line with an `L:<id>` pointer instead of spelling out the evidence behind it. The detail is one `learnings-ledger.mjs show --id` away, and the block that would have carried it inline for every entry stays small. The same applies to prior art (`T:<id>`, `triage-memory.mjs show --id`) and to offloaded tool payloads (`[[ref:<node_id>]]`, `offload-ref.sh`): a pointer in context, the evidence on disk, nothing lost.

Put the stable material FIRST and the volatile material LAST. Reordering volatile content into the middle of the prefix invalidates the cache for everything after it, so keep the boundary clean.

## Why it matters

- Repeated runs on the same repo pay roughly cache-read price (~10% of input) on the stable prefix instead of full price - the dominant per-run token cost once a repo is "known."
- It turns accumulated learnings from a cost into an asset: more durable knowledge => bigger cached prefix => lower marginal cost, not higher.
- The effect is visible in `metrics.jsonl` `tokens_cached` / `cache_ratio` and is exactly what `learning-curve.mjs` trends over time (rising cache ratio + falling tokens/task = the learning curve working).

## Anti-patterns

- Interleaving the task text with conventions/learnings (breaks the prefix).
- Putting the task-ranked block (`brief --task`) in the prefix. It changes every run, so everything after it re-bills at full price - which costs more than the relevance was worth.
- Letting the stable prefix bloat past what the phase needs (context rot; cache a lean prefix, not everything - see the corpus merge/drop + progressive-disclosure discipline).
- Regenerating repo evidence from scratch each run instead of reusing cached/learned artifacts (defeats both the cache and the learning curve).
