Skip to content

fix(catalog): fold dated model ids in both directions and at both widths - #3117

Closed
olddonkey wants to merge 1 commit into
lidge-jun:devfrom
olddonkey:fix/dated-variant-fold
Closed

fix(catalog): fold dated model ids in both directions and at both widths#3117
olddonkey wants to merge 1 commit into
lidge-jun:devfrom
olddonkey:fix/dated-variant-fold

Conversation

@olddonkey

@olddonkey olddonkey commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes #3024.

Summary

mergeConfiguredModelsIntoLiveCatalog keeps a configured id when live discovery returns the same deployment under a date-suffixed id. isDatedVariantId decided that, and it was wrong in two independent ways:

  1. it accepted only an eight-digit YYYYMMDD suffix, and
  2. it folded only configured = base against live = dated.

Both halves have to hold for the reported case. Alibaba Token Plan entitles the account to deepseek-v4-pro-0813 while upstream GET /models advertises just deepseek-v4-pro, so neither the four-digit suffix nor the reverse direction matched and the configured id was removed from the authoritative live catalog — silently, since GET /api/providers still reported discovery: { "status": "ok" }. The reporter verified the model is callable both directly upstream and through the proxy.

const DATED_VARIANT_SUFFIX = /^(\d{8}|(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01]))$/;

export function isDatedVariantId(liveId: string, configuredId: string): boolean {
  return hasDatedSuffix(liveId, configuredId) || hasDatedSuffix(configuredId, liveId);
}

Two deliberate limits

The four-digit branch range-checks month and day. A numeric suffix is not automatically a date — a -4096 context size or a -2025 year must not be read as a deployment date and aliased onto an unrelated live row's metadata. Covered by a test.

The eight-digit branch stays a bare digit run. Tightening it to a real calendar date would change which ids fold today, which is not what this fixes.

The fold still requires a live relative, so an id with no live counterpart drops exactly as before.

Verification

Reverting isDatedVariantId to its previous body turns all four new cases red, including the end-to-end one:

(fail) isDatedVariantId folds a four-digit MMDD suffix
(fail) isDatedVariantId folds in both directions
(fail) isDatedVariantId refuses numeric suffixes that are not dates
(fail) configured dated id is retained when live advertises only the base (issue #3024)

The end-to-end test reproduces the report's shape — upstream advertises deepseek-v4-pro and deepseek-v4-flash-0731, config lists both plus deepseek-v4-pro-0813 and a deepseek-retired-9 with no live relative — and asserts the first three survive while the last still drops.

  • bun run test16628 pass, 14 skip, 0 fail, exit 0; all six serial lanes green.
  • bun run typecheck — passed.
  • bun run privacy:scan — passed.
  • tests/codex-catalog.test.ts — 226 pass / 0 fail.

One tradeoff worth stating

A provider that genuinely retires a dated id while keeping its base will now keep that id advertised, where dropping it was the right answer. The fold cannot distinguish "unadvertised but callable" from "retired".

The forward direction already carried the mirror image of this risk and was accepted. The cost of today's behaviour is concrete and reported: an entitled, verified-callable model disappears from the dashboard, ocx models live, and the Codex model picker, with no signal on any API surface.

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.

Summary by CodeRabbit

  • Bug Fixes
    • Improved recognition of dated model variants during model discovery.
    • Supports both full-date (YYYYMMDD) and short-date (MMDD) variant formats.
    • Prevents unrelated numeric suffixes from being incorrectly interpreted as dates.
  • Tests
    • Added coverage for matching dated and base model IDs in either direction.
    • Added validation for accepted and rejected date suffix formats.

Fixes lidge-jun#3024.

`mergeConfiguredModelsIntoLiveCatalog` keeps a configured id when live discovery
returns the same deployment under a date-suffixed id. `isDatedVariantId` decided
that, and it was wrong twice over: it accepted only an eight-digit `YYYYMMDD`
suffix, and it folded only `configured = base` against `live = dated`.

Both halves have to hold for the reported case. Alibaba Token Plan entitles an
account to `deepseek-v4-pro-0813` while `GET /models` advertises just
`deepseek-v4-pro`, so neither the four-digit suffix nor the reverse direction
matched and the configured id was removed from the authoritative live catalog —
silently, since `GET /api/providers` still reported `discovery: ok`. The model is
callable both directly and through the proxy.

The four-digit branch range-checks month and day so an ordinary numeric suffix —
a `-4096` context size, a `-2025` year — cannot be read as a deployment date and
aliased onto an unrelated live row's metadata. The eight-digit branch stays a
bare digit run on purpose: tightening it to a real calendar date would change
which ids fold today, which is not what this fixes.

The fold still requires a live relative, so an id with no live counterpart drops
as before; the end-to-end test pins that alongside the retained ones.

One tradeoff worth stating. A provider that genuinely retires a dated id while
keeping its base will now keep that id advertised, where dropping it was right.
The fold cannot tell "unadvertised but callable" from "retired". The forward
direction already carried the mirror of this risk, and the cost of today's
behaviour is concrete and reported: an entitled, verified-callable model
disappears with no signal on any API surface.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
🔒 Security Review Completed 2026-08-31T20:51:24.469241Z 4a9f266 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions github-actions Bot added bug Something isn't working review-ready labels Aug 31, 2026
@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

✅ READY

  • all PR quality gates passed; the review readiness checklist is complete.

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.

4/4 boxes ticked.

This pull request is already Ready for Review.
The review-ready label marks this PR as ready; review automation runs independently.
Maintainers: @lidge-jun @Ingwannu

@coderabbitai

coderabbitai Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Team

Run ID: b272c930-0b96-4209-a5cc-a5fa6f8a3476

📥 Commits

Reviewing files that changed from the base of the PR and between 42ad9c4 and 4a9f266.

📒 Files selected for processing (2)
  • src/codex/catalog/provider-fetch.ts
  • tests/codex-catalog.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The catalog now recognizes dated model variants with either YYYYMMDD or validated MMDD suffixes in both identifier directions. Discovery tests verify that configured dated IDs remain when live discovery returns their base IDs.

Changes

Dated variant catalog handling

Layer / File(s) Summary
Symmetric date matching and discovery retention
src/codex/catalog/provider-fetch.ts, tests/codex-catalog.test.ts
isDatedVariantId now checks both base-to-dated and dated-to-base comparisons. The suffix matcher accepts validated eight-digit dates and four-digit month/day dates. Tests cover configured dated IDs retained during discovery, bidirectional matching, and invalid numeric suffixes.

Estimated code review effort: 2 (Simple) | ~15 minutes

Merge Risk: ⚪ Minimal · up to 4a9f2

This change preserves configured model identifiers when live discovery reports a supported dated or undated variant, preventing callable models from disappearing from the catalog. No actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers: lidge-j

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: symmetric folding of dated model IDs with support for both suffix widths.
Linked Issues check ✅ Passed The changes in src/codex/catalog/provider-fetch.ts implement the requirements in issue #3024. isDatedVariantId now supports validated YYYYMMDD and MMDD suffixes in both configured-to-live and live-to-…
Out of Scope Changes check ✅ Passed The changes are limited to the dated-variant matching logic in src/codex/catalog/provider-fetch.ts and related regression tests in tests/codex-catalog.test.ts. These changes directly support issue #30
Full details: Linked Issues check

Explanation

The changes in src/codex/catalog/provider-fetch.ts implement the requirements in issue #3024. isDatedVariantId now supports validated YYYYMMDD and MMDD suffixes in both configured-to-live and live-to-configured directions. The existing live-row requirement remains unchanged. tests/codex-catalog.test.ts adds regression coverage for valid and invalid cases.

Full details: Out of Scope Changes check

Explanation

The changes are limited to the dated-variant matching logic in src/codex/catalog/provider-fetch.ts and related regression tests in tests/codex-catalog.test.ts. These changes directly support issue #3024 and do not introduce unrelated functionality.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@lidge-jun

Copy link
Copy Markdown
Owner

리뷰 · 우선순위 70 / 80

설명

이 PR은 이슈 #3024를 지금 dev HEAD 42ad9c44d에서 고칩니다. 패키지는 2.39.0이고, 바로 앞에 들어간 것은 #3110(가득 찬 5시간 burst 창을 소진으로 보기)입니다. round-2 prio≥70에서 남은 것은 #3008과 #3019입니다. 이 PR은 그 열차 밖이지만, 카탈로그에서 쓸 수 있는 모델이 조용히 사라지는 실제 버그입니다. 작성자는 olddonkey이고, 라벨은 bug + review-ready입니다. types.ts/config.ts 분할과 무관하고, 같은 주제로 열린 다른 PR은 없습니다. #3024에는 이미 grok-bot 리뷰가 있습니다.

#3024가 말한 장면은 이렇습니다. Alibaba Token Plan(국제) 계정은 deepseek-v4-pro-0813을 쓸 수 있습니다. 설정 providers.*.models에도 그 아이디가 들어 있습니다. 그런데 업스트림 GET /models는 날짜 붙은 아이디가 아니라 기본 아이디 deepseek-v4-pro만 줍니다. 지금 HEAD의 mergeConfiguredModelsIntoLiveCatalog(src/codex/catalog/provider-fetch.ts 1648행 근처)는 설정 아이디가 라이브 목록에 없으면 isDatedVariantId로 “같은 배포 가족인가”를 봅니다. 맞으면 라이브 행의 메타데이터를 가져와서 설정 아이디로 한 줄 더 넣습니다. 틀리면 조용히 버립니다. GET /api/providers의 discovery는 여전히 ok입니다. 대시보드, ocx models live, Codex 피커에서 그 모델이 사라지는데, API는 아무 경고도 안 줍니다. 제보자는 업스트림과 프록시 둘 다로 그 모델이 실제로 호출된다고 확인했습니다.

지금 HEAD의 isDatedVariantId(939–942행)는 두 가지가 좁습니다. 첫째, live = configured-YYYYMMDD 한 방향만 봅니다. 설정이 날짜 붙고 라이브가 기본이면 바로 false입니다. 둘째, 접미사는 숫자 여덟 자리만 인정합니다. Alibaba/DeepSeek가 쓰는 네 자리 MMDD(0813, 0731)는 처음부터 탈락합니다. 그래서 #3024 모양은 접미사 폭과 방향이 둘 다 어긋나서, 폴드가 한 번도 성공하지 못합니다.

이 PR이 하는 일은 그 함수만 고치는 것입니다. DATED_VARIANT_SUFFIX는 여덟 자리 숫자 또는 달/일이 범위 안에 있는 네 자리 MMDD를 받습니다. hasDatedSuffix(longer, shorter)로 접두사+접미사를 검사하고, isDatedVariantId는 양방향을 봅니다. 라이브에 친척이 없으면 예전처럼 버립니다. 그래서 “업스트림에 아예 없는 아이디를 무조건 남긴다”(#1690 retainModels 쪽)와는 다릅니다. 본문이 말한 트레이드오프도 분명합니다. 제공자가 날짜 아이디를 진짜로 은퇴시키고 기본만 남겨 두면, 이제 그 날짜 아이디가 카탈로그에 남을 수 있습니다. 앞방향 폴드에도 거울처럼 같은 위험이 이미 있었고, 오늘은 “호출 가능한데 조용히 사라짐”이 더 아프다는 판단입니다.

테스트는 tests/codex-catalog.test.ts에 네 덩어리가 더해집니다. 끝에서 끝까지 #3024 모양(라이브는 기본만, 설정은 날짜 포함 → 날짜 유지, 친척 없는 deepseek-retired-9는 삭제), 네 자리 접미사, 양방향, 그리고 4096/1332처럼 날짜가 아닌 숫자 접미사는 거절. 본문은 함수를 예전 몸으로 되돌리면 네 케이스가 빨개진다고 적었습니다. CI hygiene/enforce-target/label/resolve-pr와 CodeRabbit은 통과입니다. 점수는 70입니다. 사용자에게 보이는 조용한 삭제 버그이고, 범위가 작고, 회귀 테스트가 제보 모양을 직접 잠급니다. 은퇴/미광고를 구분하지 못하는 한계와, 여덟 자리 접미사를 달력으로 조이지 않은 선택은 남지만, 그걸 이 PR에서 풀 필요는 없습니다.

라인 src/codex/catalog/provider-fetch.ts:DATED_VARIANT_SUFFIX - 네 자리는 달/일을 검사하지만, 여덟 자리는 예전처럼 숫자만 봅니다. model-99999999 같은 가짜 날짜도 폴드됩니다. 본문이 의도한 제한이라서 버그는 아닙니다. 나중에 여덟 자리도 달력으로 조이면 지금 폴드되던 아이디가 바뀔 수 있으니, 바꿀 때는 기존 Anthropic 스타일 케이스를 먼저 잠가야 합니다
라인 tests/codex-catalog.test.ts isDatedVariantId refuses… model-0230 - 2월 30일은 달력으로는 틀린데 폭 검사만 보면 통과라서 true로 고정했습니다. 의도입니다. 다만 “날짜처럼 보이는 잘못된 날”을 허용한다는 뜻이 주석에만 있고, 제품 문서에는 없습니다
경로 mergeConfiguredModelsIntoLiveCatalog - 폴드가 성공하면 라이브 행 메타데이터를 복사하고 아이디만 설정 값으로 바꿉니다. 설정이 deepseek-v4-pro-0813이고 라이브가 deepseek-v4-pro이면, 카탈로그에 날짜 아이디가 기본 행의 힌트/컨텍스트를 입고 생깁니다. 기본 아이디 행도 그대로 남습니다. 테스트가 둘 다 있다고 확인합니다. 메타데이터가 날짜 배포와 다르면 틀린 한도가 붙을 수 있는데, 그건 예전 앞방향 폴드와 같은 계약입니다
경로 tests/codex-catalog.test.ts 기존 테스트 이름 isDatedVariantId matches only -YYYYMMDD - 이제 네 자리와 역방향도 맞는데 이름은 그대로입니다. 동작 주장은 새 테스트가 맡고, 이름만 낡았습니다. 필수 수정은 아닙니다
경로 이슈 #1690 vs #3024 - 본문과 이슈가 구분한 대로, 라이브 친척이 있을 때만 남깁니다. 무조건 retain이 아닙니다. 리뷰어가 #1690 패치로 착각하지 않게, 머지 설명에 한 줄만 더 적어도 좋습니다

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

  • 본문이 적은 트레이드오프(은퇴한 날짜 아이디가 기본만 남을 때 카탈로그에 남는 것)를 지금 받아들일지, 아니면 후속으로 “라이브에 기본만 있으면 설정 날짜 아이디는 경고만 하고 남긴다” 같은 신호를 넣을지
  • #3024를 이 랜딩으로 바로 닫을지. 재현 모양과 테스트가 같고, discovery ok + 조용한 삭제 구멍이 이 폴드로 닫힙니다
  • round-2 남은 [Bug][Windows]: dashboard update aborts after proxy stops when history restore exits non-zero #3008 / #3019보다 먼저 넣을지. 파일은 src/codex/catalog/provider-fetch.ts와 카탈로그 테스트라서 그 두 이슈와 겹치지 않습니다
  • 여덟 자리 접미사를 언젠가 달력 검사로 조일지. 이 PR 범위 밖이 맞습니다

너의 추천
머지를 추천합니다. #3024의 두 구멍(접미사 폭 + 방향)을 최소 변경으로 막고, 끝단 테스트가 제보 모양을 잠급니다. 은퇴/미광고 구분은 후속 이슈로 남겨도 됩니다. Protect dev 리뷰 후 넣고, 머지 커밋이 정해지면 #3024를 닫으면 됩니다. 라벨은 바꾸지 않습니다.

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

@Ingwannu

Ingwannu commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Closing as superseded by #3100, which landed on dev at b46164e7858e35c50f8c1a0605d4d333fd434862.

The underlying #3024 bug was real, but the merged replacement deliberately combines the calendar-aware suffix matcher with a directional merge rule. This PR's default bidirectional fold would let a live base row resurrect a configured dated snapshot with no evidence that the dated ID remains callable—the blocker already identified during #3041 review. #3100 retains configured base IDs when live discovery supplies a dated relative, without making the unsafe reverse inference.

Thank you for the focused report and tests. No additional patch from this branch is needed now that the canonical fix is on dev.

@Ingwannu Ingwannu closed this Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working review-ready

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants