fix(codex): gate account-native models by entitlement - #2146
Conversation
gpt-daybreak-blue-latest is in the static native set, so catalog sync copied it onto every account selector and Pool could bind a bare Daybreak request to an account whose authenticated roster never contained it. The upstream answered "The 'gpt-daybreak-blue-latest' model is not supported when using Codex with a ChatGPT account." Make the authenticated ChatGPT roster the source of truth: discover per-account entitlement, advertise the gated row only where an eligible account confirms it, and refuse selection of an account that cannot serve it. Discovery failures fail closed - the row disappears rather than being offered on unproven evidence. Carries @Ingwannu's #2101, with three corrections: Selector compact missed the wire rewrite. accountGatedCompactWireModel was derived from the caller's raw model string, and an account-qualified selector like side/gpt-daybreak-blue-latest does not match the gated map, so it still took the native compact endpoint the guard exists to avoid. It now derives from route.modelId, the same value core.ts normalizes from. Direct callers shared one 64-entry roster cache with main/Pool. A burst of distinct Direct callers evicted the very entries the catalog projects from, so the gated row vanished until rediscovery. The two classes now evict separately. A comment in native-models.ts still claimed routing never collapses Daybreak into gpt-5.6-sol, which the wire normalization does exactly. Stacked on #2137: this consumes the substituteMainCredential value that PR corrects, so it must not land ahead of it. Closes #2097
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
✅ Deterministic PR hygiene checks passed. |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (2)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
리뷰 · 우선순위 66 / 80#2097 실화임. 지금 #2101을 가져온 거임. base가 남은 구멍은 본문이 말함. 점수는 66임. 맞는데 2.27 불은 #2143 로그가드랑 #2137 bearer임. 예전에 엔타이틀먼트는 마지막에 두기로 했음. 그 순서 유지. 해결방안: #2137 머지 후 이 PR. #2097 attribution. Direct 광고 구멍은 후속. 이 댓글은 grok-bot이 작성했습니다 |
Ingwannu
left a comment
There was a problem hiding this comment.
I cannot approve the current entitlement-gating head because it is 22 commits behind the current dev tip (caf20353f). This change crosses catalog, account usability, authentication context, convergence, and request admission, so stale validation is not sufficient.
Please rebase the focused entitlement work onto the latest dev, resolve the current catalog/auth conflicts, and rerun the focused account-entitlement regressions plus full exact-head CI. Re-request review after the final file set and current-head behavior are stable.
Summary
gpt-daybreak-blue-latestlives in the static native set, so catalog sync copied it onto every account selector and Pool could bind a bare Daybreak request to an account whose authenticated roster never contained it. The upstream answeredThe 'gpt-daybreak-blue-latest' model is not supported when using Codex with a ChatGPT account.(#2097).This makes the authenticated ChatGPT roster the source of truth: entitlement is discovered per account, the gated row is advertised only where an eligible account confirms it, and an account that cannot serve the model is skipped before selection. Discovery failures fail closed — the row disappears rather than being offered on unproven evidence.
Credit: @Ingwannu's #2101 is the implementation, the 333-line entitlement module, and the test suite.
Stacked on #2137 — merge that first
This is a real dependency, not a convenience: this change passes
substituteMainCredentialForDirect: substituteMainCredentialintoresolveCodexAuthContext, consuming the exact value #2137 corrects. Landing it ondevalone would silently reintroduce #2132 for every routed provider.It is the only genuine stack edge in this backlog — every other absorbed PR was disjoint and shipped as a sibling.
Three corrections
Selector compact missed the wire rewrite.
accountGatedCompactWireModelwas derived from the caller's raw model string, and an account-qualified selector likeside/gpt-daybreak-blue-latestdoes not match the gated map (slugsEquivalentreads the account namespace as a routed provider prefix). It still took the native compact endpoint the guard exists to avoid. Now derived fromroute.modelId, the same valueapplyCodexAccountGatedWireNormalizationuses.Direct callers shared one 64-entry roster cache with main/Pool. Direct keys are per-credential and unbounded in practice, while main/Pool keys are what the catalog projects from — so a burst of distinct Direct callers evicted the gated row until rediscovery. The two classes now evict separately.
Comment rot in
native-models.tsclaimed routing never collapses Daybreak intogpt-5.6-sol, which the wire normalization does exactly. Corrected to describe what the code does.Verification
bun test --isolate tests— 13,554 pass, 0 fail, 10 skip (857 files), on the stacked head.bun test --isolateoncodex-model-entitlements,bearer-admission-routed-provider,codex-auth-context,server-auth— 146 pass, 0 fail. That set is the composition check: fix(responses): stop requiring a ChatGPT credential for routed providers #2137's tests and fix(codex): gate account-native models by entitlement #2101's pass together at this tip.bun run typecheck— clean.bun run privacy:scan— passed.Known gaps, not fixed here
Named rather than left for a reviewer to find:
nativeOpenAiSlugs()/visibleNativeSlugs()ignore account mode, so a Direct/v1/modelslisting without selectors can still advertise Daybreak from a Pool-only grant. That is advertisement only — Direct dispatch still checks the caller/main credential — and it predates this change. Same-account gated-400 retry remains Pool-only.Supersedes
Closes #2101 (@Ingwannu) once merged, with attribution.
Checklist
Closes #2097