Skip to content

feat(codex): reserve main account as last resort#495

Draft
Alvin0412 wants to merge 3 commits into
lidge-jun:devfrom
Alvin0412:agent/main-account-last-resort
Draft

feat(codex): reserve main account as last resort#495
Alvin0412 wants to merge 3 commits into
lidge-jun:devfrom
Alvin0412:agent/main-account-last-resort

Conversation

@Alvin0412

@Alvin0412 Alvin0412 commented Jul 26, 2026

Copy link
Copy Markdown

Summary

  • add an opt-in mainAccountLastResort pool policy, defaulting to false
  • prefer selectable added accounts for new and re-bound work, including side-effect-free routing previews
  • prevent quota rebalancing and periodic affinity re-evaluation from moving a healthy added account onto the main login
  • keep cooldown and failure failover ordered across remaining added accounts before allowing the main login
  • persist explicit manual account selection across proxy restarts while automatic routing keeps main as the final reserve
  • document the behavior in both user docs and the OpenAI account-mode source of truth

Motivation

Pool mode currently treats the main Codex login as a first-class rotation candidate. Because it is inserted ahead of added accounts and participates in lowest-usage selection, new work can move onto the main login even while an added account is still usable.

Some users want the opposite operational policy: consume added pool accounts first and preserve the main login as a final reserve.

Behavior

The policy is explicit and backward compatible:

{
  "mainAccountLastResort": true
}

When omitted or false, routing is unchanged.

When true:

  • new or re-bound work prefers selectable added accounts;
  • quota auto-switching stays among added accounts and does not move a healthy added account to main;
  • 429 cooldown and transient-failure paths try remaining added accounts before main;
  • main remains available when no added account is selectable;
  • explicit manual account selection applies immediately, survives proxy restarts, and remains until normal quota or failure routing changes it;
  • existing thread affinity remains authoritative until normal re-evaluation, expiry, or failure handling.

Direct mode, the OpenAI API-key provider, credential storage, and account-qualified model selection are unchanged.

Root cause

getEligiblePoolAccounts() always inserted the main login as the first eligible candidate, while quota selection compared it directly with added accounts. There was no policy boundary separating routine quota balancing from true last-resort failover.

This change keeps one eligible-account engine but applies two explicit rules: omit main while another added failover candidate exists, and exclude main from quota moves away from a still-healthy added account.

The rebase onto current dev also preserves its immediate manual-selection contract: the selected account id is persisted as explicit provenance across proxy restarts, then cleared when ordinary quota or failure routing changes the active account.

Security review

This touches Codex account selection at an authentication boundary and should receive the explicit maintainer security review required by MAINTAINERS.md.

The change does not alter token loading, credential persistence, API responses, account identifiers, or logging. The option is disabled by default.

Verification

  • bun test tests/codex-main-rotation.test.ts tests/codex-routing.test.ts tests/codex-auth-api.test.ts tests/config.test.ts — 245 passed, 0 failed
  • bun run typecheck — passed
  • bun run lint:gui — passed
  • cd docs-site && bun run build — 131 pages built
  • bun run privacy:scan — passed
  • full suite on this branch — 4,836 passed; 2 macOS/Bun ECONNRESET failures
  • clean origin/dev@5ff20dc reproduction — the same 2 tests fail at the same pre-assertion fetches:
    • websocket upgrade rejects hostile Origin even with a valid API token
    • OpenAI option auth matrix keeps direct, pool, and API credentials independent

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs and maintainer invariants were updated.
  • Security-sensitive behavior was reviewed for secrets, auth boundaries, and unsafe defaults.

Summary by CodeRabbit

  • New Features
    • Added mainAccountLastResort to keep work on selectable added pool accounts, using the main login only as a final fallback after added accounts are exhausted.
    • Manual Codex account selection now persists across proxy restarts, applies immediately, and is cleared automatically when routing rules change it.
  • Documentation
    • Updated English and Simplified Chinese docs/specs to describe the new fallback and selection semantics.
  • Tests
    • Expanded coverage for last-resort routing, failover exhaustion order, and manual selection persistence/clearing.

@coderabbitai

This comment was marked as resolved.

@github-actions github-actions Bot added the enhancement New feature or request label Jul 26, 2026
@Alvin0412
Alvin0412 marked this pull request as ready for review July 26, 2026 09:12
@Wibias
Wibias marked this pull request as draft July 26, 2026 16:21
@Alvin0412
Alvin0412 force-pushed the agent/main-account-last-resort branch from 89b94a6 to 670d7f4 Compare July 26, 2026 18:27
@Alvin0412
Alvin0412 marked this pull request as ready for review July 26, 2026 18:42
chatgpt-codex-connector[bot]

This comment was marked as resolved.

@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: 1

🤖 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 `@src/codex/routing.ts`:
- Line 27: Persist manual-selection provenance alongside the persisted active
account, including an explicit marker for manual selection of the main account,
instead of relying on the process-local manualAccountSelections WeakMap. Update
setActiveCodexAccount() to clear the marker when routing changes the active
account, and ensure preferAddedPoolAccount() honors the marker after
configuration reloads. Add a regression test covering save/reload after manually
selecting main with a selectable added account.
🪄 Autofix (Beta)

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: 5ee9415f-e216-453e-9f2a-e0ea341a82c2

📥 Commits

Reviewing files that changed from the base of the PR and between 89b94a6 and 670d7f4.

📒 Files selected for processing (12)
  • README.md
  • docs-site/src/content/docs/reference/configuration.md
  • docs-site/src/content/docs/zh-cn/reference/configuration.md
  • readme/README.zh-CN.md
  • src/codex/auth-api.ts
  • src/codex/routing.ts
  • src/config.ts
  • src/types.ts
  • structure/08_openai-provider-tiers.md
  • tests/codex-main-rotation.test.ts
  • tests/codex-routing.test.ts
  • tests/config.test.ts

Comment thread src/codex/routing.ts Outdated
@Alvin0412
Alvin0412 force-pushed the agent/main-account-last-resort branch from 670d7f4 to 6e3d519 Compare July 26, 2026 19:01
coderabbitai[bot]

This comment was marked as resolved.

@Wibias Wibias left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Verdict

Request changes ÔÇö do not merge until provenance/upgrade contract, docs/comment accuracy, and process gates below are fixed in this PR. Optional follow-ups are not accepted for this PR; all quality items below must ship in this PR before merge.

Summary

Opt-in mainAccountLastResort correctly prefers added pool accounts for new/re-bound work, keeps quota rebalancing off main while an added account is healthy, and only admits main after added candidates are exhausted. Manual selection provenance via manualCodexAccountSelectionId (including __main__) is the right fix for the earlier WeakMap restart gap, and EN/ZH/structure docs are largely in sync. Base branch is correctly dev.

Blocking findings

None on merge conflicts.

Merge conflict status: mergeable=MERGEABLE, mergeStateStatus=UNSTABLE. Not CONFLICTING ÔÇö no conflicted files. Branch is diverged from dev (ahead_by=3, behind_by=6); rebase onto latest dev in this PR before merge (freshness / avoid landing a stale tip). Conflicts are not currently present, but divergence is not an acceptable leave-behind.

Additional required quality changes (fix in this PR before merge)

  1. Pre-existing explicit main selection is not treated as manual under the new flag ÔÇö src/codex/routing.ts preferAddedPoolAccount (~L505ÔÇô511)

    • Failure mode: Protection is only manualCodexAccountSelectionId === active. A config that already has activeCodexAccountId: "__main__" from a prior GUI/CLI selection (no provenance field ÔÇö every install before this PR) and then enables mainAccountLastResort: true will have the next non-affinity resolveCodexAccountForThreadDetailed call vacate main via setActiveCodexAccount even though no quota/failure event occurred. That contradicts the PR/docs contract that explicit manual selection ÔÇ£survives proxy restartsÔÇØ / ÔÇ£remains until normal quota or failure routing changes it.ÔÇØ
    • Why this is not free to grandfather-naively: the recovery test correctly relies on active === __main__ without a pin meaning ÔÇ£automatic last-resort occupancy,ÔÇØ which should yield to a recovered added account. Blindly treating every MAIN active as manual would break that.
    • Fix (pick one, implement + test):
      • One-shot config migration when the provenance field is introduced (versioned / first-seen marker) that copies a pre-existing activeCodexAccountId into manualCodexAccountSelectionId exactly once; or
      • Document the upgrade caveat in EN + ZH config docs / README and structure SoT: enabling the flag does not inherit a pre-flag __main__ active id as a pin ÔÇö re-select main via dashboard/CLI. Add a focused regression or explicit negative test so the contract stays honest.
    • Current docs claim the stronger behavior without the caveat (docs-site/.../configuration.md mainAccountLastResort / manualCodexAccountSelectionId rows; structure/08_openai-provider-tiers.md).
  2. Stale activeCodexAccountId type comment ÔÇö src/types.ts (~L643ÔÇô644)

    • Still says undefined = main (passthrough as-is). Under mainAccountLastResort, cleared/undefined active means automatic no-pin selection (often an added account), not ÔÇ£use main.ÔÇØ Update the comment to match structure/08 and the API null/__main__ contract so future callers do not reintroduce Option-A assumptions.
  3. Deduplicate main-exclusion predicates ÔÇö mild duplication between main-exclusion in getEligiblePoolAccounts vs getQuotaAutoSwitchCandidates. Consolidate or share the predicate in this PR so the last-resort / quota paths cannot drift; different call sites may keep distinct filters, but the shared ÔÇ£exclude main under last-resortÔÇØ rule must live in one place with coverage that both paths exercise.

  4. Rebase onto latest dev ÔÇö tip is 6 commits behind dev. Rebase (or merge dev) in this PR and re-resolve any new conflicts before merge; do not leave divergence for a follow-up.

  5. Re-run / land green required CI on the rebased tip ÔÇö Cross-platform CI and React Doctor are currently action_required (fork PR ÔÇö maintainer must approve workflow runs) and have not produced a green required suite on HEAD 7e0351d. After rebase, ensure required CI is approved and green on the mergeable SHA.

Note (already addressed on current HEAD ÔÇö no further code change required beyond verification after rebase): prior CodeRabbit/Codex threads on WeakMap persistence and __main__ vs null were addressed (6e3d519, 7e0351d) and look correct.

Optional / non-blocking

None. Do not defer quality work to a follow-up PR; ship the items above in this PR.

Standards / Spec / Security

Standards

  • Branch targeting: dev ÔÇö OK (AGENTS.md / MAINTAINERS.md).
  • Bun-native / privacy: No Node-only runtime additions; no new token/account-identifier logging ÔÇö OK.
  • Docs sync: EN + zh-CN configuration + README/zh README + structure/08 updated together ÔÇö OK aside from the upgrade-caveat gap above.
  • Tests: Solid focused coverage in tests/codex-main-rotation.test.ts, tests/codex-auth-api.test.ts, tests/config.test.ts for last-resort ordering, preview, affinity, save/reload pin, and __main__ vs null API ÔÇö OK.
  • Security boundary (process): Touches Codex account selection / auth-api active PUT and persisted selection provenance. Per MAINTAINERS.md / AGENTS.md, explicit maintainer security review is required before merge. Diff does not appear to alter credential storage, token loading, or secret logging; default remains false.

Spec (PR body as spec)

  • Claims 1ÔÇô4, 6ÔÇô8 (prefer added, quota stays on added, failover order, main as final fallback, affinity authoritative, default-off, Direct/API-key untouched): implemented correctly on review of preferAddedPoolAccount, getEligiblePoolAccounts, getQuotaAutoSwitchCandidates, affinity early-return before prefer, and non-mutating previewCodexAccountForRequest.
  • Claim 5 (manual selection survives / remains until quota/failure): partial ÔÇö holds for selections made through the updated PUT /api/codex-auth/active path; incomplete for pre-provenance configs (finding #1).
  • Affinity / preview / pin-clearing / caller signature probes: no additional defects found. setActiveCodexAccount clears the pin; delete clears a matching pin; only callers of resetCodexRoutingForManualSelection appear updated.

CI / process gates

Gate Status
Merge conflicts MERGEABLE / not CONFLICTING
mergeStateStatus UNSTABLE
Cross-platform CI action_required (fork PR ÔÇö maintainer must approve workflow runs); has not produced a green required suite on HEAD 7e0351d
React Doctor action_required (same)
enforce-target / label / CodeRabbit green
Divergence 3 ahead / 6 behind dev ÔÇö rebase required in this PR
Maintainer approval + security review still required (MAINTAINERS.md)

Do not merge until: (1) all Additional required quality changes above are addressed in this PR (no follow-up PRs), (2) required CI is approved and green on current HEAD, (3) maintainer security review is recorded.

After fixes

Once all blocking + required quality changes above are addressed (and conflicts/rebase done), mark this PR Ready for review (leave draft only until then). Then ping for re-review / CI.

@Wibias
Wibias marked this pull request as draft July 26, 2026 20:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants