Skip to content

The credit row broke the column alignment its own comment documents - #161

Merged
ualtinok merged 4 commits into
cortexkit:mainfrom
iceteaSA:fix/credit-bar-alignment
Sep 18, 2026
Merged

ualtinok merged 4 commits into
cortexkit:mainfrom
iceteaSA:fix/credit-bar-alignment

Conversation

@iceteaSA

@iceteaSA iceteaSA commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #160. Both defects were caught by looking at the rendered sidebar; neither was visible to the tests that shipped with it.

The label column overflowed

QuotaRow padded with props.label.padEnd(3), directly beneath a comment stating the invariant:

Left group (label · bar · pct) stays left-aligned in fixed columns so bars and percentages line up across rows

5h and 7d are two characters, so the hardcoded 3 covered them. credits is seven — padEnd(3) is a no-op, and the bar plus its percentage started four columns right of every other row.

The column belonged to the wrong scope

Deriving the width from the row set fixes one account and breaks the sidebar, because buildQuotaRowsForDisplay is called with a single account's quota. An account with no spend control computes 3 while one with credits computes 8, so stacked accounts disagree about where the bar starts:

cortex      7d  ▓▓▓▓▓▓▓▓  86%
work-alt    5h       ▓▓▓▓▓▓▓▓   0%
            credits  ▓▓▓▓▓▓▓▓  20%

The column is a property of the sidebar, not of a row set. computeQuotaLabelWidth now takes the account list and is computed once for every rendered account:

cortex
  7d      ▓▓▓▓▓▓▓▓  86%
work-alt
  5h      ▓▓▓▓▓▓▓▓   0%
  7d      ▓▓▓▓▓▓▓▓  51%
  credits ▓▓▓▓▓▓▓▓  20%

Semantics: no account anywhere has a credit budget → width stays 3, byte-identical to today; any account has one → every account shares the wider column. The first half is the regression guard, since most setups have no spend control at all.

The width is label.length + 1 rather than the bare maximum. Padding to exactly the longest label leaves it with no separator (credits▓▓▓▓) — invisible for 5h/7d only because the floor of 3 already exceeds them.

The amounts were parsed and dropped

normalizeWham parses limit, used, remaining and unit; the sidebar rendered only usedPercent. "20%" of an unknown budget is not actionable — 20% of 2,500 credits and 20% of 50 look identical.

Now 502 / 2,500 credits in the sidebar and 502 / 2,500 credits, 1,998 remaining in /openai-quota. Three details that matter: used arrives as 501.7787666320801 and must not render raw; the unit comes from the unit field rather than being assumed, so no $ — these are credits, not currency; and the unit is pluralised against the quantity.

Verification

Mutation proofs, each reverting one change alone:

(fail) one account with spend control widens every account label column   [width made per-account]
(fail) renders grouped credit amounts with a pluralised unit             [compact k form restored]
(fail) renders grouped credit amounts with a pluralised unit             [pluralisation removed]

I checked the regression guard directly rather than trusting the assertion — computeQuotaLabelWidth returns 3 for a two-account sidebar with no spend control anywhere, and 8 once one account has it.

Worth noting why #160's tests passed through this: they assert on the row projection — labels, values, presence — which cannot see that two accounts disagree about where column two begins. Geometry needs a test that spans accounts.

Gates from the repo root: build, format:check, lint, types, test — 147 core + 1377 OpenCode (+3) + 14 Pi.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Fixes the quota sidebar so bars and percentages stay aligned across accounts even when a credit row is present.

The label column width is now computed once over the whole sidebar rather than per-account, so an account with a credits row no longer shifts its own bars to the right of every other row. Accounts without a spend control keep the previous 3-character width, so rendering is byte-identical when no account has a credit budget.

Also renders credit budget amounts with grouped digits and a pluralized unit — 502 / 2,500 credits — in both the sidebar and /openai-quota, replacing the previously unreadable raw 502 / 2500.

Written for commit ecc673b. Summary will update on new commits.

Review in cubic

@ualtinok
ualtinok merged commit 0890c80 into cortexkit:main Sep 18, 2026
5 checks passed
@ualtinok

Copy link
Copy Markdown
Contributor

Merged as part of ff2458b. Good catch, and the scope argument is the right one — the column belongs to the sidebar, not to a row set, and the stacked-accounts example is what makes that obvious.

The label.length + 1 detail is worth having spelled out. Padding to the bare maximum leaves no separator, and that is invisible today only because the floor of 3 already exceeds 5h and 7d — so the bug would have appeared the first time a label was exactly as long as the widest one.

One gap I closed on top

Your test pins computeQuotaLabelWidth: give it two accounts, get the wider column. Nothing pinned what the sidebar hands it, and that is the half that was wrong before — measuring one account's rows instead of every rendered account is precisely how the columns came to disagree.

I checked by mutation rather than assuming: narrowing both call sites to [state().main?.quota ?? null] — the original defect, restored — left all 1377 tests green. The list was built inline at two call sites inside a component no test can render without the host runtime, so it was unreachable from the suite.

It is now renderedQuotas(state), named with the reason beside it, and dropping fallbacks from it reddens. It also covers a case neither call site spelled out: a disabled account draws no rows, so counting its labels would widen the column for a row nobody sees. Your inline version had the enabled filter and was right about it; now that is stated rather than incidental.

This is the second time in two PRs that the property was pinned and the wiring was not — same shape as the double guard on #157. Worth watching for as a pattern rather than treating each as a one-off: a unit test on the function, and the thing that feeds it left in a place tests cannot reach.

Gate: core 147, opencode 1378, pi 14, typecheck and biome clean.

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