How we got here
A day after v0.40.0 shipped, a five-lane audit fleet swept the new storage engine, the ingest pipeline, the query layer, and the CLI/hooks surface for bugs - each finding required a pinned repro before it counted. The sweep produced 24 verified issues. This release closes 19 of them; the remaining five are design questions or follow-up investigations, tracked open on the run map (#956).
What was actually wrong
The critical one: a mixed-model session billed every token at its last model (#936). Session cost was priced from session-total tokens at the session's display model, so a run that started on a frontier model and finished on a cheap one - or dispatched to both - overcounted by 30.9% on live data. Session cost is now the sum of per-turn estimates, each priced at its own turn's model, on both the Claude and Codex parsers. Pricing also stopped trusting partial answers elsewhere: a cost total is now null rather than a silent undercount when any used token component has no rate (#941), rollup groups with mixed priced/unpriced rows recompute or flag instead of reporting a partial sum as the total (#944), and ingest prices against the merged catalog instead of the built-in one (#937).
The storage seams got four hardening fixes (#964): the FTS content digest could miss a rebuild when text moved between existing rows (the XOR aggregate was pairing-blind), `putMany` could silently lose duplicate-id rows inside one batch, a WAL stat error could green-light a snapshot clone over a non-quiescent database, and the NDJSON spool deduped on raw ids while writing encoded ones. Each now has a pinned regression test - and the duplicate-collapse warning fired on its first real ingest, which is exactly what it is for.
Two runtime races are gone: a hook guard that threw synchronously could disable every later blocking guard in the dispatcher (#946), and two concurrent stale reads could both fork a background ingest (#947) - the spawn now runs under an exclusive claim that also survives a hard process kill.
The judgment sidecar's proposal dedupe signatures were hashed with `Bun.hash`, meaning a bun upgrade would re-key every signature and re-mint duplicates of settled proposals (#942). Signatures are SHA-256 now, with a one-time lossless migration that re-keys ids and links in a single transaction without dropping any decision row.
The rest: recall and cost query selectors rank matches by recency before applying their limit instead of keeping an arbitrary subset (#930, #931), the session inspector no longer loses tool cards past row 4,000 and reports whole-session totals on paginated views (#934, #935), derived SQL-model rows now clear when a reparse invalidates their source (#928, #929), routing-impact blocks price at turn grain instead of attributing whole overlapping sessions (#940), and the otel coverage and skills-recovery rollups count what they claim to count (#932, #933).
The one cross-fix interaction
Moving turn-level cost columns into the enrichment set (#966's catalog fix) silently changed what `ax segment export` ships: cost columns no longer ride a segment. The full-suite gate on merged main caught the contract tests still pinning the old decision, and the pin was retracted in place (#967) - an imported segment now re-prices against the importing machine's catalog, which the same release's backfill extension makes automatic.
Upgrade notes
The first ingest after upgrading runs three one-time cutovers: a full FTS rebuild (digest formula v3), a full re-derive of the cache-bust and run-evidence SQL models, and the proposal signature migration. Measured on a 16 GB live store: one 62s warm ingest, then back to the ~30s steady state.