Skill v1.0.0
currentLLM-judged scan90/100version: "1.0.0" id: prompt-prefix-stability synopsis: keep the cached system-prompt prefix byte-stable when touching the loop or prompts triggers: [BuildStableSystem, BuildVolatileSystem, cache_control, prompt cache, prefix, system prompt, anthropic, openai cache]
Both Anthropic and OpenAI prompt caching key on the prefix of the request being byte-identical to a recent prior call. Loom's loop deliberately splits the system prompt into a stable prefix (cached) and a volatile tail (after the cache breakpoint). Breaking byte-stability silently disables caching and roughly 5–10×s input cost.
What lives where
Stable prefix — BuildStableSystem in agent/internal/loop/loop.go:
- Mode base prompt (from embedded
agent/internal/prompts/<mode>.md). _output_conventions.md(shared).- Tool catalogue (one-liners) + per-tool description bodies.
- Skills catalogue lines only (id + synopsis + triggers — no bodies).
- Sub-agent preset advertisements (name + description) — only when
spawn_subagent is in the registry.
Volatile tail — BuildVolatileSystem:
- Workspace root path.
- Index state (files scanned, semantic search status).
- Loaded skill bodies (only those the model called
load_skillon). LOOM.mdcontent (wrapped in<rules>XML, 32 KB cap).
Cache breakpoints — agent/internal/llm/anthropic.go:
- After the stable system prefix.
- After the full tool-definitions block.
- Prior user turn + current user turn (set by
markAnthropicUserBreakpoints).
OpenAI uses automatic prefix-based caching — no explicit markers, but the same byte-stability rules apply.
Rules
- Never put per-turn-variable data in the stable prefix. Timestamps,
workspace paths, file counters, anything reading workspaceState — these all go in the volatile tail.
- Tool list order must be deterministic. MCP tools are sorted by name in
Driver.registry(). If you add a new source of tools, sort it the same way before appending.
- Skills are advertised, not loaded. Adding a skill body to the
catalogue lines = cache invalidation on every load. Keep bodies in the volatile tail behind load_skill.
- Adding a tool invalidates the cache once. That's expected. But
re-ordering the existing tools invalidates it on every turn from then on — don't shuffle without a reason.
- The rules bundle hash is captured at task start. Mid-task
LOOM.md
edits do not invalidate the running cache (the Entry holds the snapshot). Editing rules between tasks is fine — it's a volatile-tail change anyway.
Verifying cache behavior
After a change that touches the prefix:
- Run a multi-turn task (3+ turns).
- Look at LLM call telemetry / response headers. Anthropic returns
cache_read_input_tokens and cache_creation_input_tokens per call.
- Expected pattern: turn 1 creates the cache, turns 2..N read it. If
every turn shows fresh creation tokens, the prefix is drifting.
When you must change the prefix
- Bump prompt revision (
agent/internal/prompts/<mode>.md) — add an entry
to docs/prompt-changelog.md per project convention.
- Tools added/removed/reordered — document why in the same changelog.
- Mode definition changes — same.