Skip to content

[WRONG BRANCH] fix(gui): bound optimistic active-account reconciliation - #206

Draft
luvs01 wants to merge 1 commit into
mainfrom
codex/propose-fix-for-gui-active-account-bug
Draft

[WRONG BRANCH] fix(gui): bound optimistic active-account reconciliation#206
luvs01 wants to merge 1 commit into
mainfrom
codex/propose-fix-for-gui-active-account-bug

Conversation

@luvs01

@luvs01 luvs01 commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Motivation

  • Prevent a pending optimistic active-account marker from indefinitely suppressing legitimate server-side /api/codex-auth/active responses such as quota auto-switch or failover, which can leave the UI pinned to a stale active id.

Description

  • Replace the unbounded pendingActiveIdRef with a bounded marker that carries staleReadsRemaining so at most one mismatched read is absorbed before the controller accepts a newer server value.
  • Apply the bounded marker when optimistically setting active id from switchAccount, setAccountPaused, and pauseExhaustedAccounts so background reconciliation cannot permanently ignore real server changes.
  • Update the /active read logic to decrement staleReadsRemaining on a mismatch and to accept the server id once the allowance is exhausted.
  • Add a focused runtime test a post-switch read accepts a newer server-side active account in gui/tests/codex-account-pool-behaviour.test.tsx that verifies one absorbed mismatch followed by convergence to a genuine server-selected account.

Testing

  • Ran the focused GUI test: cd gui && bun test tests/codex-account-pool-behaviour.test.tsx, and the modified test passed.
  • Ran GUI checks: cd gui && bun run lint and cd gui && bun run build, and both completed (build emitted the existing Vite chunk-size advisory only).
  • Ran typecheck: bun run typecheck, which completed.
  • Ran the broader test runs (bun run test / cd gui && bun test tests) which executed hundreds of tests; the GUI/controller tests covering this change passed, while the full repository run encountered unrelated, environment-dependent failures (network/proxy/timeouts and platform zlib differences) not caused by this change.

Codex Task

Summary by CodeRabbit

  • Bug Fixes

    • Improved account switching and pausing reliability when status updates arrive slightly out of order.
    • Prevented temporary stale account data from incorrectly overriding recent changes.
    • Ensured newer server-side account information is applied correctly after an account switch.
  • Tests

    • Added coverage for updating the active account after a delayed status response.

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Aug 9, 2026
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The account pool now tolerates one stale /active response after account switches and pause mutations. Later mismatches reconcile normally. A behavioral test verifies that a subsequent server-side account update replaces the optimistic active account.

Changes

Active account reconciliation

Layer / File(s) Summary
Pending reconciliation state and response handling
gui/src/hooks/useCodexAccountPool.ts
The pending active-account state tracks one remaining stale read. The first mismatching /active response preserves the accepted ID; later mismatches update the active account and clear the pending state.
Mutation integration and behavioral validation
gui/src/hooks/useCodexAccountPool.ts, gui/tests/codex-account-pool-behaviour.test.tsx
Successful account switches and single-account or bulk pauses allow one stale /active response. The test verifies that a later response with account a3 replaces the accepted account a2.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: lidge-jun, wibias

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: bounding optimistic active-account reconciliation in the GUI.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/propose-fix-for-gui-active-account-bug

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

⏳ DRAFT

  • wrong target branch (main); retarget to dev. UI screenshot required.

What to do

  • Retarget this PR to dev — all contributions go to dev.
  • Add a screenshot of the UI change to the PR description.

Its title has been prefixed with [WRONG BRANCH].
This pull request was already a draft. Its draft status will be preserved after every issue above is resolved.

@github-actions github-actions Bot changed the title fix(gui): bound optimistic active-account reconciliation [WRONG BRANCH] fix(gui): bound optimistic active-account reconciliation Aug 9, 2026
@github-actions
github-actions Bot marked this pull request as draft August 9, 2026 06:30

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@gui/src/hooks/useCodexAccountPool.ts`:
- Line 136: Serialize all active-account mutations in useCodexAccountPool
through one shared gate or monotonic mutation revision, covering switch, pause,
bulk-pause, and setAccountPriority. Ensure each management API response updates
activeId and pendingActiveIdRef only when it is still the latest mutation,
preventing late responses from overwriting newer state. Add reverse-order
coverage for switch/pause and pause/priority responses.
- Around line 219-222: Preserve the optimistic active ID in lastGoodByBase when
the pending stale read is intentionally accepted, rather than retaining
prior.activeId when nextActiveId is undefined. Update the cache-write logic
around the pendingActiveIdRef/staleReadsRemaining flow to record the pending ID
after a successful mutation, while remaining consistent with management API
responses. Add a remount test covering this interval and verifying the new
surface retains the optimistic selection.

In `@gui/tests/codex-account-pool-behaviour.test.tsx`:
- Around line 485-488: Replace the fixed 30 ms timeout after switchAccount in
the account-pool test with deterministic synchronization: wait for the expected
post-switch GET request or resolve its deferred mock response before asserting.
Ensure the synchronization specifically proves the background load started by
switchAccount completed, rather than allowing the explicit load to consume the
request allowance.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8b777215-70b9-493d-870c-ce490f4c58f3

📥 Commits

Reviewing files that changed from the base of the PR and between 121f1ad and eff710b.

📒 Files selected for processing (2)
  • gui/src/hooks/useCodexAccountPool.ts
  • gui/tests/codex-account-pool-behaviour.test.tsx

// Set by switchAccount so a background load already in flight cannot roll the active
// id back to a value the server had not yet committed when that request was issued.
const pendingActiveIdRef = useRef<{ id: string | null } | null>(null);
const pendingActiveIdRef = useRef<{ id: string | null; staleReadsRemaining: number } | null>(null);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Sequence all active-account mutations before updating the shared marker.

pendingActiveIdRef is shared by switch, pause, and bulk-pause operations, but these handlers can overlap. A late pause response can overwrite a newer switch result at Lines [394-397] or [480-483]. Conversely, setAccountPriority can clear a newer pause marker at Line [448]. loadGenerationRef orders refresh reads only. It does not order these PUT responses. The next read can therefore consume or accept the wrong active account.

Use one shared active-mutation gate or queue, or attach a monotonic mutation revision and apply activeId and the marker only for the current revision. Add reverse-order tests for switch/pause and pause/priority responses.

As per path instructions, gui/** state changes must stay consistent with management API responses.

Also applies to: 338-341, 394-397, 480-483

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gui/src/hooks/useCodexAccountPool.ts` at line 136, Serialize all
active-account mutations in useCodexAccountPool through one shared gate or
monotonic mutation revision, covering switch, pause, bulk-pause, and
setAccountPriority. Ensure each management API response updates activeId and
pendingActiveIdRef only when it is still the latest mutation, preventing late
responses from overwriting newer state. Add reverse-order coverage for
switch/pause and pause/priority responses.

Source: Path instructions

Comment on lines +219 to +222
if (pending && pending.staleReadsRemaining > 0 && serverActiveId !== pending.id) {
// Allow one eventually-consistent response to preserve the accepted value,
// but ensure a repeated mismatch can reconcile legitimate routing changes.
pending.staleReadsRemaining -= 1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve the optimistic active ID in lastGoodByBase.

When Line [219] consumes the stale read, nextActiveId remains undefined. The cache write at Lines [251-253] then keeps prior?.activeId, which is the pre-mutation value. If another surface mounts before the next /active read, Lines [108-110] seed it with that old value, while its new pendingActiveIdRef has no allowance. The new surface can accept the same stale response and lose the optimistic selection.

Update lastGoodByBase when the mutation succeeds, or use the pending ID when recording a load that intentionally preserves the optimistic state. Add a remount test for this interval.

As per path instructions, gui/** state changes must stay consistent with management API responses.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gui/src/hooks/useCodexAccountPool.ts` around lines 219 - 222, Preserve the
optimistic active ID in lastGoodByBase when the pending stale read is
intentionally accepted, rather than retaining prior.activeId when nextActiveId
is undefined. Update the cache-write logic around the
pendingActiveIdRef/staleReadsRemaining flow to record the pending ID after a
successful mutation, while remaining consistent with management API responses.
Add a remount test covering this interval and verifying the new surface retains
the optimistic selection.

Source: Path instructions

Comment on lines +485 to +488
await act(async () => {
expect(await seen.current!.switchAccount("a2")).toEqual({ ok: true, activeId: "a2" });
await new Promise((resolve) => setTimeout(resolve, 30));
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Replace the fixed delay with deterministic synchronization.

switchAccount starts void load() at gui/src/hooks/useCodexAccountPool.ts Line [349] and returns before that load completes. The 30 ms sleep at Lines [485-487] does not prove that the first post-switch GET ran. A slow test run can execute the assertion before the background read, or let the explicit load() become the read that consumes the allowance.

Resolve a deferred mock response or wait for the expected GET count before asserting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gui/tests/codex-account-pool-behaviour.test.tsx` around lines 485 - 488,
Replace the fixed 30 ms timeout after switchAccount in the account-pool test
with deterministic synchronization: wait for the expected post-switch GET
request or resolve its deferred mock response before asserting. Ensure the
synchronization specifically proves the background load started by switchAccount
completed, rather than allowing the explicit load to consume the request
allowance.

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

Labels

aardvark bug Something isn't working codex

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant