Track multiple provider plans#300
Open
ozymandiashh wants to merge 1 commit into
Open
Conversation
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.
Summary
This PR adds first-class support for tracking multiple subscription plans at the same time, scoped by provider. It fixes the current limitation where setting a Codex/ChatGPT Pro-style plan overwrites an existing Claude plan, forcing users to manually swap plans just to compare overage.
With this change, users can keep separate Claude, Codex, and Cursor plan budgets configured together, while the dashboard and JSON outputs report one overage summary per active provider plan.
Closes #299.
Why
Today CodeBurn stores one plan in
~/.config/codeburn/config.json:{ "plan": { "id": "claude-max", "monthlyUsd": 200, "provider": "claude", "resetDay": 1, "setAt": "2026-04-01T00:00:00.000Z" } }That works for a single subscription, but breaks down for users who subscribe to more than one AI coding product. A common setup is:
codexprovidercursorBefore this PR, running a second
codeburn plan set ...command replaced the first plan entirely. The dashboard could only show one subscription overage line, even though usage data already supports multiple providers.What Changed
Provider-keyed plan config
CodeBurn now supports a provider-keyed
plansmap:{ "plans": { "claude": { "id": "claude-max", "monthlyUsd": 200, "resetDay": 1, "setAt": "2026-04-01T00:00:00.000Z" }, "codex": { "id": "custom", "monthlyUsd": 200, "resetDay": 1, "setAt": "2026-04-01T00:00:00.000Z" } } }Existing single-plan configs remain supported. If a user still has the legacy
plankey, CodeBurn reads it as a single provider-scoped plan. The next plan write migrates the config forward to the newplansmap.CLI behavior
Setting a provider-specific plan now updates only that provider:
Showing plans lists all configured provider plans:
Example output shape:
Provider-specific reset is now supported:
That removes only the Codex plan and leaves the Claude plan intact. A plain reset still clears all plan config:
Preset plans now reject mismatched provider scopes instead of silently ignoring them. For example, this is rejected because
claude-maxis naturally a Claude plan:codeburn plan set claude-max --provider codexCustom plans still support explicit scopes, including
all:codeburn plan set custom --monthly-usd 150 --provider allDashboard overage rows
The dashboard overview can now render multiple plan rows, one per active provider plan. For example, a user with Claude Max and a Codex custom plan will see separate overage summaries instead of one overwriting the other:
Each row keeps its own:
JSON compatibility
JSON report/status payloads preserve the existing top-level
planfield for backwards compatibility, while adding a provider-keyedplansmap.Example shape:
{ "plan": { "id": "claude-max", "provider": "claude", "budget": 200, "spent": 406.2, "percentUsed": 203.1, "status": "over" }, "plans": { "claude": { "id": "claude-max", "provider": "claude", "budget": 200, "spent": 406.2, "percentUsed": 203.1, "status": "over" }, "codex": { "id": "custom", "provider": "codex", "budget": 200, "spent": 118.4, "percentUsed": 59.2, "status": "under" } } }The top-level
planremains the first active plan in provider display order, so existing consumers that only know about a single plan continue to work. New consumers can use the additiveplansmap.Avoiding double counting
Aggregate
allplans are kept mutually exclusive with provider-specific plans. This avoids showing both:which would double-count the same spend in the overage view.
The invariant is enforced in two places:
allplan replaces provider-specific plans, and saving a provider-specific plan removes anyallplanplans.alland provider-specific plans drop the aggregateallentry defensivelyUsage calculation
For multiple active plans, CodeBurn parses sessions once across all providers and then filters the in-memory usage by provider and reset period for each plan. This avoids re-reading all sessions once per configured plan.
For a single active provider plan, CodeBurn keeps the existing parser-level provider filter, so single-plan users do not pay the cost of scanning unrelated providers.
Projection math now keys daily spend from API call timestamps when available, which matches the provider filtering logic and handles turns whose user message and assistant call land on different days.
Tests Added / Updated
This PR adds coverage for:
planfallback into the new plan mapreset --provider allbehavioralland provider-specific plansplan --format jsonparseAllSessions(..., 'all')callValidation
npm run buildnpx vitest run- 47 files / 673 tests passinggit diff --checkNote
npx tsc --noEmitstill fails on an existing unrelated error insrc/models-report.ts(151,13):That file is outside this PR's diff.