Skip to content

fix(codex): persist terminal pool reauth causes and surface stored verdicts - #5222

Merged
lidge-jun merged 2 commits into
lidge-jun:devfrom
luvs01:fix/pool-reauth-cause-followup
Sep 19, 2026
Merged

lidge-jun merged 2 commits into
lidge-jun:devfrom
luvs01:fix/pool-reauth-cause-followup

Conversation

@luvs01

@luvs01 luvs01 commented Sep 19, 2026

Copy link
Copy Markdown
Collaborator

Motivation

Follow-up to #5191, whose change was carried into dev by the #5199 squash (09698b9b3a). The squash was taken from that PR's original commit, so the fixes made for its first review — and the further scope its second review recommended — never landed on dev:

  • fetchFreshPoolAccountQuota still reports needsReauth + refresh_failed for every TokenRefreshError, so a transient token-endpoint 5xx quarantines a healthy account ([Bug]: a time-valid stored Codex pool token is quarantined on the first Responses 401 #2887), while a genuinely dead grant is never marked and flips back to healthy on the next cached listing.
  • projectCodexAccountHealth flattens a stored http_status:401/http_status:403 verification failure to refresh_failed; unauthorized/forbidden never surface.
  • A memory-only reauth mark still names refresh_failed, which outranks the stored verdict inside the projection until the mark is cleared.
  • Terminal refresh failures observed during quota probes live only in process memory, so a restart loses the cause entirely.

Description

  • fetchFreshPoolAccountQuota emits refresh_failed only for terminal refresh failures via isTerminalCodexPoolRefreshFailure (revoked/expired grant or missing credential); transient/unknown errors return needsReauth: false like the 401-recovery path, and terminal failures call markAccountNeedsReauth so the cause survives quota caching.
  • projectCodexAccountHealth surfaces a stored verification failure's own cause when the caller gives no reason: http_status:401 -> unauthorized, http_status:403 -> forbidden.
  • poolAccountDto no longer names a reason on behalf of a memory-only mark, so a stored 401/403 verdict surfaces while the mark is still present rather than only after it is cleared.
  • Terminal refresh failures (revoked/expired) observed in recoverPoolQuotaFrom401 and fetchFreshPoolAccountQuota are persisted via markCodexAccountValidationFailed with terminal: true — the same verdict the token guardian writes — so a restart cannot flip a dead grant back to healthy.
  • The two decision points the reviewer flagged on fix(codex): preserve pool reauthentication failure causes #5191 are implemented per the reviewer's recommendation (stored verdicts outrank a bare memory mark; dead grants found during probing are persisted). Happy to adjust either at review.
  • Tests: the deferred-validation matrix expects the stored http-status cause with or without the in-memory mark; the pool-reauth-cause cases assert health.reason, the dead-grant mark surviving a cached listing, the persisted refresh_revoked terminal verdict, a restart-simulated listing, and a transient 5xx producing no reauth; projectCodexAccountHealth gains stored http_status:401/403 cases.

Tests

  • bun test tests/codex-integration/codex-auth-api.test.ts — 343 pass, 0 fail
  • bun test tests/oauth/oauth-health.test.ts — 24 pass, 0 fail
  • bun x tsc --noEmit — clean

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.

…es outrank bare marks

A memory-only reauth mark carries no cause of its own, so poolAccountDto no longer names refresh_failed on its behalf: projectCodexAccountHealth then falls back to the persisted verdict, letting a stored http_status:401/403 surface as unauthorized/forbidden while the mark is still present.

Terminal refresh failures found during quota probes (revoked/expired grants) are now persisted through markCodexAccountValidationFailed with terminal: true, the same verdict the token guardian writes - the in-memory mark dies with the process, and only the stored verdict keeps a cached listing from calling the dead grant healthy after a restart. Applied in both recoverPoolQuotaFrom401 and fetchFreshPoolAccountQuota.

Tests: the deferred-validation matrix now expects the stored http status cause whether or not the in-memory mark survives; the pool-reauth-cause helper gains persisted-verdict and restart-simulation assertions for the dead-grant path.
@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Repository: lidge-jun/opencodex/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: d83e3726-3e23-46df-8649-7e18e41764a8

📥 Commits

Reviewing files that changed from the base of the PR and between b9d430b and 4d4bfd7.

📒 Files selected for processing (6)
  • src/codex/auth-api/account-list.ts
  • src/codex/auth-api/pool-quota-probe.ts
  • src/oauth/health.ts
  • tests/codex-integration/codex-auth-api.test.ts
  • tests/helpers/pool-reauth-cause.ts
  • tests/oauth/oauth-health.test.ts
 __________________________________________
< Dollars to donuts, you didn't test this. >
 ------------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

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 19, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Deterministic hygiene checks failed.

  • 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/oauth/health.ts.
  • missing_coauthor_credit — This pull request says it reimplements, supersedes, carries, or rebases another author's pull request, but no Co-authored-by trailer names that author. Prose in a commit body is not read by anything; the trailer is what GitHub counts. Add it to the description or a commit, or obtain attribution-approved. Paths: #5199.

@github-actions

github-actions Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • hygiene: unsponsored_surface. hygiene: missing_coauthor_credit.

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/oauth/health.ts.
  • Fix missing_coauthor_credit — This pull request says it reimplements, supersedes, carries, or rebases another author's pull request, but no Co-authored-by trailer names that author. Prose in a commit body is not read by anything; the trailer is what GitHub counts. Add it to the description or a commit, or obtain attribution-approved. Paths: #5199.
  • 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.

Automatic draft conversion failed (token cannot change draft status). Please convert this pull request to a draft manually. The required enforce-target check will keep failing until every issue above is resolved.

@github-actions github-actions Bot added the bug Something isn't working label Sep 19, 2026
@lidge-jun
lidge-jun marked this pull request as ready for review September 19, 2026 22:29
@lidge-jun
lidge-jun merged commit a32b8cc into lidge-jun:dev Sep 19, 2026
3 of 5 checks passed
@lidge-jun

Copy link
Copy Markdown
Owner

리뷰 · 우선순위 30 / 80

이 PR은 #5191 리뷰에서 고치라고 한 두 가지를 넣습니다. #5191은 스쿼시 #5199로 dev에 들어갔고, 그 스쿼시에는 리뷰 수정이 없었습니다. 베이스는 dev입니다. PR은 아직 초안입니다.

토큰 서버가 5xx를 주면 이제 재로그인을 요구하지 않습니다. 갱신 토큰이 거절되거나 만료된 경우만 refresh_failed로 적고, 가디언과 같은 판정을 파일에 남깁니다. 서버를 다시 켜도 계정 목록은 그 계정을 건강으로 바꾸지 않습니다. 파일에 401이 있으면 unauthorized, 403이면 forbidden입니다. 메모리 표시만 있을 때는 이유를 지어내지 않아서, 저장된 401과 403이 그대로 보입니다. src/oauth/health.ts가 인증 파일이라 검사가 unsponsored_surface로 막혀 있습니다. 본문에 #5199를 carried라고 적어서 missing_coauthor_credit도 같이 실패합니다. 준비 체크리스트는 0/4입니다.

src/codex/account-usability.ts:104 - 요청에 쓸 계정을 고를 때는 메모리 표시와 검증 대기만 봅니다. 파일의 lastCodexValidationTerminal은 안 봅니다. 목록은 projectCodexAccountHealth가 그 판정을 읽어서 재로그인이 필요하다고 보여 줍니다. 고르기는 재시작 뒤에 그 판정이 있어도, 검증이 이미 끝난 계정이면 통과시킵니다. 목록은 죽었다고 하고, 프록시는 그 계정으로 요청을 보냅니다. 테스트는 목록만 확인하고 고르기는 확인하지 않습니다.

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

고르기도 목록과 같이 막을지 정해 주세요. 토큰 가디언이 적어 둔 판정도 고르기는 원래 안 봅니다. 이 PR이 사용량 조회에서도 같은 판정을 남기면, 목록과 실제 선택이 어긋나는 계정이 늘어납니다.

missing_coauthor_credit은 본문의 carried와 #5199 때문입니다. #5191과 이 PR 작성자는 둘 다 luvs01입니다. #5199 작성자는 스쿼시를 머지한 lidge-jun입니다. Co-authored-by를 넣을지, attribution-approved로 넘길지 정해 주세요.

너의 추천

src/codex/account-usability.ts에서 기록을 읽은 뒤, lastCodexValidationTerminal이 참이고 lastCodexValidationStatusfailed이면 needs_reauth를 반환하세요. 재시작 뒤에 죽은 갱신 토큰으로 요청이 나가지 않습니다. 그 경우를 테스트에 하나 넣으세요. maintainer-sponsored와 공동 작성자 처리를 끝내기 전에는 머지하지 마세요.

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

@luvs01
luvs01 deleted the fix/pool-reauth-cause-followup branch September 20, 2026 06:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working intake: hygiene-blocked Deterministic PR hygiene checks failed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants