fix(opencode): stale fallback quota and clobbered active account in sidebar#57
Merged
Merged
Conversation
This was referenced Jun 4, 2026
There was a problem hiding this comment.
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)
…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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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-quotawas run manually. Adds anonFallbackQuotaFetchedcallback toFallbackAccountManager, 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 hardcodedactiveId: 'main'and fired asynchronously after routing was decided, clobbering the active fallback back tomain(e.g. in fallback-first the served fallback flipped back to main). Adds alastSidebarRoutingmemory recorded inwriteSidebarState, and routes the async callback through a newrefreshSidebarQuota()that reuses the last routing instead of hardcoding main.Changes
packages/core/src/accounts.ts—onFallbackQuotaFetchedoption + invocation inrefreshQuotaForDueAccounts.packages/opencode/src/index.ts—lastSidebarRouting+refreshSidebarQuota; async refresh no longer clobbersactiveId.Tests
onFallbackQuotaFetchedfires on background quota change.Need help on this PR? Tag
/codesmithwith 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.
onFallbackStorageChangedinFallbackAccountManager, hooked torefreshSidebarQuota()in theAnthropicAuthPlugin.lastSidebarRoutingand reusing it inrefreshSidebarQuota()instead of hardcoding'main'.Written for commit 832e0a7. Summary will update on new commits.
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
mainby a fire-and-forget async callback. The PR description referencesonFallbackQuotaFetchedbut the landed option name isonFallbackStorageChanged, reflecting a rename suggested in a prior review.onFallbackStorageChangedtoFallbackAccountManager, fired once per background pass when storage changes. The plugin wires this to a newrefreshSidebarQuota()helper that rewrites the sidebar without a request.activeId: 'main'in the asyncrefreshMain().then(...)callback withrefreshSidebarQuota(), which readslastSidebarRouting— a variable updated on everywriteSidebarStatecall that carries the most recent routing decision.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
refreshSidebarQuotacallslatestGetAuth()unconditionally — even from the fallback background-quota path — which could on token rotation causequotaManager.getMain()to destructively clear the cached main quota. The impact is transient (null main quota for one sidebar cycle, one extrarefreshMaincall on the next request) and low probability.No files require special attention.
Important Files Changed
onFallbackStorageChangedcallback option toFallbackAccountManager, fired once per background quota pass when any fallback storage change is persisted.lastSidebarRoutingandrefreshSidebarQuota()to fix the hardcodedactiveId: 'main'clobber in the async main refresh callback.onFallbackStorageChangedfires exactly once when storage changes.Reviews (2): Last reviewed commit: "refactor(core): rename onFallbackQuotaFe..." | Re-trigger Greptile