Cost is in microseconds
Not in abstract units. The operators span dict probes, HNSW walks, blob reads and a neural forward pass; any synthetic unit needs conversion factors anyway, and being honest about the unit is what letsexplain --analyze validate the
model rather than merely display it.
Recall is part of the objective
Three layers, and the stratification is how pruning happens without violating the protocol:- A validity rule, never costed. With two or more scoring generators available, a plan using fewer than two is
inadmissible. Single-authority plans are structurally inexpressible. (One exemption: a plan whose only generator is
an exhaustive scan is a no-authority plan, not a single-authority one.) The scan earns its recall of 1.0 by
reading what the indices read: a canonical block’s normalized view, through the module store, so every generator
observes one text. A block registered with no view matches on its media type alone, for the scan exactly as for the
indices, and
explainsays how many such blocks a canonical scan is walking past. - A recall floor per intent, as feasibility rather than as a price — 1.00 exact, 0.95 lexical, 0.85 semantic, 0.80 associative. Capped at the best recall any valid indexed plan actually achieves, and the capping is reported as a degradation: an unreachable floor forced a 5.3-second scan over a 600-microsecond probe at 100k blocks.
J = cost + λ·(1 − recall)·C_miss, to choose among plans that are already correct.
Reading explain
Four fields answer most questions:
indices_availablevsindices_consulted. “Why did it not use the vector index” has exactly four possible answers — absent, stale, model tag mismatched, or cost — and all four are visible here.considered— every rejected plan with its reason.only 1 scored generator with 3 availableis the single-authority rule refusing a plan.statistics— per module: fresh or stale, and the fingerprint.prelude_us— the provenance ledger, cached per root. It once charged 136 ms to a query that did not cause it.
--analyze reports the estimate beside the actual per node, and estimation_error summarises the gap. Refitting
the constants automatically is not built. They are measured defaults, and [planner] in vitruvio.toml overrides
them by hand — which is enough to correct a machine whose disk or CPU disagrees with the ones they were measured on,
and honest about the fact that nothing refits itself yet.