fix(llm): record usage/cost from the foreground transport#523
Merged
Conversation
The Claude foreground transport (CR_CLAUDE_FOREGROUND=1) parsed only session_id/is_error/result from the print-mode result envelope and left Response.Usage empty. Every foreground session therefore wrote blank token/cost columns, and the review footer reported cost and tokens as 'unavailable'. Decode total_cost_usd and the usage object (input/output/cache tokens, speed) from the same envelope — the data was already there — and project it onto Response.Usage, mirroring what the bg transport recovered from the session transcript. Cost still falls back to token-based estimation when the adapter reports none. All fields nullable: an omitted field stays unavailable rather than being recorded as a real zero.
piekstra-dev
approved these changes
Jul 21, 2026
piekstra-dev
left a comment
Collaborator
There was a problem hiding this comment.
Automated PR Review
Reviewed commit: 79e1786a75ce
Profile: reviewer - Posting as: piekstra-dev
Summary
| Reviewer | Findings |
|---|---|
| go:implementation-tests | 0 |
Reviewer Coverage
| Reviewer | Status | Inspected | Skipped | Constraints |
|---|---|---|---|---|
| go:implementation-tests | complete_broad | internal/llmadapters/subprocess.go, internal/llmadapters/subprocess_test.go | unavailable | unavailable |
0 PR discussion threads considered. 0 summarized; 0 resolved.
Completed in 1m 44s | unavailable | claude-sonnet-5 | cr 0.10.264
| Field | Value |
|---|---|
| Model | claude-sonnet-5 |
| Reviewers | go:implementation-tests |
| Engine | claude_cli · claude-sonnet-5 |
| Reviewed by | cr · piekstra-dev |
| Duration | 1m 44s wall · 1m 31s compute |
| Cost | unavailable |
| Tokens | unavailable |
Per-workstream usage
| Workstream | Model | In | Out | Cache read | Cache create | Cost | Duration |
|---|---|---|---|---|---|---|---|
| orchestrator-selection | claude-sonnet-5 | unavailable | unavailable | unavailable | unavailable | unavailable | 10s |
| go:implementation-tests | claude-sonnet-5 | unavailable | unavailable | unavailable | unavailable | unavailable | 1m 12s |
| orchestrator-rollup | claude-sonnet-5 | unavailable | unavailable | unavailable | unavailable | unavailable | 8s |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The Claude foreground transport (
CR_CLAUDE_FOREGROUND=1, the default the daemon and stall-hardened runs use since 0.10.258) never recorded usage.parseClaudeForegroundOutputdecoded onlysession_id/is_error/resultfrom claude's print-mode result envelope, and the foreground path builtResponse{StructuredOutput: output}with noUsage.Result: every foreground session wrote empty
tokens_in/out/cache/cost_usd(andduration_ms=0) to the ledger, so the review footer reads:— cost and the per-workstream token/cost table all "unavailable". The bg transport recovered this from the session transcript (
claudeBGTranscriptUsage); the foreground transport dropped it.Fix
The data is already in the envelope claude prints on exit —
total_cost_usdplus ausageobject (input_tokens,output_tokens,cache_read_input_tokens,cache_creation_input_tokens,speed). Decode those fields onclaudeForegroundOutputand project them ontoResponse.Usagefor the success path, mirroring the bg transport. The existing token-based cost estimation still applies as a fallback when the adapter reports no cost.All fields are nullable, so a field the CLI omits stays "unavailable" rather than being recorded as a real zero — and a parse miss leaves usage empty exactly as before (no regression).
Tests
TestSubprocessClaudeForegroundModenow emits a representative envelope (total_cost_usd+usage) and assertsResponse.Usagecarries the tokens, cost, and speed through.Note
An unrelated, pre-existing test-harness bug (
trustCurrentTempFixturessetsTMPDIRto a subdir it never creates) fails twointernal/pipelinetests under git ≥2.53 locally; it reproduces on cleanmainand is fixed separately. It does not touch this change.