Skip to content

feat(usage): see token burn and cost by T3 build and Labs setting - #69

Closed
ScriptedAlchemy wants to merge 8 commits into
mainfrom
feat/usage-cost-attribution
Closed

ScriptedAlchemy wants to merge 8 commits into
mainfrom
feat/usage-cost-attribution

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Why

There was no way to tell whether a merged PR or a Labs setting changed token burn or cost. Usage has no split by build or setting, and runs record neither.

What changed

  • Each run attempt is stamped at turn start with the T3 build, who started it, and every boolean setting. A failed stamp logs a warning and cannot fail a turn.
  • Usage gets a Cost by build and setting section under Cost and Tokens. It shows where spend went, burn per build, on and off for six Labs settings, and before and after a build you pick.
  • Each comparison returns Changed, No detectable change, or Not enough data. Not enough data carries no ratio, so no client can show a number the data doesn't support.
  • The report is a guarded RPC behind diagnostics:read, and its response tolerates client and server version differences.

How far to trust a verdict

Measured on about 375 real threads per side over 14 days, split at random, with a known cut applied to one side.

Real change Prompt tokens per call flagged Dollars per run flagged
None (false alarm, 3,000 splits) 0.8% 0.6%
15% cut 11% 1%
30% cut 96% 23%
50% cut 100% 73%
70% cut 100% 100%

Real comparisons are not random, and the report says so above the cards. When arm assignment followed each thread's main model, as a default model switch around a build would, false "Changed" verdicts on prompt tokens reached 4.7% (14 of 300). Dollars stayed at 0 of 300 at that setting. Removing secondary model use took it to 0 of 300, so these are real differences between threads that dip into a model and threads that live on it, not a counting error.

Merge order

Land this before turning on #64, so a baseline exists. Stamps start when a build with this code runs, and nothing is backfilled. A setting's on and off comparison starts once a build that knows that setting runs, so #64 needs its own row in usageExperiments.ts.

The on and off card will not confirm #64's savings. A reset applies to about 75 person-sent returns across 26 threads in 14 days of the data measured here. That is too few and too noisy to read on one machine, even with a holdback. #64 should record eligibility and whether the reset applied.

Left out

  • Mobile and an MCP tool. The service method is ready for both.
  • Verdicts for providers other than Claude and Codex. They appear in the ledger and the builds table.

Verification

  • vp run check:changed passes, with 36 typechecks and 327 tests in 18 files.
  • A real Replay turn wrote a stamp through the production server. The stamp took 0.23 to 0.42 ms against 100 to 221 ms for turn start (n=3).
  • The report rendered in the browser over 4,915 attempts. It takes about 2 seconds with a warm cache.
  • Not tested: packaged desktop, app.t3.codes, remote and tunnel connections, a cold cache, and the Labs link and Retry button by hand.

Screenshots

None attached. This repo is public and the only data I have shows real spend, so the maintainer decides whether to post a capture. Before this change the section does not exist.

Claude Opus 5.5 in Claude Code, through T3 Code. GPT-6 Astra and Claude Fable 5.1 reviewed it independently, and GPT-6 Astra found the sparse overlap defect fixed in the last commit.

🤖 Generated with Claude Code

ScriptedAlchemy and others added 8 commits November 5, 2026 12:32
Pure join of priced usage records to run attempts by session, and a thread-resampled
bootstrap that only returns a ratio when the data supports one.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ettings

One row per attempt, written before the running commit so a retry cannot lose it. Failure to
write leaves the attempt unstamped and never stops the turn.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A server.getUsageCohortReport read joins priced transcript records to run attempts and
compares experiment arms with a thread-resampled bootstrap. Behind diagnostics:read and a
usageCohortReports capability.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
An on-demand section for one environment, hidden when the server lacks the capability, plus a
Labs row that opens Usage and a short user guide section.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A nominal 99% bootstrap flagged 2.75% of random splits of real threads,
because spend is heavy-tailed. A report holds up to 14 verdicts, so about a
third of reports would have shown a false Changed. A 99.8% interval over
4,000 resamples flags 0.57% and still catches 97% of a 30% prompt size cut.

Also keep a stamping defect from failing a turn, count a run on two models
once in arm totals, and share the report's 92 day limit with clients.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The build selector no longer shows a raw value, the self-selection note
appears once instead of on every card, a wait longer than the report's window
says so, and the Labs row and guide say where the section lives.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A web client rejected the whole report when a newer server retuned its
interval, because the confidence was a strict literal on the wire. Unknown
comparisons and rows now drop on their own instead of failing the report, and
the failure state gets a Retry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A comparison counted every thread toward its 30 thread minimum, but only
threads on a provider, model, and speed both sides ran shape the estimate.
Thirty threads per side with one in common returned "Changed, 2x, [2, 2]".

A stratum now counts for as many threads as its smaller side, for both the
minimum and the weight, so one thread cannot speak for a model the other
side ran heavily.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Nov 5, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 63a3e43

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

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