fix(submit): bypass cost-without-tokens check for Cursor legacy premium-tool-call - #612
Conversation
β¦um-tool-call Cursor's pre-2025-05 usage exports include `premium-tool-call` rows that are billed per tool invocation and carry no token attribution at all (input/output/cache columns are empty). PR #557 introduced a sanity check that rejects any submission where cost > 0 and tokens === 0, which permanently locks out any user with historical Cursor data β even a few cents of legacy tool-call charges block the entire upload (we observed a real submission where ~$45 of premium-tool-call rows blocked $45,618 of otherwise valid data, ~0.1% of the payload). Allow `cursor/premium-tool-call` specifically by adding a single legacy carve-out. The check still fires for every other `(client, model)` combination, including other cursor models, so genuine parser regressions remain visible. Also enrich the cost-without-tokens error messages so operators can read the offending row directly out of the CLI output: - Client-level errors now include the client, modelId, providerId, full cost (`cost=$X.XXXX`), and the full token breakdown (`input/output/cacheRead/cacheWrite/reasoning`). - Day-level errors list which clients on that day were responsible (`offending clients: cursor/premium-tool-call (provider=cursor) cost=$2.0500; ...`). - Submission-summary errors include the same per-client offender list. Constraint: cannot drop Cursor `premium-tool-call` rows in the CLI parser because their cost is a legitimate spend the user wants reflected in totals. Rejected: Drop tokenless rows in the CLI scanner | loses real cost attribution that the leaderboard depends on. Rejected: Loosen the check globally (cost > 0 && tokens === 0 β warning) | re-opens the original PR #557 vulnerability (implausible cost-only submissions). Confidence: high Scope-risk: narrow Directive: Only add new entries to CURSOR_LEGACY_TOKENLESS_MODELS for Cursor billing events that legitimately lack token attribution β never for parser bugs (fix the parser instead). Not-tested: A Cursor user whose entire history is exclusively `premium-tool-call` (summary-level legacy subtraction path is exercised only by unit tests, not by an end-to-end submit).
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
π‘ Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 66f7bbd91c
βΉοΈ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with π.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| day.totals.cost, | ||
| day.totals.tokens |
There was a problem hiding this comment.
Exclude legacy Cursor cost from CPM checks
When a legacy cursor/premium-tool-call row shares a day with any token-bearing usage, this branch runs because day.totals.tokens > 0, and the cost-per-million check uses the full day cost including the tokenless legacy charge. A historical Cursor day such as $2.05 in premium tool calls plus 100 normal tokens now computes $20,500/M and is rejected, even though the legacy row is supposed to be skipped; subtract the legacy tokenless cost (and do the same for the summary check) before applying this sanity cap.
Useful? React with πΒ / π.
There was a problem hiding this comment.
Good catch β fixed in 5ab9a79. The day-level (and summary-level) branches now compute a shared checkableCost = max(0, totalCost - legacyCost) and pass that to both the cost-without-tokens check and pushCostPerMillionError, so a legacy premium-tool-call charge sharing a day with normal token-bearing usage no longer inflates the cost-per-million ratio. Added excludes cursor legacy cost from the cost-per-million sanity cap as a regression test exercising the exact $2.05 + 100 tokens shape you described β it previously would have computed $20,600/M and failed; now it passes.
Address Codex review (P1) on PR #612: when a legacy `cursor/premium-tool-call` row shares a day with a small amount of token-bearing usage, the day-level branch falls through to the cost-per-million check because `day.totals.tokens > 0`. Using the full day cost (legacy + real) as the numerator meant tiny token counts tripped the $10k/M ceiling even though the legacy row is supposed to be skipped β e.g. $2.05 in legacy tool calls plus 100 normal tokens computed $20,600/M and was rejected. Subtract the legacy tokenless Cursor cost before applying the cost-per-million cap at both day and summary levels (was previously only subtracted for the cost-without-tokens branch). The cost-without-tokens branch behavior is unchanged because both branches now read from the same `checkableCost` value. Add a regression test (`excludes cursor legacy cost from the cost-per-million sanity cap`) that pins the exact mixed-day shape from the review. Confidence: high Scope-risk: narrow Directive: Any future cost-related sanity check on day/summary aggregates must also subtract `legacyCost` first β wire it off the same `checkableCost` local instead of recomputing.
There was a problem hiding this comment.
1 issue found across 2 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Address cubic review (P2) on PR #612: the new legacy-cost subtraction used strict `> 0` float comparison at both the day and summary cost-without-tokens checks. Floating-point summation residue could trip the check on a valid all-legacy submission β e.g. `0.1 + 0.2 === 0.30000000000000004` while IEEE `0.3 β 0.299999999999999988`, so `totalCost - legacyClientCost β 5.5e-17 > 0` even though the user truly has $0.30 of legacy cost and nothing else. Introduce `LEGACY_COST_FLOAT_EPSILON = 1e-6` and use it as the threshold on both check sites. 1e-6 is well below any realistic LLM charge, so any legitimate non-legacy cost still trips the check; only FP rounding noise is absorbed. Add a regression test (`tolerates floating-point residue when subtracting cursor legacy cost`) that constructs the exact `0.1 + 0.2` vs `0.3` scenario and asserts the residue is positive but the submission still validates. Confidence: high Scope-risk: narrow Directive: Any cost-vs-cost float comparison on aggregated sums should use LEGACY_COST_FLOAT_EPSILON (or a comparable epsilon) β strict `> 0` is a footgun on summed IEEE 754 values.
premium-tool-call
β¦ium-tool-call` (junhoyeo#612) * fix(submit): bypass cost-without-tokens check for Cursor legacy premium-tool-call Cursor's pre-2025-05 usage exports include `premium-tool-call` rows that are billed per tool invocation and carry no token attribution at all (input/output/cache columns are empty). PR junhoyeo#557 introduced a sanity check that rejects any submission where cost > 0 and tokens === 0, which permanently locks out any user with historical Cursor data β even a few cents of legacy tool-call charges block the entire upload (we observed a real submission where ~$45 of premium-tool-call rows blocked $45,618 of otherwise valid data, ~0.1% of the payload). Allow `cursor/premium-tool-call` specifically by adding a single legacy carve-out. The check still fires for every other `(client, model)` combination, including other cursor models, so genuine parser regressions remain visible. Also enrich the cost-without-tokens error messages so operators can read the offending row directly out of the CLI output: - Client-level errors now include the client, modelId, providerId, full cost (`cost=$X.XXXX`), and the full token breakdown (`input/output/cacheRead/cacheWrite/reasoning`). - Day-level errors list which clients on that day were responsible (`offending clients: cursor/premium-tool-call (provider=cursor) cost=$2.0500; ...`). - Submission-summary errors include the same per-client offender list. Constraint: cannot drop Cursor `premium-tool-call` rows in the CLI parser because their cost is a legitimate spend the user wants reflected in totals. Rejected: Drop tokenless rows in the CLI scanner | loses real cost attribution that the leaderboard depends on. Rejected: Loosen the check globally (cost > 0 && tokens === 0 β warning) | re-opens the original PR junhoyeo#557 vulnerability (implausible cost-only submissions). Confidence: high Scope-risk: narrow Directive: Only add new entries to CURSOR_LEGACY_TOKENLESS_MODELS for Cursor billing events that legitimately lack token attribution β never for parser bugs (fix the parser instead). Not-tested: A Cursor user whose entire history is exclusively `premium-tool-call` (summary-level legacy subtraction path is exercised only by unit tests, not by an end-to-end submit). * fix(submit): exclude Cursor legacy cost from cost-per-million sanity cap Address Codex review (P1) on PR junhoyeo#612: when a legacy `cursor/premium-tool-call` row shares a day with a small amount of token-bearing usage, the day-level branch falls through to the cost-per-million check because `day.totals.tokens > 0`. Using the full day cost (legacy + real) as the numerator meant tiny token counts tripped the $10k/M ceiling even though the legacy row is supposed to be skipped β e.g. $2.05 in legacy tool calls plus 100 normal tokens computed $20,600/M and was rejected. Subtract the legacy tokenless Cursor cost before applying the cost-per-million cap at both day and summary levels (was previously only subtracted for the cost-without-tokens branch). The cost-without-tokens branch behavior is unchanged because both branches now read from the same `checkableCost` value. Add a regression test (`excludes cursor legacy cost from the cost-per-million sanity cap`) that pins the exact mixed-day shape from the review. Confidence: high Scope-risk: narrow Directive: Any future cost-related sanity check on day/summary aggregates must also subtract `legacyCost` first β wire it off the same `checkableCost` local instead of recomputing. * fix(submit): use float-epsilon when subtracting Cursor legacy cost Address cubic review (P2) on PR junhoyeo#612: the new legacy-cost subtraction used strict `> 0` float comparison at both the day and summary cost-without-tokens checks. Floating-point summation residue could trip the check on a valid all-legacy submission β e.g. `0.1 + 0.2 === 0.30000000000000004` while IEEE `0.3 β 0.299999999999999988`, so `totalCost - legacyClientCost β 5.5e-17 > 0` even though the user truly has $0.30 of legacy cost and nothing else. Introduce `LEGACY_COST_FLOAT_EPSILON = 1e-6` and use it as the threshold on both check sites. 1e-6 is well below any realistic LLM charge, so any legitimate non-legacy cost still trips the check; only FP rounding noise is absorbed. Add a regression test (`tolerates floating-point residue when subtracting cursor legacy cost`) that constructs the exact `0.1 + 0.2` vs `0.3` scenario and asserts the residue is positive but the submission still validates. Confidence: high Scope-risk: narrow Directive: Any cost-vs-cost float comparison on aggregated sums should use LEGACY_COST_FLOAT_EPSILON (or a comparable epsilon) β strict `> 0` is a footgun on summed IEEE 754 values.
What I hit
Running
npx tokscale@latest submitagainst a freshly synced local payload (5,038 Cursor CSV rows, 230 active days, 59.07B tokens across 10 clients) failed at server-side validation with a long list ofCost submitted without tokenserrors. The full failure mode looks like this:The entire 230-day submission was rejected.
Root cause
pushCostSanityErrorsinpackages/frontend/src/lib/validation/submission.ts(introduced by PR #557 βfix(submit): reject implausible submitted totals) treats any(date, client, modelId)aggregate withcost > 0 && tokens === 0as fatal:The check fires at three levels: per-client, per-day total, and overall summary total. Any single offending row fails the whole submission.
Cursor's CSV usage exports have legitimate cost-only rows whose token columns are blank:
cursor/premium-tool-callcursor/auto,cursor/claude-3.5-sonnet,cursor/claude-4-sonnet-thinking,cursor/o3Costbut noTotal Tokens(sporadic)crates/tokscale-core/src/sessions/cursor.rs(the Rust CSV parser) preserves these rows verbatim, so they reach the server ascost > 0, tokens = 0aggregates.Forensic count from my local cache
I crawled
~/.config/tokscale/cursor-cache/usage.csv(the same file the CLI submits) and summed every(date, model)aggregate wherecost > 0 && totalTokens === 0:premium-tool-callclaude-4-sonnet-thinkingautoclaude-3.5-sonneto3premium-tool-callaccounts for 89% of the rejected cost and is the only model that is always tokenless by design (it disappeared from Cursor's schema in May 2025).The fix
Two parts, both inside
packages/frontend/src/lib/validation/submission.ts:1. Cursor legacy carve-out (the actual unblock)
Add a narrow allowlist for the one model that legitimately and consistently has no token attribution:
This carve-out is applied at all three check sites:
day.totals.costbefore testing. If the day has only legacy rows the check no longer fires; if the day has a mix of legacy + a real tokenless regression, the remaining (non-legacy) cost still trips the check.Other cursor models (
auto,claude-3.5-sonnet,claude-4-sonnet-thinking,o3) are explicitly not included in the allowlist. They sometimes have tokenless rows, but unlikepremium-tool-callthose tokenless rows look like genuine parser/API regressions that we want to keep surfacing. Hiding them under a blanket cursor allowlist would mask real bugs.2. Detailed English error messages
The old
Cost submitted without tokensline was unactionable β operators had to re-run the CLI in debug mode to figure out which row failed. The new messages embed the full row context:All three levels now report client, providerId, modelId, full cost, and the full token breakdown. Day- and summary-level errors also enumerate which clients on that scope were responsible.
The legacy
pushCostSanityErrorshelper was split into:pushCostPerMillionError), unchanged in behavior.Why not the alternatives
cost > 0 && tokens === 0to a warning globallypremium-tool-callpaths.Test plan
npx vitest run __tests__/api/submit.test.tsβ 47/47 pass (was 44 before, +3 new tests).npx vitest run(full frontend suite) β 283/283 pass.npx eslint src/lib/validation/submission.ts __tests__/api/submit.test.tsβ clean.rejects submitted cost without corresponding tokenstest still passes (the new message still starts with the same prefix).allows cursor legacy premium-tool-call rows that lack token attributiontest verifies the unblock works for the exact shape I hit locally (date2025-04-29, cost$2.05, all-zero tokens).does not extend the cursor legacy bypass to other cursor modelstest pins the carve-out topremium-tool-callonly βcursor/claude-3.5-sonnetwith cost-only still fails.allows cursor legacy rows mixed with normal token-bearing rowstest covers the realistic mixed-day case.includes client/provider/model/cost/tokens detail in tokenless-cost errorstest pins the new English error format so it doesn't silently regress.Files changed
packages/frontend/src/lib/validation/submission.tsβ carve-out + richer error messages.packages/frontend/__tests__/api/submit.test.tsβ 4 new tests, existing assertions intact.Rollback
git revert HEADβ single commit, isolated to the validation module.Summary by cubic
Unblocks submissions with historical Cursor data by bypassing the βcost without tokensβ check for legacy
cursor/premium-tool-callrows and excluding their cost from cost-per-million checks. Real tokenless-cost regressions still fail and are easier to debug.cursor/premium-tool-callrows (cost > 0, tokens = 0) at client/day/summary levels; other Cursor models remain strict.Written for commit 8eb7bfa. Summary will update on new commits. Review in cubic