Skip to content

tui: a Codex row with no session window draws one wide weekly bar - #463

Merged
MagicalTux merged 6 commits into
KarpelesLab:masterfrom
thomasahle:pr/tui-codex-weekly-bar
Sep 25, 2026
Merged

MagicalTux merged 6 commits into
KarpelesLab:masterfrom
thomasahle:pr/tui-codex-weekly-bar

Conversation

@thomasahle

Copy link
Copy Markdown
Contributor

Codex subscriptions publish a 7-day window and no 5-hour one, so those rows showed a Ses - cell beside Claude rows with a real session bar. A Codex row that has reported a weekly window and no session window now draws only Wk, at the width of both cells; an account that has not reported yet keeps both cells until its probe says otherwise, so the column does not flicker at startup. Tests updated to the per-row rule.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VGCUTFLQcsfvXQMaqGyqP8

thomasahle and others added 6 commits September 24, 2026 16:58
Codex subscriptions publish a 7-day window and no 5-hour one, so the Ses cell
on those rows read `-` while Claude rows beside them kept a real session
bar. The row now decides for itself: a Codex account that has reported a
weekly window and no session window draws only Wk, at the width of both
cells, whatever its neighbours state; an account that has not reported yet
keeps both cells until its probe says otherwise, so the column does not
flicker at startup.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VGCUTFLQcsfvXQMaqGyqP8
The per-row rule keyed on `unified5h == null`, which _clearExpiredQuotas
nulls the moment a 5-hour window runs out, so a Codex row that does meter
one would have swung between Ses/Wk and one wide weekly bar every five
hours. Record what the reading actually said instead: `_updateCodexQuota`
and `applyCodexUsageData` set `quota.sessionWindowStated` to true when a
reading carried a 5-hour window and false when it carried a weekly one and
no 5-hour one; the expiry sweep leaves it alone. The row rule and the
pane-level shortBar rule both read that fact, so they agree. It rides
into status through the `{...quota}` spreads (attach mode copies the
whole object) and is persisted, so a restored row does not change shape
at its first reading.

Tests: the bar-width assertion in tui-split-panes matched brackets a bar
never draws and so passed unconditionally; it now measures the drawn rows
(the Codex Wk bar spans 2*bw+6 columns, ends where the Claude row's Wk bar
ends, and is the last thing on its row). Added the case the rule must not
trigger on: a Codex row whose 5-hour window expired keeps both cells.
docs/usage.md describes the per-row rule.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The rows were aligned; the test's index math was one short on both sides.
The weekly label is `Wk ` — three columns, padded to `Ses`'s width — and
the row adds one more space before the bar, so a bar starts four columns
after its `W`, not three. The Codex-row check therefore expected 77 for a
78-column row, and the Claude-row check only agreed because it was short
by the same one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@MagicalTux
MagicalTux merged commit ed1606a into KarpelesLab:master Sep 25, 2026
5 checks passed
@MagicalTux

Copy link
Copy Markdown
Member

Going in, reworked on the branch: keying on unified5h == null flipped the row every five hours, because _clearExpiredQuotas nulls the session reading the moment its window expires, and master's Codex parser does fill unified5h from the model-scoped family for most subscriptions. The rule now keys on a sticky fact recorded when a reading arrives (quota.sessionWindowStated, persisted), applied to both the per-row and the pane-level rule; the test asserted against brackets that bar() never draws, so it passed unconditionally — replaced with a column check, plus an expired-window case; docs updated.

@MagicalTux MagicalTux mentioned this pull request Sep 29, 2026
MagicalTux added a commit that referenced this pull request Sep 29, 2026
Thirty-two commits since 1.1.21. Two change routing on an existing
config without an opt-in (#480, #481); the rest is opt-in, additive, or
display.

Behaviour changes
  #481 an API-key account's 401 is a cooldown, not a permanent `error`:
       1 min, then 5, 15 and 60 for every further rejection with no success
       in between; any 2xx/3xx resets it. The request still fails over and
       the client never sees the 401. OAuth accounts are unchanged
  #480 with session distribution on, requests carrying no session id stay
       within the top priority tier, so a fallback gateway no longer answers
       Claude Code's bootstrap and connector calls
  #470 a 200 whose SSE stream reports a provider failure before any output
       (`server_is_overloaded`, `response.failed`) fails over once, like a
       status-shaped failure would
  #465 a reload removes running accounts whose config entry is gone from
       disk, so `teamclaude remove` from another shell takes effect at once
  #460 `import` refuses an account whose token upstream has definitively
       rejected (401/403), even with `--name`; a 5xx or timeout still imports

Rename
  #483 the project is being renamed to TeamRouter (#72). This release accepts
       the new name everywhere the old one is read and changes nothing an
       install has on disk: `teamrouter` runs the same CLI, every
       `TEAMCLAUDE_*` variable is also read as `TEAMROUTER_*` (which wins when
       both are set), every `/teamclaude/…` control route also answers at
       `/teamrouter/…`, and `~/.config/teamrouter.json` is used when it exists

Features
  #441 per-account egress proxy (`accounts[].routing`: http, socks4/4a,
       socks5/5h) for refresh, probes and requests; `login --routing`,
       `teamclaude routing set/show/clear`, a connection check before it is
       relied on, and a short hold when the proxy is unreachable
  #427 `accounts[].allowExtraUsage: true` lets a paid extra-usage account
       serve once every account is past its threshold, instead of a 429
  #466 `accounts[].maxSpend`, a money cap judged against the month-to-date
       extra-usage spend upstream reports; the TUI shows what an account
       has billed
  #436 `autoRedeemResets` spends a free Codex rate-limit reset credit when
       the Codex pool runs dry (off by default)
  #482 `advisorEligibility: "strict" | "prefer"`; when the advisor model
       narrows selection to a subset of the fleet the log says so, and status
       carries the reading (`advisorNarrowing`)
  #478 `stripOverageHeaders` drops another org's per-organization billing
       headers from responses, for a pool spanning several orgs (#476)
  #471 `quota.unified5hSeenAt` / `unified7dSeenAt` in status: when upstream
       last stated each shared window
  #446 client and dimension usage for the last 5h and 24h in status and the
       dashboard, resumed across restarts
  #458 #459 #461 the dashboard sets the switch threshold, enables/disables and
       reprioritizes an account, and has a light theme remembered per browser
  #464 `l` in the TUI signs an account in `error` in again from the dashboard
  #457 status records which Codex limit meters each model
       (`quota.codexModelLimits`)
  #442 `quotaBarPercent` drops the percentage beside a TUI bar's countdown
  #451 `stripRequestFields` takes `content.<block type>` to drop content
       blocks a strict Anthropic-compatible upstream rejects
  #469 `proxy.mcp` schemas declare their item types, the write audit line
       records what happened, and the write queue has a depth (#447–#450)

Fixes
  #477 a refused WebSocket handshake whose headers all drop is relayed as a
       well-formed head instead of a blank line and body bytes
  #474 two members of one ChatGPT workspace are told apart by user id, so a
       second `login --codex` no longer replaces the first
  #469 a Codex Responses stream with no Content-Type is relayed as a stream
       and booked; thread repair on the global upstream; TUI settings and
       status gaps; a hint when a local login would have served
  #463 a Codex row with no session window draws one wide weekly bar

Tests
  #484 #485 #486 the suite asserts behaviour, not the scheduler: wall-clock
       upper bounds are gone, and subprocess tests spawn the server through
       `test-helpers/spawn-server.js`, which verifies the server it reached by
       `server.pid` (new in status) instead of trusting a port

Tooling
  #452 #453 #454 #455 docker workflow actions bumped
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.

2 participants