Skip to content

fix(opencode): stale fallback quota and clobbered active account in sidebar#57

Merged
ualtinok merged 3 commits into
cortexkit:mainfrom
iceteaSA:fix/sidebar-fallback-quota
Jun 4, 2026
Merged

fix(opencode): stale fallback quota and clobbered active account in sidebar#57
ualtinok merged 3 commits into
cortexkit:mainfrom
iceteaSA:fix/sidebar-fallback-quota

Conversation

@iceteaSA
Copy link
Copy Markdown
Contributor

@iceteaSA iceteaSA commented Jun 4, 2026

Summary

Two independent fixes to the OpenCode quota sidebar.

BUG 1 — stale fallback quota

Background fallback-quota refresh updated the QuotaManager cache + disk but never triggered a sidebar write, so a fallback account's quota in the sidebar went stale until /claude-quota was run manually. Adds an onFallbackQuotaFetched callback to FallbackAccountManager, fired after a background quota pass persists a change, wired in the plugin to re-render the sidebar.

BUG 2 — clobbered active account

The fire-and-forget quotaManager.refreshMain().then(...) callback hardcoded activeId: 'main' and fired asynchronously after routing was decided, clobbering the active fallback back to main (e.g. in fallback-first the served fallback flipped back to main). Adds a lastSidebarRouting memory recorded in writeSidebarState, and routes the async callback through a new refreshSidebarQuota() that reuses the last routing instead of hardcoding main.

Changes

  • packages/core/src/accounts.tsonFallbackQuotaFetched option + invocation in refreshQuotaForDueAccounts.
  • packages/opencode/src/index.tslastSidebarRouting + refreshSidebarQuota; async refresh no longer clobbers activeId.

Tests

  • 3 new regression tests (verified red without the fix): active fallback not clobbered after async main refresh; background fallback refresh updates the sidebar without a request; onFallbackQuotaFetched fires on background quota change.
  • Full suite green (typecheck + test + build).

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Fixes two sidebar issues: stale fallback quota and the active account being reset to main after async refresh. The sidebar now stays accurate and keeps the selected account.

  • Bug Fixes
    • Sidebar refreshes after background fallback storage changes (quota update, token refresh, or error recording) via onFallbackStorageChanged in FallbackAccountManager, hooked to refreshSidebarQuota() in the AnthropicAuthPlugin.
    • Preserves the active account during async main quota refresh by tracking lastSidebarRouting and reusing it in refreshSidebarQuota() instead of hardcoding 'main'.

Written for commit 832e0a7. Summary will update on new commits.

Review in cubic

Greptile Summary

This PR fixes two independent bugs in the OpenCode quota sidebar: stale fallback quota after background refresh, and the active account being clobbered back to main by a fire-and-forget async callback. The PR description references onFallbackQuotaFetched but the landed option name is onFallbackStorageChanged, reflecting a rename suggested in a prior review.

  • Bug 1 (stale fallback quota): Adds onFallbackStorageChanged to FallbackAccountManager, fired once per background pass when storage changes. The plugin wires this to a new refreshSidebarQuota() helper that rewrites the sidebar without a request.
  • Bug 2 (clobbered active account): Replaces the hardcoded activeId: 'main' in the async refreshMain().then(...) callback with refreshSidebarQuota(), which reads lastSidebarRouting — a variable updated on every writeSidebarState call that carries the most recent routing decision.
  • Tests: Three regression tests cover the callback firing behavior, the background-fallback-to-sidebar path, and the async main-refresh clobber race with a delayed fetch mock.

Confidence Score: 5/5

Safe to merge; both bug fixes are logically sound and backed by regression tests confirmed red before the fix.

The routing-memory and callback wiring are minimal and well-scoped. The one subtle concern is that refreshSidebarQuota calls latestGetAuth() unconditionally — even from the fallback background-quota path — which could on token rotation cause quotaManager.getMain() to destructively clear the cached main quota. The impact is transient (null main quota for one sidebar cycle, one extra refreshMain call on the next request) and low probability.

No files require special attention.

Important Files Changed

Filename Overview
packages/core/src/accounts.ts Adds onFallbackStorageChanged callback option to FallbackAccountManager, fired once per background quota pass when any fallback storage change is persisted.
packages/opencode/src/index.ts Introduces lastSidebarRouting and refreshSidebarQuota() to fix the hardcoded activeId: 'main' clobber in the async main refresh callback.
packages/opencode/src/tests/accounts.test.ts Adds a regression test verifying onFallbackStorageChanged fires exactly once when storage changes.
packages/opencode/src/tests/index.test.ts Adds two integration regression tests for the clobber race and the background-fallback-to-sidebar path.

Reviews (2): Last reviewed commit: "refactor(core): rename onFallbackQuotaFe..." | Re-trigger Greptile

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

No issues found across 4 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.
Architecture diagram
sequenceDiagram
    participant Plugin as AnthropicAuthPlugin
    participant FallbackMgr as FallbackAccountManager
    participant QuotaMgr as QuotaManager
    participant Sidebar as Sidebar State
    participant Storage as Account Storage

    Note over Plugin,Storage: BUG 1 FIX: Stale fallback quota refresh

    Plugin->>FallbackMgr: constructor({ onFallbackQuotaFetched })
    FallbackMgr->>FallbackMgr: startBackgroundRefresh()

    loop Background quota check cycle
        FallbackMgr->>FallbackMgr: refreshQuotaForDueAccounts()
        FallbackMgr->>QuotaMgr: fetch fallback quota
        QuotaMgr-->>FallbackMgr: quota data
        alt Quota changed
            FallbackMgr->>Storage: save()
            FallbackMgr->>Plugin: NEW: onFallbackQuotaFetched()
            Plugin->>Plugin: NEW: refreshSidebarQuota()
            Plugin->>Storage: loadAccounts()
            Storage-->>Plugin: storage data
            Plugin->>Plugin: writeSidebarState(lastSidebarRouting)
            Plugin->>Sidebar: update with fresh fallback quota
        end
    end

    Note over Plugin,Storage: BUG 2 FIX: Async main refresh clobbering active account

    Plugin->>Plugin: initialize lastSidebarRouting = { activeId: 'main', route: 'main' }
    
    Plugin->>Plugin: writeSidebarState(options)
    Plugin->>Plugin: NEW: record lastSidebarRouting = { activeId, route }

    Plugin->>FallbackMgr: loader() → fetch messages
    alt Fallback serves request
        Plugin->>Plugin: writeSidebarState( activeId: 'fallback-1', route: 'fallback' )
        Plugin->>Plugin: record lastSidebarRouting = { activeId: 'fallback-1', route: 'fallback' }
        Plugin-->>Sidebar: active = fallback-1
    end

    Plugin->>QuotaMgr: fire-and-forget refreshMain(mainToken)
    QuotaMgr-->>Plugin: .then() callback
    Note over Plugin: Previously: hardcoded activeId: 'main'
    Plugin->>Plugin: NEW: refreshSidebarQuota()
    Plugin->>Plugin: read lastSidebarRouting.activeId = 'fallback-1'
    Plugin->>Plugin: writeSidebarState( activeId: 'fallback-1', route: 'fallback' )
    Plugin-->>Sidebar: maintains fallback as active (not clobbered)
Loading

Re-trigger cubic

Comment thread packages/core/src/accounts.ts Outdated
…nged

The callback fires on any persisted fallback storage change (token refresh,
quota update, or error recording), not only quota fetches. Rename per review
feedback for an accurate API name.
@ualtinok ualtinok merged commit 9999a56 into cortexkit:main Jun 4, 2026
4 checks passed
@iceteaSA iceteaSA deleted the fix/sidebar-fallback-quota branch June 4, 2026 07:56
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