Skip to content

[bug] why-selected ignores account policies: a paused account is reported as the winning candidate #654

Description

@choa712

Summary

codex-multi-auth why-selected recomputes candidate scores without applying the account-policy layer that the runtime rotation proxy actually uses (blockedAccountIndexes / scoreBoostByAccount from lib/policy/runtime-policy.ts). As a result it can report a paused account as the winning candidate — while the runtime proxy correctly refuses to route to it. During incident response this diagnostic actively misleads: we spent time believing the router was selecting a revoked account when it was not.

Reproduction

  1. Have 3 accounts registered. Pause one of them: codex-multi-auth account pause 2
  2. Confirm the policy is stored: codex-multi-auth account policy list --json (shows "paused": true)
  3. Run codex-multi-auth why-selected --last
  4. Send a real request through the runtime rotation proxy and check ~/.codex/multi-auth/app-bind/runtime-rotation-app-bind-status.jsonlastAccountLabel

Expected Behavior

why-selected mirrors the runtime selection inputs: paused/drained accounts are excluded (or at least rendered as blocked (policy: paused)), and per-account policy weight boosts ((weight - 1) * 2) are included in the displayed scores, so the diagnostic agrees with what the proxy does.

Actual Behavior

The paused account is counted in "Available: 2 of 3 account(s)" and printed as Selected: with health=100, top score. Policy weight boosts are absent from the scores (an account with policy weight 10 shows no boost). Meanwhile the actual runtime proxy correctly serves requests from a different, non-paused account (lastAccountLabel confirms).

Sanitized output on 2.8.1, with account B paused (its refresh token is also revoked upstream):

why-selected: Last selection (live recomputation; no persistent tracker)
Available: 2 of 3 account(s)

Selected: account 2 <account-B>
  score: 463.98
  health: 100.0
  ...
Candidates (sorted by score desc):
  * 2 <account-B>: score=463.98 health=100 ...   <- paused via account policy
  x 3 <account-C>: ... (unavailable (rate-limited, cooling down, or circuit open))
    1 <account-A>: score=450.20 ...              <- policy weight 10, no boost shown

Code pointers: lib/codex-manager/commands/why-selected.ts builds candidates from the rotation scorer only; it never calls loadRuntimePolicyState / evaluateRuntimePolicy, whereas the proxy path (lib/runtime/rotation-account-selection.ts) checks policy.blockedAccountIndexes and feeds scoreBoostByAccount into the hybrid selector.

Environment

  • codex --version: codex-cli 0.146.0
  • codex-multi-auth status output summary: 3 accounts, storage healthy, runtime rotation app-bind in use
  • codex-multi-auth rotation status output summary: proxy enabled, app bind running
  • npm ls -g codex-multi-auth: 2.8.1 (installed from a checkout of the 2.8.1 release commit; not fork-modified)
  • OS: macOS (Apple Silicon)
  • Node.js: 26.4.0

Diagnostic Outputs

Covered above (sanitized why-selected + account policy list). Happy to attach more on request.

Compliance Confirmation

  • I am using this project for personal development workflows.
  • This report does not request policy bypasses or prohibited usage.
  • I removed any secrets/tokens from this report.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions