Skip to content

v1.2.0 — scoped limits, honest staleness, concurrency safety

Choose a tag to compare

@aweussom aweussom released this 11 Aug 12:14
· 6 commits to main since this release

Everything since v1.0.0. Version 1.1.0 was bumped in the manifests but never tagged or released, so its changes ship here too.

Plugin users: marketplace.json was stuck at 1.0.0 through both bumps, so /plugin marketplace add was offering 1.0.0 regardless of what was on main. Fixed in this release.

Scoped limits

If your plan includes a model- or surface-scoped limit — a separate allowance from the 5h and weekly buckets — it now gets its own status line segment:

Opus 5 | main | ctx:36% | 5h:70% ~1h54m | 7d:9% ~6d14h | Fable:15%

Read generically from the API's limits[] array, labelled from scope.model.display_name with fallbacks to model.idsurfacekind. No model name is hardcoded, so a bucket arriving or leaving with your plan needs no code change. A successful fetch is authoritative including by omission: a bucket you no longer have simply stops being reported.

New result keys: scoped_summary, scoped_alert, scoped_worst, scoped_worst_pct, scoped_worst_resets_in. All empty for accounts with no scoped limits, so nothing changes if you don't have one.

Honest staleness — ? instead of a spent figure

A cached percentage is trustworthy only until the window it describes resets. If fetches are failing when a window rolls over, the library can prove its figure is obsolete, so it drops the value and sets percent_unknown; the bundled status line renders 5h:?⚠.

Previously a rolled-over window displayed its last percentage indefinitely, next to a ~0 min countdown. Buckets are judged independently — a rolled-over 5h window leaves the weekly and scoped figures alone.

Fixes

  • Field-shift bug in the bash cache parser. _ql_parse_cache split jq output on \t, but tab is IFS whitespace, so bash strips leading tabs and collapses runs of them. Any empty field shifted every value after it — concretely, a null 5h percentage put the weekly figure into QUOTA_RESULT[pct], silently. Both parsers now use 0x1F, which is non-whitespace and preserves empty fields. This affected Linux/WSL2 users of 1.0.0.
  • Cache writes are now atomic (write-to-temp then rename). Several Claude Code sessions share one cache file, and a torn read parsed as "no cache", which sent that session down the blocking fetch path.
  • Fetch lock uses O_EXCL / FileMode::CreateNew instead of check-then-write, which let simultaneous sessions all launch a fetch at once. Locks are abandoned after 120s, since a recycled PID would otherwise look like a fetch in flight forever.
  • Retries taper after failures — 60s → 120s → 240s → 480s, capped at 15 min, keyed on the consecutive_failures counter that already existed and was unused. A rate-limited endpoint was previously retried on every refresh, turning a brief outage into a long one.
  • -NoProfile added to the documented status line command. Without it your PowerShell profile loads on every refresh and any output it produces lands on stdout ahead of the status line.

Also in this release (from 1.1.0)

  • An error result key, so status lines can say why quota is missing instead of showing nothing or a bare stale marker
  • HTTP 200 without usage fields, and an unreadable quota-data.json, are reported as explicit parse errors instead of failing silently
  • /quota skill always surfaces the error when stale or invalid
  • Execution-policy docs covering managed vs unmanaged machines

Cache schema 2 → 3

Adds scoped_limits[] and percent_unknown. Additive only — an older library reads a v3 cache without complaint.

Tests

New tests/test-quota-lib.sh — 38 checks over synthetic fixtures shaped like real API responses. No network calls, so it can't consume quota or trip rate limits. Requires jq.

bash tests/test-quota-lib.sh

Upgrading

Windows: re-run install.ps1. Linux/WSL2: re-run install.sh. Plugin users: reinstall from the marketplace to pick up 1.2.0. No cache reset needed — the schema change is additive.