CacheExplanation
A read-only trace of how SemanticCache.explain evaluated one prompt — the tool you reach for when a hit you expected did not happen, or one you did not expect did.
Unlike SemanticCache.lookup, producing an explanation changes nothing: no counter in CacheStats moves, no entry is marked recently-used, and the Verifier is never called. A diagnostic that mutated the statistics you are trying to read, or made a model call, would be worse than useless. What it reports is the threshold-and-guard decision — the layer you actually tune.
val why = cache.explain("Convert 250 USD to EUR")
println(why.decision) // REJECTED_BY_GUARD
why.candidates.first().rejectingGuards // [numeric]Properties
The nearest entries considered, best-first — at most candidates of them, and empty when the scope holds nothing yet. Every guard is evaluated on every one of these, even the ones already below the threshold, so you can see a guard verdict that a real lookup would never have reached.
What a SemanticCache.lookup of this prompt would decide, up to — but not including — the verifier.