Skip to content

fix(llm): record usage/cost from the foreground transport#523

Merged
piekstra merged 1 commit into
mainfrom
fix/foreground-usage-telemetry
Jul 21, 2026
Merged

fix(llm): record usage/cost from the foreground transport#523
piekstra merged 1 commit into
mainfrom
fix/foreground-usage-telemetry

Conversation

@piekstra

Copy link
Copy Markdown
Contributor

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. parseClaudeForegroundOutput decoded only session_id/is_error/result from claude's print-mode result envelope, and the foreground path built Response{StructuredOutput: output} with no Usage.

Result: every foreground session wrote empty tokens_in/out/cache/cost_usd (and duration_ms=0) to the ledger, so the review footer reads:

Completed in 3m 49s | unavailable | claude-sonnet-5 | cr 0.10.264

— 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_usd plus a usage object (input_tokens, output_tokens, cache_read_input_tokens, cache_creation_input_tokens, speed). Decode those fields on claudeForegroundOutput and project them onto Response.Usage for 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

TestSubprocessClaudeForegroundMode now emits a representative envelope (total_cost_usd + usage) and asserts Response.Usage carries the tokens, cost, and speed through.

Note

An unrelated, pre-existing test-harness bug (trustCurrentTempFixtures sets TMPDIR to a subdir it never creates) fails two internal/pipeline tests under git ≥2.53 locally; it reproduces on clean main and is fixed separately. It does not touch this change.

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
piekstra requested a review from piekstra-dev July 21, 2026 03:29

@piekstra-dev piekstra-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@piekstra
piekstra merged commit e65d8e3 into main Jul 21, 2026
10 checks passed
@piekstra
piekstra deleted the fix/foreground-usage-telemetry branch July 21, 2026 03:37
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.

2 participants