The owner's context-meter audit (2026-09-23), three fixes in the pure
contextWindow() fold (core/prices.ts):
- NUMERATOR: cache tokens count. Provider "input" excludes
cacheRead/cacheWrite, so input+output read wildly low on cache-heavy
sessions (40k shown vs ~190k real). The fold is now pi's own
totalTokens semantics: input + output + cacheRead + cacheWrite. The
fields were already persisted in every CallUsage (chat-handler's
capture seam); non-caching providers are unchanged.
- DENOMINATOR: the live catalog wins. contextWindow() accepts an
optional live-limits index (provider:id -> contextWindow — the shape
the client already holds from GET /api/models, where toModelDto
projects pi's Model.contextWindow); CONTEXT_LIMITS, the generated
duplicate, is demoted to fallback for models the live catalog doesn't
carry. The UI's stats-bar join passes its catalog index; the call is
unchanged when omitted.
- RIDE-ALONG (honesty guard): a zeroed/aborted last step falls back to
the previous step's footprint — an abort artifact is not a reading.
All-zero runs return null (no meter, never a bogus tiny number).
Tests pin all three: the cache-inclusive fold (and the plain no-cache
fold unchanged), live-index-beats-table + table fallback + unknown-to-
both null, and the zeroed-step fallback (previous step wins; all-zero
is null).
885/885 tests, typecheck/lint/build green.