Skip to content

fix(reset): stop refusing eligible credits on wham applicable_available_count - #133

Merged
ualtinok merged 2 commits into
cortexkit:mainfrom
iceteaSA:fix/reset-applicable-gate
Sep 4, 2026
Merged

fix(reset): stop refusing eligible credits on wham applicable_available_count#133
ualtinok merged 2 commits into
cortexkit:mainfrom
iceteaSA:fix/reset-applicable-gate

Conversation

@iceteaSA

@iceteaSA iceteaSA commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Fixes #131. Implements the ruling in the issue thread: eligibility no longer refuses on wham's applicable_available_count; the disagreement is disclosed in the confirmation instead.

Changes

core/reset-credits.ts

  • evaluateResetPrecondition drops the applicableAvailableCount <= 0 refusal and its parameter. Exhaustion (resetWindowIsExhausted: usedPercent >= 100 and resetsAt in the future, or an active rate-limit mark) is the only precondition. selectCreditToSpend stays the credit-side gate.
  • The pre-consume re-check in the redemption path applies the same rule. no_applicable_credits is gone from ResetRedemptionErrorKind and the reason union; nothing produced it anymore.

commands.ts

  • Preview keeps applicableAvailableCount for the applicable/available display line.
  • L2 confirmation: Spend 1 of ${availableCount}, plus, when availableCount > 0 && applicableAvailableCount === 0, one line: the server does not currently count this credit as applicable; redemption may return a no-op, and a no-op does not spend the credit. "May", not a promise.

tui/command-dialogs.tsx

  • The TUI DialogConfirm interpolated applicableAvailableCount in its SPENDS 1 of N line, so the newly eligible shape would have shown "Spend 1 of 1" and "SPENDS 1 of 0" in the same dialog. Now availableCount on both surfaces.

Tests

The two regressions from the thread plus one for the disclosure:

  • exhausted window, future resetsAt, resetCreditsAvailable: 1, resetCreditsApplicable: 0, one available plan-supported codex_rate_limits credit → preview row eligible: true. Reddened by restoring the applicable <= 0 refusal.
  • usedPercent: 100 with resetsAt in the past → eligible: false, reason not exhausted. Reddened by making resetWindowIsExhausted ignore resetsAt.
  • confirmation carries the disclosure line exactly when availableCount > 0 && applicable === 0. Reddened by removing the condition.
  • the TUI confirmation fixture now uses available 2, applicable 0, so the existing SPENDS 1 of 2 assertion reddens if the tsx reverts to the applicable count.

Existing tests that asserted the old refusal were rewritten to the new property, not deleted.

bun run test: 1122 pass / 0 fail / 45 files (baseline at 512e451: 1120). Typecheck and Biome clean. Order-dependence scan clean on commands.test.ts, reset-credits.test.ts, command-dialogs.test.ts. An independent review re-applied all three mutations and confirmed each goes red.

Not done, on purpose

No probe of what the server returns for a consume against a window it considers healthy, per the thread. The confirmation says "may no-op" and stops there.


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 #131. Reset eligibility no longer refuses on wham's applicable_available_count; the precondition now checks exhaustion only, and the confirmation discloses when the server reports zero applicable credits.

  • The pre-consume re-check applies the same exhaustion-only rule.
  • Both the markdown preview and TUI confirmation now show the available count, not the applicable count.
  • When available credits exist but none are applicable, the confirmation says redemption may no-op without spending the credit.
  • Removes the no_applicable_credits error path and updates tests for the new eligibility rule.

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

Review in cubic

…le_count

Remove applicable_available_count from the exhaustion precondition while retaining it for display and confirmation disclosure. Use available credit counts in the confirmation and update reset command, core, and dialog coverage for stale windows and server applicability disagreement.
The markdown confirmation already spends "1 of ${availableCount}"; the
TUI DialogConfirm still interpolated applicableAvailableCount, so the
newly eligible shape (available 1, applicable 0) rendered "Spend 1 of 1"
and "SPENDS 1 of 0" in the same dialog. Both surfaces now read
availableCount. The confirmation fixture carries that exact shape so the
existing "SPENDS 1 of 2" assertion pins it.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 6 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/opencode/src/commands.ts">

<violation number="1" location="packages/opencode/src/commands.ts:1315">
P2: When the credit list contains an eligible credit but both available-count fields are omitted, this confirmation still says `Spend 1 of 0`. Preserve the unknown count or render an explicit unknown/at-least-one value instead of defaulting it to zero.</violation>

<violation number="2" location="packages/opencode/src/commands.ts:1320">
P2: When WHAM omits `applicable_available_count`, this condition treats the missing metric as an explicit zero and tells users the server excludes the credit. Keep `undefined` distinct from `0`, and disclose only for an explicitly reported zero.</violation>
</file>

<file name="packages/opencode/src/tests/reset-credits.test.ts">

<violation number="1" location="packages/opencode/src/tests/reset-credits.test.ts:461">
P3: The renamed test 'accepts exhausted quota when the server reports no applicable credits' is now identical to the test above it ('accepts an exhausted live window with an applicable credit'): both call evaluateResetPrecondition({ primary: quotaWindow(100) }, false, now) and expect { ok: true }. Because applicableAvailableCount was removed from the function signature, this test no longer tests anything about applicable credits and only duplicates the prior case. Rename it to reflect what it actually asserts (e.g. 'accepts exhausted quota regardless of applicable credits') or drop it.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

`Quota resets: **${row.resetTime ?? 'unavailable'}**`,
'',
]
if ((row.availableCount ?? 0) > 0 && row.applicableAvailableCount === 0) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When WHAM omits applicable_available_count, this condition treats the missing metric as an explicit zero and tells users the server excludes the credit. Keep undefined distinct from 0, and disclose only for an explicitly reported zero.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/commands.ts, line 1320:

<comment>When WHAM omits `applicable_available_count`, this condition treats the missing metric as an explicit zero and tells users the server excludes the credit. Keep `undefined` distinct from `0`, and disclose only for an explicitly reported zero.</comment>

<file context>
@@ -1313,11 +1312,17 @@ function renderResetConfirm(row: ResetPreviewRow): string {
     `Quota resets: **${row.resetTime ?? 'unavailable'}**`,
     '',
   ]
+  if ((row.availableCount ?? 0) > 0 && row.applicableAvailableCount === 0) {
+    lines.push(
+      'The server does not currently count this credit as applicable; redemption may return a no-op, and a no-op does not spend the credit.',
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid. The === 0 was right but commands.ts:1240 coerced the omitted field with ?? 0 before it reached the row. 225c437 keeps undefined through to the row, discloses only on an explicit 0, and renders ?/N in the preview line when the field is absent. Test: omitted resetCreditsApplicable → no disclosure, ? marker; re-adding the ?? 0 makes it fail.

`Account: **${row.label}** (\`${row.accountKey}\`)`,
`Current quota: **${row.usedPercent ?? 'unknown'}% used**`,
`Credit: **Spend 1 of ${row.applicableAvailableCount ?? 0}**`,
`Credit: **Spend 1 of ${row.availableCount ?? 0}**`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When the credit list contains an eligible credit but both available-count fields are omitted, this confirmation still says Spend 1 of 0. Preserve the unknown count or render an explicit unknown/at-least-one value instead of defaulting it to zero.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/commands.ts, line 1315:

<comment>When the credit list contains an eligible credit but both available-count fields are omitted, this confirmation still says `Spend 1 of 0`. Preserve the unknown count or render an explicit unknown/at-least-one value instead of defaulting it to zero.</comment>

<file context>
@@ -1313,11 +1312,17 @@ function renderResetConfirm(row: ResetPreviewRow): string {
     `Account: **${row.label}** (\`${row.accountKey}\`)`,
     `Current quota: **${row.usedPercent ?? 'unknown'}% used**`,
-    `Credit: **Spend 1 of ${row.applicableAvailableCount ?? 0}**`,
+    `Credit: **Spend 1 of ${row.availableCount ?? 0}**`,
     `Credit expires: **${row.selectedCreditExpiresAt ?? 'unavailable'}**`,
     `Quota resets: **${row.resetTime ?? 'unavailable'}**`,
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid. 225c437: when both available_count fields are absent, availableCount falls back to the number of credits the eligibility filter accepts (factored into isResetCreditEligible / countEligibleResetCredits so selectCreditToSpend and the count share one definition); if that is also 0 the confirmation renders unknown, never 0. The TUI DialogConfirm reads the same row value, so both surfaces agree. Test: both counts omitted, one eligible credit → Spend 1 of 1; removing the list fallback makes it fail.

})

it('refuses exhausted quota without applicable credits', () => {
it('accepts exhausted quota when the server reports no applicable credits', () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The renamed test 'accepts exhausted quota when the server reports no applicable credits' is now identical to the test above it ('accepts an exhausted live window with an applicable credit'): both call evaluateResetPrecondition({ primary: quotaWindow(100) }, false, now) and expect { ok: true }. Because applicableAvailableCount was removed from the function signature, this test no longer tests anything about applicable credits and only duplicates the prior case. Rename it to reflect what it actually asserts (e.g. 'accepts exhausted quota regardless of applicable credits') or drop it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/tests/reset-credits.test.ts, line 461:

<comment>The renamed test 'accepts exhausted quota when the server reports no applicable credits' is now identical to the test above it ('accepts an exhausted live window with an applicable credit'): both call evaluateResetPrecondition({ primary: quotaWindow(100) }, false, now) and expect { ok: true }. Because applicableAvailableCount was removed from the function signature, this test no longer tests anything about applicable credits and only duplicates the prior case. Rename it to reflect what it actually asserts (e.g. 'accepts exhausted quota regardless of applicable credits') or drop it.</comment>

<file context>
@@ -448,20 +448,20 @@ describe('reset redemption precondition', () => {
   })
 
-  it('refuses exhausted quota without applicable credits', () => {
+  it('accepts exhausted quota when the server reports no applicable credits', () => {
     expect(
-      evaluateResetPrecondition({ primary: quotaWindow(100) }, false, 0, now),
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, it was a duplicate after the parameter went away. Dropped in 225c437; the property is pinned at the commands level by exhausted preview remains eligible when the server reports no applicable credits, which is the test that goes red if the refusal comes back.

@ualtinok
ualtinok merged commit cc694fb into cortexkit:main Sep 4, 2026
5 checks passed
@oaiauth-alfonso

Copy link
Copy Markdown

Merged as cc694fb. Verified independently rather than from the report — all four mutations re-applied here:

mutation result
restore the applicable <= 0 refusal 15 fail
resetWindowIsExhausted ignores resetsAt 2 fail
remove the disclosure condition 1 fail
TUI reverts to applicableAvailableCount 3 fail

The TUI catch is yours and I missed it. My ruling on the issue named the confirmation as the place to disclose the disagreement, and I did not trace that DialogConfirm was interpolating the applicable count into its own spend line. The eligible shape would have rendered Spend 1 of 1 and SPENDS 1 of 0 in the same dialog — a self-contradicting confirmation on an irreversible action, which is worse than the bug being fixed. Following a ruling into the surfaces it touches, rather than stopping at the file it names, is the part that made this complete.

One thing I checked because it is the failure mode I have been rejecting all week: refuses exhausted quota without applicable credits is gone from reset-credits.test.ts. It encoded the bug, and it is replaced by accepts exhausted quota when the server reports no applicable credits asserting the inverted property on the same shape. That is a rewrite, not a deletion to make a change pass.

Also worth recording: my first attempt at the disclosure mutation did not match the source, so the suite stayed green and briefly looked like a vacuous test. The mutation has to be confirmed to have applied before its result means anything — a substitution that silently matches nothing proves exactly as much as not running it.

Gate on main after merge: 1122 pass / 0 fail, typecheck and Biome clean.

Declining the probe was right. The confirmation says "may no-op" and the operator decides with everything we know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants