2.4.0 — the measurement can finally have two sides `tune-context experiment` switches the doctrine and RAG off and back on, so the same work can be run in both arms: hook registrations, the managed CLAUDE.md block, doctrine skills, agents and the project .mcp.json. Every touched path is fingerprinted and copied aside before it is modified, so a `set` interrupted halfway is still reversible; `restore` puts the bytes back or refuses, naming what would be lost. `verify` reads the off arm as "off" instead of as a broken install, and `init` refuses while an arm is set. Cost stops being a proxy. eval/prompt-metrics.mjs turns transcripts into one row per prompt — time split into queued and work, tokens split by cache class, model, effort, tools, delegations — and eval/pricing.mjs prices them at API list price, cache multipliers included. It is NOT a bill: a Pro/Max plan charges its flat fee, and the fields say so in their names. The breakdown carries the epic's thesis in one line: on the local baseline, cache reads and cache writes are nearly the whole figure while plain input is 0.07% of it. Rows know which arm they came from. A branch shaped exp/<task>/<arm> says which arm was intended, the TASK-087 ledger which was set, and a disagreement leaves the arm unresolved with the reason on the row rather than picking a winner. pass_order rides along, because the second implementation of a task is cheaper for a reason that has nothing to do with the arm. Pre-release review caught two measurement bugs: `restore` now ends a run even when it parked nothing (the "on" arm's window used to stay open forever, and only that arm's, which made the contamination one-sided), and a cost that is not a finite number is refused instead of being counted as priced and turning the whole run's total into $NaN.