Skip to content

Show detected plan evidence from the CLI - #15

Merged
starkdmi merged 1 commit into
mainfrom
account-plans-cli
Aug 26, 2026
Merged

starkdmi merged 1 commit into
mainfrom
account-plans-cli

Conversation

@starkdmi

Copy link
Copy Markdown
Owner

Adds statsai account plans, closing a gap opened by #14.

Why

The account-plan evidence work converted Codex local_auth subscriptions into
account_plan_observations and cleared plan_name from the account rows. Both
changes are correct — a plan detected from auth.json is not a billing record,
and the price those rows carried came from a hardcoded "plus" => 2000 table
rather than from the user.

But nothing in the CLI reads account_plan_observations. So subscription list
went from four rows to one, account list now returns plan_name: null, and the
plans an account is on became visible only after syncing to the hosted dashboard.
For the open-source, local-first half of the product that is a regression in
reach even though no data was lost.

What it does

statsai account plans [--provider <p>] [--account <selector>] [--all]

Reports the newest stored observation per account, plus the observation count,
and every observation behind --all. Against a real store:

account plan observed evidence n
starkov100@… Plus 2026-08-26 quota_status 5
starkdev@… Plus 2026-08-26 quota_status 4
starkovd@… Plus 2026-08-26 quota_status 3
hello@videochapter.app Plus 2026-05-28 quota_status 3
pavel.du.rove2@… Free 2026-08-11 quota_status 6

That matches the dashboard's cards, including the Free that explains why the
manual $20 subscription on that account needed ending.

What it deliberately does not do

It does not derive a plan timeline. deriveAccountPlanHistory lives in the
hosted API, and a second implementation here could disagree with it about the
same rows. The field is latest_observation, not current_plan: it reports the
most recent thing this machine recorded, not a verdict about what the account is
on now. Observations that never resolved to an account are still listed, under a
null account id, rather than dropped.

Review notes

A review pass on the first draft found two issues, both fixed here:

  • Provider was compared and grouped byte-for-byte. The legacy migration matches
    codex case-insensitively and then copies the subscription's own provider
    string through, so a stored Codex would have been hidden from
    --provider codex and listed again under its own heading. Providers are now
    canonicalized (case-folded before the alias lookup, since
    adapter_for_provider matches exact lowercase literals) before both the
    filter and the grouping key.
  • The test claimed the newest observation wins "not the current-snapshot flag"
    while no fixture set that flag. A fixture now sets is_current_snapshot: true
    on an older observation, and another stores its provider as Codex, so both
    behaviours are actually exercised.

Verification

cargo test -p statsai -p statsai-store -p statsai-adapters (193 + 237 + 210),
cargo clippy --all-targets -D warnings, cargo fmt --check, and a manual run
against a 3.8 GB store.

Converting local_auth subscriptions into plan observations moved detected
plans out of `subscription list` and cleared `plan_name` from `account list`,
and nothing reads `account_plan_observations`. The plans an account is on
became visible only after syncing to the hosted dashboard.

`account plans` reports the newest stored observation per account, with every
observation behind `--all`. It deliberately does not derive a timeline: that
derivation lives in the API, and a second copy here could disagree with it.
The field is `latest_observation`, not `current_plan`, for that reason.

Providers are canonicalized before filtering and grouping. The legacy
migration copies a subscription provider string through verbatim, so a stored
`Codex` would otherwise be hidden from `--provider codex` and listed again
under its own heading.
@starkdmi
starkdmi merged commit a2e759f into main Aug 26, 2026
4 checks passed
@starkdmi
starkdmi deleted the account-plans-cli branch September 1, 2026 13:13
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