Part of epic #8286 (Phase 3 — AI observability). Surfaced by #10212.
Problem
Token counts now reach PostHog (#10212 fixed the scrubber that was nulling them), and the input side is badly under-reported for the claude-code provider. Real events captured minutes after deploying orb-v3.7.0-beta.9:
| model |
provider |
$ai_input_tokens |
$ai_output_tokens |
$ai_total_cost_usd |
| claude-sonnet-5 |
claude-code |
2 |
787 |
$0.2145 |
| claude-sonnet-5 |
claude-code |
2 |
157 |
$0.0801 |
| qwen3:8b |
ollama |
2706 |
544 |
$0.000564 |
Two input tokens for a review that produced 787 output tokens and cost $0.21 is not plausible — a review prompt carries the diff plus assembled repo context. The local ollama model on the same box reports 2,706, which is the realistic shape.
Cause
extractCliUsage reads only these keys (src/selfhost/ai.ts):
const INPUT_TOKEN_KEYS = ["input_tokens", "inputTokens", "prompt_tokens", "promptTokens"] as const;
There is no reference anywhere in the file to cache_read_input_tokens or cache_creation_input_tokens.
Anthropic's usage envelope reports cached prompt tokens separately from input_tokens. On a cache hit — which is the normal case for this engine, since the system prompt and repo context are stable across a review — input_tokens contains only the small uncached remainder, and the bulk sits in cache_read_input_tokens. So the parser faithfully records the one number the CLI put in the field it looks at, and silently drops the rest.
This was invisible until now: the redaction bug was nulling every token count, so there was nothing to notice.
Impact
$ai_input_tokens under-reports claude-code input by orders of magnitude.
- PostHog derives
$ai_input_cost_usd / $ai_output_cost_usd from token counts, so the derived input cost is ~0 and the input/output cost split is meaningless. ($ai_total_cost_usd is unaffected — it is sent directly from the CLI's own total_cost_usd.)
- Cost-per-token and cache-hit-rate analysis are impossible.
- The same parser backs
loopover_ai_input_tokens_total in Prometheus, so Grafana under-reports identically.
PostHog already models this
posthog.ai_events has native cache_read_input_tokens and cache_creation_input_tokens columns, and $ai_generation carries a $ai_cache_reporting_exclusive boolean documented as: "Whether cache tokens are excluded from the input token count. When true, cache tokens are separate from input tokens (Anthropic-style)." So the vendor contract has a first-class slot for exactly this — we simply never populate it.
Deliverables
Part of epic #8286 (Phase 3 — AI observability). Surfaced by #10212.
Problem
Token counts now reach PostHog (#10212 fixed the scrubber that was nulling them), and the input side is badly under-reported for the
claude-codeprovider. Real events captured minutes after deployingorb-v3.7.0-beta.9:$ai_input_tokens$ai_output_tokens$ai_total_cost_usdTwo input tokens for a review that produced 787 output tokens and cost $0.21 is not plausible — a review prompt carries the diff plus assembled repo context. The local ollama model on the same box reports 2,706, which is the realistic shape.
Cause
extractCliUsagereads only these keys (src/selfhost/ai.ts):There is no reference anywhere in the file to
cache_read_input_tokensorcache_creation_input_tokens.Anthropic's usage envelope reports cached prompt tokens separately from
input_tokens. On a cache hit — which is the normal case for this engine, since the system prompt and repo context are stable across a review —input_tokenscontains only the small uncached remainder, and the bulk sits incache_read_input_tokens. So the parser faithfully records the one number the CLI put in the field it looks at, and silently drops the rest.This was invisible until now: the redaction bug was nulling every token count, so there was nothing to notice.
Impact
$ai_input_tokensunder-reports claude-code input by orders of magnitude.$ai_input_cost_usd/$ai_output_cost_usdfrom token counts, so the derived input cost is ~0 and the input/output cost split is meaningless. ($ai_total_cost_usdis unaffected — it is sent directly from the CLI's owntotal_cost_usd.)loopover_ai_input_tokens_totalin Prometheus, so Grafana under-reports identically.PostHog already models this
posthog.ai_eventshas nativecache_read_input_tokensandcache_creation_input_tokenscolumns, and$ai_generationcarries a$ai_cache_reporting_exclusiveboolean documented as: "Whether cache tokens are excluded from the input token count. When true, cache tokens are separate from input tokens (Anthropic-style)." So the vendor contract has a first-class slot for exactly this — we simply never populate it.Deliverables
cache_read_input_tokensandcache_creation_input_tokensfrom the CLI usage envelope.AiUsageand emit them as their own PostHog properties rather than silently folding them into$ai_input_tokens— the split is real information (cache hit rate), and flattening it would trade one inaccuracy for another.$ai_cache_reporting_exclusiveso PostHog knows the provider's convention and computes cost correctly.$ai_input_tokensstays the uncached remainder (matching Anthropic's own semantics, with cache reported alongside) or becomes the true total. Whichever is chosen, document it whereINPUT_TOKEN_KEYSis defined — the current behaviour is not a decision, it is an omission.input_tokens, largecache_read_input_tokens.extractCliUsageequivalent (packages/loopover-engine/src/miner/cli-subprocess-driver.ts) for the same gap — fix(observability): report the miner's real input/output token split to PostHog #10199/feat(observability): capture every miner AI generation, and stop fabricating zero token counts #10233 landed the input/output split there, but it reads the same key families.