Skip to content

Read Claude Code's real usage from the result event - #41

Open
nrodd wants to merge 1 commit into
mainfrom
harness-token-truth
Open

nrodd wants to merge 1 commit into
mainfrom
harness-token-truth

Conversation

@nrodd

@nrodd nrodd commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Claude Code's stream-json result event carries usage, total_cost_usd, num_turns, duration_ms, session_id and subtype. We typed it as result?: string and discarded the rest, then spent prompt budget asking the model to self-estimate its own token count. This parses the event instead.

Tokens

complete now carries the measured count (all four usage buckets summed, so it matches what the run actually billed) plus token_source:

  • harness — read out of Claude Code's result event
  • agent — a model self-estimate, only ever a fallback, and labelled as such
  • none — Codex and Gemini give no structured stream, so their runs report no count rather than a silent zero

Never compare token counts across harnesses without that field.

Run outcome

subtype separates a finished run from error_max_turns / error_during_execution. Both of those still exit 0, which is exactly the "exit 0 but the model refused or abandoned" case installConfirmed previously had to guess at from the install marker alone. The harness verdict is now decisive when we have it: such a run closes the funnel as partial, warns in the terminal, and frames the demo guide as post-install work.

Also

  • Terminal runs print one dim line of run economics: 442.0k tokens · $0.18 · 12 turns · 41s.
  • Cost, turn count and subtype go to debug-only note() calls, since the proto has no fields for them (added to the known-gaps list in telemetry.ts).
  • The token self-estimate is gone from the MCP telemetry section and the complete row. GUI handoffs now report no token number rather than an unlabelled guess that arrives indistinguishable from a measured one.
  • token_source is deliberately not in the marker allowlist, so a forged marker cannot claim its guess was measured.

Notes for review

  • The brief pointed at src/prompt/build.ts:52 and templates/install-prompt.md for the self-estimate instruction. It actually lived only in the MCP section (mcpTelemetrySection) and COMPLETE_ROW; the stdout/terminal path only ever asked for duration_ms.
  • token_source is a new metadata field. If the telemetry endpoint rejects unknown metadata fields the way it 400s unknown steps, complete events will start failing until the proto gains it. Worth confirming before this ships.

Testing

npm run typecheck and npm run build pass. Parser smoke-tested end to end against a fake claude binary emitting real-shaped stream-json, for both a success result and an error_max_turns result that exits 0: stats parse, the error path warns, and the telemetry marker in the same stream is still extracted.


Note

Medium Risk
Changes onboarding funnel outcomes and telemetry metadata (token_source, total_tokens sourcing); backend must accept the new metadata field or complete events may fail.

Overview
The wizard now reads real token usage, cost, turns, and duration from Claude Code’s stream-json result event instead of prompting models to self-estimate tokens. Terminal runs show a dim run economics line and warn when the result subtype indicates the run did not finish cleanly (e.g. turn limit or mid-execution error), even when exit code is 0.

Telemetry complete events attach total_tokens only when there is a measured harness total (both phases summed) or a labelled agent fallback, always with token_source (harness / agent / none). Harness-reported errors make installConfirmed decisive for funnel success vs partial. MCP install prompts no longer ask for token estimates on complete; marker parsing still blocks forged token_source. Cost and extra harness fields go to debug note() until the proto supports them.

Reviewed by Cursor Bugbot for commit 91e8259. Bugbot is set up for automated code reviews on this repo. Configure here.

Claude Code's stream-json result event already carries usage, total_cost_usd,
num_turns, duration_ms, session_id and subtype; we typed it as `result?: string`
and dropped the rest, then asked the model to self-estimate its own token count
in the prompt.

Parse the whole event instead:

- total_tokens on the `complete` event is now the measured count (all four
  usage buckets), with token_source ('harness' | 'agent' | 'none') alongside it
  so counts from a harness that measures are never compared with a model's
  guess. Codex and Gemini expose no structured stream, so their runs report
  'none' rather than a silent zero.
- `subtype` decides whether the install is confirmed. error_max_turns and
  error_during_execution still exit 0, which is exactly the refused-or-abandoned
  case the exit code could not distinguish; such a run now closes the funnel as
  partial and frames the demo guide as post-install work.
- Terminal runs print one line of run economics (tokens, cost, turns, duration),
  and cost/turns/subtype go to debug notes since the proto has no fields yet.

Drop the token self-estimate from the MCP telemetry section and the complete
row: an unlabelled estimate is worse than no number now that the wizard has a
measured one. token_source is deliberately absent from the marker allowlist, so
a forged marker cannot claim its guess was measured.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant