Skip to content

fix(menubar): decode the Codex quota windows array - #5

Merged
luwill merged 3 commits into
luwill:mainfrom
amenjason936:fix/menubar-codex-windows
Oct 12, 2026
Merged

luwill merged 3 commits into
luwill:mainfrom
amenjason936:fix/menubar-codex-windows

Conversation

@amenjason936

@amenjason936 amenjason936 commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Since 1.8 the backend emits the Codex quota as quota.codex.data = { plan_type, windows: [{ window_minutes, used_percent, resets_at }] } (src/codexQuota.js). menubar/main.swift:22 still decodes the old flat used_percent, so these never appear in the menubar:

  • the ·C<n>% in the capsule
  • the Codex menu line
  • the 80%/95% notifications

This was a P2 in docs/RELEASE-READINESS-2026-09-24.md.

Change

  • DataQ decodes windows: [Window]?. The flat used_percent stays as a fallback for pre-1.8 snapshots.
  • The capsule and the threshold alerts use the tightest window (highest used_percent).
  • The menu lists each window, labelled 5h, 周, or N分钟.
  • resets_at is deliberately not decoded. It is unused, and a type mismatch on any field makes JSONDecoder fail the whole Summary, which would blank the capsule to T--.

Tests

CI has no swiftc, so test/menubar-contract.mjs checks the Swift ↔ API contract instead, with 14 checks:

  • It builds a real buildSummary payload (Codex windows, balance, active Claude 5h block) in a temp-HOME offline child.
  • For every Decodable struct the menubar declares, it compares each field's type against that payload, including non-optional fields. It reads only the struct's own fields, not nested ones.
  • It checks the plus, pro and legacy-flat shapes from codexQuotaView.
  • It checks the max-window expression and the label mapping.

9 checks fail against the current main.swift. Deliberately wrong versions, such as .min() instead of .max(), a mistyped field, or swapped labels, also fail.

Not compiled here (no macOS on the machine this was made on). Please run npm run build-bar && token-watcher bar against a server with a Codex quota snapshot before merging. The capsule should read T…·C<max%> and the menu should list the 5h/周 lines.

Independent of the other PRs in this set. Only the registration line in test/run.mjs overlaps.

🤖 Generated with Claude Code

Since 1.8 the backend emits quota.codex.data as { plan_type, windows: [...] }
(src/codexQuota.js), but the menubar still decoded the flat used_percent.
So the Codex % in the capsule, its menu line and the 80/95% notifications
never appeared. The capsule and alerts now follow the tightest window,
and the menu lists each window (5h / 周). The flat field stays as a
fallback for pre-1.8 snapshots. resets_at is deliberately not decoded,
since a type mismatch on any field fails the whole Summary decode.

CI has no swiftc, so test/menubar-contract.mjs checks the contract
instead. It builds a real buildSummary payload in a temp-HOME child,
compares every Decodable struct's fields (including non-optional ones)
against it by type, and asserts the max-window expression and labels.
9 checks fail against the current main.swift. Please build on macOS
(npm run build-bar) before merging: not compiled here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@luwill
luwill merged commit 1956bec into luwill:main Oct 12, 2026
10 checks passed
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.

3 participants