Skip to content

feat(codex): add per-account auto-switch thresholds - #4020

Draft
alexalok wants to merge 8 commits into
lidge-jun:devfrom
alexalok:alex/account-auto-switch-main
Draft

feat(codex): add per-account auto-switch thresholds#4020
alexalok wants to merge 8 commits into
lidge-jun:devfrom
alexalok:alex/account-auto-switch-main

Conversation

@alexalok

@alexalok alexalok commented Sep 8, 2026

Copy link
Copy Markdown

Summary

  • Add an optional usage-based switching threshold per Codex account, including the main account. Missing or cleared overrides inherit the global threshold; 0 disables proactive usage switching only for that account.
  • Apply the effective account threshold consistently to new-task routing, bound-task re-evaluation, subagent fallback, and CLI pin guidance. Remove account-owned threshold state when an account is deleted while preserving concurrent config edits.
  • Add a Custom account threshold account-card control. Inherited accounts show only the disabled toggle; the percentage appears after enablement, and native duplicate number-input arrows are hidden.
  • Document the config and management API contract across maintained locales, with regression coverage for routing, persistence, API validation, deletion, CLI output, and dashboard interaction races.
  • Merge dev through f94dd88f12a1a9aeb355aa9b2d7166ef5b002ac9, retaining the fix(codex): restore main policy binding after owned startup #4085 startup-policy binding fence. Add threshold-zero safety regressions and fix caller-owned main cooldown and failed-save/deletion rollback gaps found during follow-up review.

Screenshot

Inherited and custom account threshold states

Verification

Validated head: 42d8b74601fde4c8fba1c7c8c43a534d6e0f30e9, September 10, 2026. Runtime: repository-local Bun 1.4.2 (744846f84), macOS arm64. The tracked working tree was clean and this exact commit was pushed without rebasing or amending the reviewed history.

Check Result
bun run typecheck Pass on the exact head.
Full root bun run test --parallel=1 22,577 pass / 40 skip / 0 fail, 593,187 assertions across 1,162 files; wrapper exit 0 in 762 seconds. All seven lanes completed, and the executed-file inventory matched the repository inventory with no missing or extra paths.
Default bun run test (four workers) Failed, unresolved: two attempts hit a Bun worker SIGSEGV in tests/routing/routing-policy-surface-parity.test.ts. The affected file passes alone and in the completed single-worker full run. This is not a claim that the default parallel crash is fixed or that remote CI is green.
Dashboard suite, unchanged dashboard tree 1,960 pass / 0 fail.
Dashboard lint, translation lint, and production build Pass; the build retains its existing large-chunk warning.
Docs build Pass, 425 pages.
bun run privacy:scan Pass on the exact head.
bun run skill:surface:check Pass.

The supported --parallel=1 option retains the full wrapper inventory, all six serial lanes, --isolate, preload, isolated homes, user lock, and full-suite timing mode. No test exclusions, relaxed assertions, disabled safety checks, or dependency-policy changes were introduced to obtain this result. The default-worker crash has not been bisected against dev; its root cause remains unproven.

Threshold-zero regressions retain these separate boundaries:

  • Main hard-lock still rejects or detours at 99%, while the explicit zero override prevents proactive switching below that boundary. Caller-owned and final-header paths are covered.
  • Fresh-process startup tests retain the fix(codex): restore main policy binding after owned startup #4085 pending-binding fence for caller fallback and main pins while allowing an eligible stored-account alternative.
  • Main and stored-account cooldowns still apply, including Retry-After, independent native model scopes, no usable fallback, and a cooldown arriving during awaited entitlement lookup. Unrelated caller credentials and explicit Direct retain their existing policy.
  • Model entitlement still rejects exact ineligible accounts or chooses an eligible detour without spending the ordinary pin/affinity.
  • Rejected threshold saves and failed account deletion restore pending deletion intent, preserving earlier accepted resets and concurrent disk edits. Threshold DTOs retain upstream email masking.

These safety regressions were also driven red with isolated mutations that bypassed hard-lock, startup binding, cooldown, entitlement, or the explicit zero override, then passed after restoration. All changes are retained in the pushed tests.

This PR remains draft. Local validation and follow-up code review do not replace maintainer security review, sponsorship, the broader account/UI product decision, or required remote CI. The default parallel-runtime failure remains explicitly recorded above.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • All CI tests are green on my local testing.
  • I pushed my PR to the latest dev commit.
  • I resolved all correct Codex and CodeRabbit findings.
  • My PR is ready for review.

@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions github-actions Bot added the intake: hygiene-blocked Deterministic PR hygiene checks failed label Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ Deterministic hygiene checks failed.

  • new_suppression — A new TypeScript, lint, formatter, or similar suppression was added. Fix the underlying issue or obtain suppression-approved. Paths: gui/src/components/AccountAutoSwitchControl.tsx.
  • unsponsored_surface — This changes an authentication, workflow, release-automation, or dependency surface. MAINTAINERS.md requires security review for these; ask a maintainer to apply maintainer-sponsored once they have reviewed it. Paths: src/codex/auth-api.ts, src/codex/auth-context.ts.

@github-actions github-actions Bot added the enhancement New feature or request label Sep 8, 2026
@github-actions github-actions Bot changed the title feat(codex): add per-account auto-switch thresholds [WRONG BRANCH] feat(codex): add per-account auto-switch thresholds Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • hygiene: unsponsored_surface.

What to do

  • Fix unsponsored_surface — This changes an authentication, workflow, release-automation, or dependency surface. MAINTAINERS.md requires security review for these; ask a maintainer to apply maintainer-sponsored once they have reviewed it. Paths: src/codex/auth-api.ts, src/codex/auth-context.ts.
  • Tick all four boxes in the PR description once you're done (currently 0/4).

Review readiness checklist

  • ⬜ All CI tests are green on my local testing.
  • ⬜ I pushed my PR to the latest dev commit.
  • ⬜ I resolved all correct Codex and CodeRabbit findings.
  • ⬜ My PR is ready for review.

0/4 boxes ticked.

This pull request was already a draft. Its draft status will be preserved after every issue above is resolved.
@alexalok Tick the boxes once your local CI is green, your branch is on the latest dev commit, and every correct Codex and CodeRabbit finding is resolved.

@alexalok
alexalok force-pushed the alex/account-auto-switch-main branch from e7923da to c9bdeb4 Compare September 8, 2026 10:41
@alexalok
alexalok changed the base branch from main to dev September 8, 2026 10:42
@github-actions github-actions Bot changed the title [WRONG BRANCH] feat(codex): add per-account auto-switch thresholds feat(codex): add per-account auto-switch thresholds Sep 8, 2026
@alexalok
alexalok force-pushed the alex/account-auto-switch-main branch from c9bdeb4 to fece6dd Compare September 8, 2026 10:47
@lidge-jun

Copy link
Copy Markdown
Owner

리뷰 · 우선순위 56 / 80

이 PR은 Codex 계정마다 사용량 기반 auto-switch 임계값을 따로 두는 기능이다. 전역 autoSwitchThreshold는 그대로 두고, codexAccountAutoSwitchThresholds 맵으로 계정(및 __main__) 덮어쓰기를 추가한다. 값이 없으면 전역 상속, 0이면 그 계정에서 시작하는 사용량 전환만 끈다. 새 작업 라우팅, bound-task 재평가, subagent fallback, CLI pin 안내, 계정 삭제 시 상태 정리, Codex Auth 카드 UI, 다국어 docs/management API까지 한 덩어리다. 지금 dev HEAD는 29bb221c3(2.49.0, tip #4021). 제품 tip은 #4002 auth/routing 축이고, 풀 임계값은 아직 전역 한 값(src/codex/routing.tsconfig.autoSwitchThreshold ?? 80)이다. 멀티 계정 풀에서 Plus/Pro/메인 드레인 속도를 다르게 가져가려는 요구와는 맞지만, 표면이 커서 점수만 보면 “가치 있음 / 지금 그대로 merge는 아님” 구간에 둔다.

구조는 비교적 깔끔하다. 새 src/codex/account-auto-switch.ts가 parse/effective/set/forget을 모으고, routing·subagent·auth-api·lifecycle이 getEffectiveCodexAutoSwitchThreshold로 갈아탄다. API는 기존 PUT /api/codex-auth/auto-switch{ id, threshold } / null 상속 복원을 확장한다. config 쪽은 priorities와 같은 “잘못된 손편집은 해당 기능만 끄고 providers/accounts는 보존” 패턴의 zod+warning이다. 특히 deleteConfigObjectChildKey / prepareConfigObjectChildDeletionRebase / applyConfigObjectChildDeletions로 계정 한 칸 삭제가 형제 키를 tombstone하지 않게 한 것은, 동시 설정 편집이 많은 이 저장소에서 필요한 기반이다. GUI는 AccountAutoSwitchControl로 토글+퍼센트, hook에 mutation gate를 새로 둔다. 초점 테스트·대시보드 테스트 숫자는 본문에 크게 적혀 있다.

막히는 지점도 분명하다. draft=true, intake: hygiene-blocked, hygiene/enforce-target 실패, readiness 체크리스트에 CI/ready 칸이 비어 있고, 본문도 full suite는 이 헤드에서 못 돌렸다고 적는다. src/types/config.tssrc/config.ts를 동시에 건드린다. 대분할 캠페인 때문에 “리베이스하지 말고 닫기” 대상은 아니지만, 분할 PR과 겹치면 충돌 비용이 크다. UX로 토글을 켜면 globalThreshold를 명시 override로 심는데, 그 순간부터 전역 값을 바꿔도 이 계정은 따라오지 않는다. 의도에 가깝지만 운영자에게 “상속 중”과 “전역과 같은 숫자로 고정”이 같아 보일 수 있다. 또한 이 기능은 #3994 같은 전환 실패 버그를 직접 고치지는 않는다. 임계값 세분화일 뿐 failover/incomplete 복구와는 축이 다르다.

라인 src/codex/routing.ts getEffectiveCodexAutoSwitchThreshold 교체 지점들 - headroom·quota autoswitch·affinity preview/reeval·unbound preview가 모두 source account 기준으로 바뀐다. 전역만 보던 호출이 남았는지 한 번 더 검색해라.
라인 src/codex/account-auto-switch.ts / src/config/rebase-provenance.ts child deletion - 계정 override 삭제가 형제 키를 지우지 않게 한 기반은 좋다. 실패 save 후 WeakMap tombstone 주석도 의도가 분명하다. 여기 회귀가 나면 설정 전체가 아플 수 있으니 테스트를 유지해라.
경로 gui AccountAutoSwitchControl 토글 on - onChange(globalThreshold)로 명시 override를 심는다. 전역과 같은 숫자여도 이후 전역 변경을 따라가지 않는다. UI에 “custom” 상태를 더 드러낼지 판단이 필요하다.
경로 PR draft / hygiene-blocked / checklist - draft, hygiene·enforce-target 실패, full suite 미완, ready 칸 미체크. 이 상태로는 merge하지 마라.
경로 src/types/config.ts codexAccountAutoSwitchThresholds - 필드 추가는 대분할 무효화 대상은 아니다. 다만 types/config 분할 열차와 겹치면 충돌 나니, 분할 PR보다 먼저 넣을지 순서를 정해라.

메인테이너의 판단이 필요한 지점

너의 추천
지금 헤드로는 merge하지 마라. (1) hygiene/enforce-target·체크리스트를 초록으로 만들고, (2) full 또는 최소 routing+config+gui 회귀를 이 헤드에서 다시 돌리고, (3) 토글 on 의미(고정 vs 상속)를 카드/문구로 명확히 한 뒤 ready로 올려라. 방향·테스트·삭제 atomicity는 좋아서 닫을 중복이 아니다. types/config 분할 때문에 버리기보다, 분할 열차와 안 겹치게 순서만 잡아라.

이 댓글은 grok-bot이 작성했습니다

@alexalok

alexalok commented Sep 8, 2026

Copy link
Copy Markdown
Author

@lidge-jun @Ingwannu — follow-up on fece6ddda:

  • Re-audited Codex threshold reads. New-task routing, bound-task re-evaluation, unbound and affinity preview, subagent fallback, and main-account pin guidance all resolve through getEffectiveCodexAutoSwitchThreshold. Remaining direct global read in src/codex/auth-api.ts exposes the global setting in management API state; generic OAuth and Anthropic reads are separate.
  • In the dashboard, inherited state is toggle-only. Enabling it writes a fixed account override initialized from the current global value; the percentage then appears as Custom account threshold. Later global changes do not modify that override. Screenshot is prepared in the PR description.
  • Current-head verification: typecheck; focused routing/config/auth/CLI tests 1,027 pass / 1 intentional skip; GUI 1,939 pass; lint/i18n/build/docs/privacy/hygiene unit tests pass. Full root suite has not completed, so the PR remains draft.

Could one of you review src/codex/auth-api.ts and src/codex/auth-context.ts, then apply maintainer-sponsored if satisfied?

@Ingwannu

Ingwannu commented Sep 9, 2026

Copy link
Copy Markdown
Owner

Followed up on fece6dd and the two requested source files. The account API distinguishes a missing id (existing global update) from a supplied account id, validates null/integer 0-100, rejects unknown stored accounts, and uses the current runtime config. The main-pin path uses the effective main-account threshold. I also confirmed the shared resolver is used at the current routing and subagent threshold-read sites; the global value exposed in management state is not itself a missed routing decision.

This is not a full approval of the larger account/UI change. Full-root validation is still explicitly incomplete in the description, and dev now includes the #4085 startup-policy change touching the same auth-context area. Please bring this head forward without dropping that fence, finish exact-head full validation, and retain tests proving that threshold 0 disables only proactive switching, not hard-lock/cooldown/entitlement enforcement. @lidge-jun The per-account UI/product decision and sponsorship remain yours; I have not applied a sponsorship label or waived readiness based only on these two files.

@alexalok

Copy link
Copy Markdown
Author

Follow-up to the September 9 review in #4020 (comment), now on 42d8b74601fde4c8fba1c7c8c43a534d6e0f30e9:

  • Merged dev through f94dd88f12a1a9aeb355aa9b2d7166ef5b002ac9 without replacing the reviewed history. The fix(codex): restore main policy binding after owned startup #4085 startup-policy binding fence remains intact, with fresh-process per-account-zero regression coverage.
  • Retained and expanded tests proving 0 disables proactive switching only, not main hard-lock, applicable cooldowns, or model entitlement. Isolated mutation checks confirmed those tests fail when the protections are bypassed. Follow-up review also found and fixed caller-owned main cooldown and failed-save/deletion rollback gaps.
  • Exact-head full-root validation completed with bun run test --parallel=1: 22,577 pass / 40 skip / 0 fail across all 1,162 files, all seven lanes and wrapper exit 0. Typecheck and privacy scan passed; dashboard tests (1,960 pass), lint/i18n/build, docs build, and skill-surface checks also passed on the unchanged corresponding trees.
  • Default four-worker bun run test still fails with a repeated Bun SIGSEGV in routing-policy-surface-parity.test.ts. The single-worker command preserves the complete inventory and isolation safeguards; the default crash is not fixed or waived, and remote CI is not claimed green.

The description now records the exact head and both full-run outcomes separately. Stale readiness attestations have been cleared, and the PR stays draft. This is a response to the requested implementation/validation follow-up, not full approval of the account/UI change or a substitute for maintainer security review and sponsorship.

@Ingwannu

Copy link
Copy Markdown
Owner

Thanks for keeping the two full-run outcomes separate. A complete single-worker inventory is useful evidence, and it should not be described as “only focused tests”; the default four-worker SIGSEGV is still a separate unresolved execution result.

I am not granting a CI exception or approval from that summary. The current 42d8b74 head also contains the newly reported cooldown and save/deletion rollback fixes, which need their own source-bound review in addition to the earlier threshold-zero controls. Keep the Draft state and attach exact-head hosted CI when available; there is no need to rerun the already-passing unchanged single-worker suite merely to repeat the count. Final account/UI acceptance remains separate.

@alexalok

Copy link
Copy Markdown
Author

Follow-up to #4020 (comment):

Draft remains in place; no CI exception, maintainer approval, sponsorship, or account/UI acceptance is inferred. The complete local single-worker inventory and the unresolved default four-worker crash remain separate results. I have not rerun the unchanged passing suite or added another code commit.

For the separate source-bound review of the new fixes, these links are pinned to 42d8b74601fde4c8fba1c7c8c43a534d6e0f30e9:

  1. Caller-owned main cooldown: auth-context guard and rechecks. The guard is Pool-only and requires a memory-only match to the observed main credential. It uses the requested model's quota scope, rejects cooled fallback, and rechecks after awaited caller entitlement. The startup-binding fence remains before pin admission. Regressions cover matching and unrelated callers, unavailable fallback, cooldown arriving during entitlement, independent scopes, and unchanged explicit Direct behavior.

  2. Rejected threshold-save rollback: endpoint copy-on-write and synchronous rollback, using the descriptor/deletion-provenance snapshot. A failed save restores the previous map and both pending deletion collections before yielding. Six regressions cover real SQLite lock contention, replacement/reset cases, post-preparation failure, prior reset intent, concurrent disk edits, and a later unrelated save.

  3. Failed account-deletion rollback: capture and restore. The existing config value rollback now also restores WeakMap-backed deletion intent; a rejected deletion must not erase a later override for an account that previously inherited. Four table cases cover absent/existing overrides, earlier unrelated resets, concurrent disk additions, and subsequent persistence while retaining the account and credential.

These are source/regression anchors for maintainer review, not a claim that the earlier two-file review approved these additions.

Exact-head hosted CI status checked on September 10:

Both test workflows point to the exact head above, but neither provides a passing hosted test result yet. Could a maintainer review the three changes and take the required action on the fork workflow runs? No workflow permissions or sponsorship labels have been changed, and required hosted CI and final account/UI acceptance remain outstanding.

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

Labels

enhancement New feature or request intake: hygiene-blocked Deterministic PR hygiene checks failed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants