Skip to content

feat(usage): report per-model plan limits in their own bar row - #46

Merged
serialexperimentslainnnn merged 1 commit into
developfrom
bugfix/notification-100-usage
Aug 10, 2026
Merged

serialexperimentslainnnn merged 1 commit into
developfrom
bugfix/notification-100-usage

Conversation

@serialexperimentslainnnn

Copy link
Copy Markdown
Owner

What

  • Fable usage is reported. Per-model windows live in rate_limits.model_scoped — an array the parser walked past. Reading it is necessary and not sufficient: the binary synthesises that key behind its own remote config (IUt(limits, jJe()) returns [] when tengu_usage_overage_included_models is empty, and the key is spliced in only when the projection yielded something), so a --print session never received it. The raw rate_limits.limits[] array it projects from does arrive untouched, so weekly_scoped entries naming a model are read from there too — the binary's filter, without its allowlist, since that list selects overage billing, not which limits meter you. resets_at is epoch seconds there as often as a string, so it is normalised rather than deserialized (a numeric one would have dropped the window in silence).
  • No more flicker. loadPlanRateLimits gives the usage endpoint 5 s and falls back to seedUtilization(), an object rebuilt from the rate-limit response headers that structurally carries only five_hour/seven_day, flagged "seeded" and accepted identically downstream. A failed poll was therefore indistinguishable from one saying the per-model window is gone. A refresh is now merged into the last one by window key.
  • The plan limits get their own responsive row under the status line, one labelled bar per window, instead of dots trailing a wrapping row of unrelated metrics.
  • Also in this cycle: the "Other models" picker group (with set_model as a correlated request, so a refusal restores the previous model), nimbus_quill hidden on both ingestion paths, and quota notifications titled through UsageWindow.title(key).

Evidence

Every claim about the binary was read out of claude 2.1.223, not inferred, and confirmed live against idea.log:

usage window model_scoped:Fable: utilization=17.0 -> 17%
get_usage raw limits[]: [… {"kind":"weekly_scoped","percent":17,
  "scope":{"model":{"display_name":"Fable"}}, …}]

(that diagnostic log was temporary and is not in this branch)

Gates — run clean locally (clean … --rerun-tasks)

Gate Result
test 780 tests, 0 failures
koverVerify / detekt / spotlessCheck pass
verifyPlugin Compatible: IC-251, IC-252, IU-253, IU-261, IU-262
buildPlugin 0 node_modules entries, META-INF/{LICENSE,THIRD-PARTY-NOTICES.md} present
npm test / lint / format 111/111, clean
npm audit --omit=dev 0 vulnerabilities

Manually validated in a real IDE (PyCharm 2026.2) before release.

🤖 Generated with Claude Code

Fable usage is reported. Per-model windows arrive in rate_limits.model_scoped, an array alongside
the keyed windows, which parseUsageReport walked past entirely. Reading it is necessary and not
sufficient: the binary does not relay that key, it synthesises it behind its own remote config
(IUt(limits, jJe()) returns an empty list when tengu_usage_overage_included_models is empty, and the
key is spliced in only when the projection yielded something), so in a --print session it never
arrived. The raw rate_limits.limits[] array it projects from does arrive untouched, so the
weekly_scoped entries that name a model are read from there too, with the binary's filter and
without its allowlist -- that list selects overage billing, not which limits meter you. resets_at is
epoch seconds there as often as a string and is normalised rather than deserialized.

A refresh is now merged into the last one instead of replacing it. loadPlanRateLimits gives the
usage endpoint 5s and falls back to seedUtilization(), an object rebuilt from the rate-limit
response headers that can only carry five_hour and seven_day; it is flagged "seeded" and accepted
identically downstream, so a failed poll was indistinguishable from one saying the per-model window
is gone, and the bar blinked out and back. Merged by key over the whole set, since the opus and
sonnet windows are missing from a seeded object for the same reason. The extra-credit balance is
deliberately not carried.

The plan limits moved out of the readout into their own responsive row under the status line, one
labelled bar per window. Inline they trailed a wrapping row of unrelated metrics, so the windows
nearest their cap were the first to wrap out of sight.

Also in this cycle: an "Other models" group in the picker holding previous generations, with
set_model sent as a correlated control request so a refusal restores the previous model instead of
leaving the tab pointed at one every turn would fail on; the nimbus_quill window hidden on both
ingestion paths rather than only the report; and quota notifications titled through
UsageWindow.title(key) so a per-model window announces "Fable", not its synthetic key.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@serialexperimentslainnnn
serialexperimentslainnnn merged commit ae3134a into develop Aug 10, 2026
10 checks passed
@serialexperimentslainnnn
serialexperimentslainnnn deleted the bugfix/notification-100-usage branch August 10, 2026 15:23
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