Skip to content

fix(catalog): backfill a published context window onto a live routed row - #5023

Merged
lidge-jun merged 2 commits into
devfrom
codex/lane-s-4971-catalog-window-backfill
Sep 18, 2026
Merged

lidge-jun merged 2 commits into
devfrom
codex/lane-s-4971-catalog-window-backfill

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Sep 18, 2026 •

Copy link
Copy Markdown
Owner

Summary

augmentRoutedModelsWithMetadata was append-only by construction. It added generated-registry rows the live provider list did not return, and if (seen.has(key)) continue skipped every id the live list did return — so it had no merge branch at all. A discovered OpenCode Go row therefore kept contextWindow: undefined even though the generated registry publishes a window for exactly that provider and id, which is the normal case there: /v1/models returns id, object, created and owned_by with no context field.

This is not a re-fix of #4944, and the serialized catalog was never affected. applyCatalogMetadata writes context_window, max_context_window and auto_compact_token_limit onto the entry straight from the generated table on both derive paths, so the entry Codex reads was already correct. #4962 repaired that data and did not touch this function; I checked its merged diff (6ba36c3076, an ancestor of this branch) before writing anything, and it changed only scripts/model-metadata.source.json, src/generated/model-metadata.ts, the two layout maps, and its own regression file. None of its four tests calls augmentRoutedModelsWithMetadata.

What was affected is every consumer that reads CatalogModel.contextWindow instead of the serialized entry, and those are not cosmetic:

  • buildClaudeContextWindows filters out routed rows with no positive window, so the map that decides the Claude [1m] marker never learned about a published 1M model and could not mark it. computeEffectiveModelEnv passes gatherRoutedModels output straight in, so this is the live path.
  • src/claude/model-info.ts derives routed max-input and the 1M variant from the same raw field.
  • The Grok config writer, the management and Claude Desktop projections (including supports1m), and the OpenCode, Cline and sibling exporters all copy the field only when present, so each omitted a value the registry knew.

Both consumers named in the report are actually safe, which is worth recording so they are not "fixed" later on a wrong diagnosis. resolveComboCatalogMember computes a provider-owned generated-metadata fallback before it examines the live row, so combo derivation never reaches the generic 128k fallback for an id the registry publishes. And src/routing/capability.ts reads provider/config maps plus a CatalogModelRow reconstructed from the serialized opencodex_capability_provenance, not the gathered CatalogModel — and that provenance writer independently falls back to generated metadata.

The fix is the missing-value merge branch. A live positive window still wins, because the upstream is the authority on its own model. The seeded row is re-hinted through applyProviderConfigHints so operator precedence is unchanged: a configured window still lowers it and providerContextCaps still caps it, exactly as for an appended row. Re-hinting an existing row is not a new pattern — resolveComboCatalogMember already does it.

Closes #4971

Verification

Local verification was not run, because this lane forbids it. No test, focused test, typecheck, build, install, or ocx invocation was executed. Hosted CI on this branch is the executable verification for this change.

Static verification performed instead:

  • Read applyProviderConfigHints to confirm the precedence the re-hint relies on. A seeded window becomes discoveredWindow, so a configured cap lowers it via Math.min, and applyProviderContextCap then applies providerContextCaps and stamps contextCap/contextCapped. A merge that wrote meta.contextWindow directly onto the row would have skipped both; the fourth new test is there specifically to catch that.
  • Checked every existing caller and test of this function for breakage. The three cases in tests/codex-integration/codex-catalog.test.ts are unaffected: two pass [] and exercise only the append path, and opencode-go catalog sync appends official rows missing from /v1/models passes one windowless live row and asserts slug membership plus toHaveLength(1) — the merge replaces that row at its existing index, so the length is preserved.
  • Confirmed the dedupe semantics survive the seen-to-indexByKey change, including a duplicate id within one metadata list: the second visit now finds the row just appended, sees it already has a window, and continues.
  • Traced the reachability claim to its end rather than asserting the field in isolation, which is why the second new test drives buildClaudeContextWindows and withOneMillionMarker and also asserts the pre-fix state of the same row, so it cannot pass vacuously.

Ratchet and layout, checked because both have broken other pull requests this cycle:

  • The historical unit cases for this function live in tests/codex-integration/codex-catalog.test.ts, which sits at exactly its recorded cap (7,985 lines in tests/fixtures/file-size-baseline.json) with zero headroom. Appending there would have failed the ratchet for every later PR.
  • The regression therefore extends the existing sibling tests/codex-integration/catalog-opencode-go-context-window.test.ts, the file fix(catalog): publish opencode-go context windows so live rows stop falling back to 128k #4962 created for the neighbouring symptom, which is the natural home anyway. It goes from 118 to 207 lines, has no baseline cap, and stays far below the 2,000-line threshold for unbaselined files. No new layout registration is needed because that basename is already present in both scripts/test-layout/layout.json and tests/fixtures/test-layout-expected.json.
  • src/codex/catalog/routed-gather.ts is not in the ratchet baseline and remains under the threshold.

Union-with-dev check, since a green PR is not a green merge result: this branch is based on 11bc4f708c. It changes one function body and one test file. It touches no roster, no hard-coded count, no exhaustive adapter or provider map, no locale catalog, and no generated or golden file — in particular it does not touch src/generated/model-metadata.ts or scripts/model-metadata.source.json, so it cannot collide with the model-metadata-sync byte comparison or with another provider-catalog refresh landing beside it. A later snapshot refresh that adds ids composes with this change rather than conflicting: more published windows simply means more rows the merge branch can fill.

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.

Docs: no user-facing surface changes. The behaviour is that an already-published context window now reaches the routed row it belongs to; docs-site documents the window sources, not this internal merge, and structure/catalog.md already describes generated metadata as the fallback authority. The rationale that would otherwise be lost is recorded in the function's own doc comment, including which two consumers are not affected. Security: no credential, auth, logging, or network behaviour is involved; the change moves an integer already present in a generated table onto an in-memory row.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed missing context-window information for certain live-discovered models.
    • Models now correctly display the published context limit when live data does not provide one.
    • Preserved upstream-reported limits and continued applying configured provider caps.
    • Restored accurate eligibility for displaying the “1M” context-window marker.
    • Prevented duplicate model entries while ensuring newly discovered models remain available.

augmentRoutedModelsWithMetadata was append-only by construction. It added
generated-registry rows the live provider list did not return and skipped every
id the live list did return, so it had no merge branch at all. A discovered
OpenCode Go row therefore kept contextWindow: undefined even though the
generated registry publishes a window for exactly that provider and id, which
is the normal case there: /v1/models returns id, object, created and owned_by
with no context field.

The serialized Codex catalog was never affected and this is not a re-fix of
#4944. applyCatalogMetadata writes context_window, max_context_window and
auto_compact_token_limit onto the entry straight from the generated table on
both derive paths, so the entry Codex reads was already correct. #4962 repaired
that data and changed nothing here.

What was affected is every consumer that reads CatalogModel.contextWindow
instead of the serialized entry, and those are not cosmetic.
buildClaudeContextWindows filters out routed rows with no positive window, so
the map that decides the Claude [1m] marker never learned about a published 1M
model and could not mark it. The Grok config writer, the management and Desktop
projections, and the OpenCode and Cline exporters all copy the field only when
present, so each omitted a value the registry knew.

Note that both consumers named in the report are actually safe, which is worth
recording so they are not "fixed" later: combo derivation resolves a
provider-owned generated fallback before it examines the live row, and
src/routing/capability.ts reads config maps plus the serialized capability
provenance rather than the gathered CatalogModel.

Add the missing-value merge branch. A live positive window still wins, because
the upstream is the authority on its own model. The seeded row is re-hinted
through applyProviderConfigHints so operator precedence is unchanged: a
configured window still lowers it and providerContextCaps still caps it,
exactly as for an appended row. Re-hinting an existing row is the same thing
resolveComboCatalogMember already does.
@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 18, 2026 05:57
@github-actions

Copy link
Copy Markdown
Contributor

✅ Deterministic PR hygiene checks passed.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 18, 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
📝 Code Review ✅ Completed 2026-09-18T06:02:20.787897Z 06e9c6f 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 the bug Something isn't working label Sep 18, 2026
@coderabbitai

coderabbitai Bot commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview 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: Advanced

Run ID: c76f7ac0-3c78-4e7d-8d69-9f5145442dc8

📥 Commits

Reviewing files that changed from the base of the PR and between 11bc4f7 and 06e9c6f.

📒 Files selected for processing (2)
  • src/codex/catalog/routed-gather.ts
  • tests/codex-integration/catalog-opencode-go-context-window.test.ts

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


📝 Walkthrough

Walkthrough

Changes

Context Window Backfill

Layer / File(s) Summary
Augmentation logic
src/codex/catalog/routed-gather.ts
augmentRoutedModelsWithMetadata now indexes live rows by provider and model ID. If a live row lacks a positive contextWindow, the function fills it from published metadata, reapplies provider hints, and preserves existing positive values.
Regression validation
tests/codex-integration/catalog-opencode-go-context-window.test.ts
Tests verify context-window backfill, [1m] marker generation, upstream-value precedence, provider caps, in-place replacement, and appending of absent IDs.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Low

Sequence Diagram(s)

sequenceDiagram
  participant LiveModels
  participant CatalogAugmentation
  participant PublishedRegistry
  participant ContextWindowBuilder
  participant OneMillionMarker
  LiveModels->>CatalogAugmentation: provide discovered model row
  PublishedRegistry->>CatalogAugmentation: provide published context window
  CatalogAugmentation->>CatalogAugmentation: fill missing window and apply provider hints
  CatalogAugmentation->>ContextWindowBuilder: return augmented model
  ContextWindowBuilder->>OneMillionMarker: pass model context window
  OneMillionMarker-->>ContextWindowBuilder: append [1m] for one-million window
Loading

Merge Risk: ⚪ Minimal · up to 06e9c

The context-window backfill is covered for the intended live-row and downstream marker behavior, with no actionable current-head risk identified.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 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 main change: backfilling a published context window onto a matching live routed row.
Linked Issues check ✅ Passed Issue #4971 requires a merge path for matching live rows. The change in src/codex/catalog/routed-gather.ts uses the existing provider/id match, fills contextWindow only when the live row lacks a…
Out of Scope Changes check ✅ Passed The reviewed changes are limited to augmentRoutedModelsWithMetadata in src/codex/catalog/routed-gather.ts and regression coverage in `tests/codex-integration/catalog-opencode-go-context-window.tes…
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • 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.

@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 74 / 80

이 PR은 지금 dev tip(11bc4f708, #4781 native-main 프로필 관리 phase 1, package 2.59.0) 위에서 이슈 #4971을 고친다. 문제의 핵심은 augmentRoutedModelsWithMetadata(src/codex/catalog/routed-gather.ts)가 처음부터 append-only였다는 점이다. 생성된 레지스트리에만 있고 라이브 /v1/models 목록에는 없는 id는 붙이지만, 라이브 목록에 이미 있는 id는 seen.has(key)로 통째로 건너뛴다. 그래서 OpenCode Go처럼 discovery가 id/object/created/owned_by만 주고 context 필드는 아예 안 주는 제공자에서는, 레지스트리가 같은 provider/id에 대해 창(window)을 이미 출판해 두었어도 라이브 CatalogModel.contextWindow는 undefined로 남는다.

중요한 구분이다. 이 결함은 #4944의 재발도 아니고, Codex가 읽는 직렬화 카탈로그 항목을 망가뜨린 것도 아니다. applyCatalogMetadata는 derive 경로에서 생성 테이블에서 바로 context_window / max_context_window / auto_compact_token_limit를 쓴다. #4962는 그 데이터를 고쳤고 이 함수는 건드리지 않았다. 진짜로 영향을 받는 쪽은 CatalogModel.contextWindow를 직접 읽는 소비자다. buildClaudeContextWindows는 양의 창이 없는 routed 행을 맵에서 빼 버리므로, 출판된 1M 모델이 Claude [1m] 마커를 못 받는다. Grok 설정 작성기, management/Desktop 투영, OpenCode·Cline 등 exporter도 필드가 있을 때만 복사해서 레지스트리가 아는 값을 빠뜨린다. 본문이 짚은 대로 combo 도출(resolveComboCatalogMember)과 src/routing/capability.ts는 생성 메타/직렬화 provenance 쪽 fallback이 있어서 이 구멍의 직접 피해자는 아니다.

고침은 빠진 값만 채우는 merge 분기다. seen Set을 indexByKey Map으로 바꿔 기존 행 인덱스를 찾고, 라이브에 양의 contextWindow가 이미 있으면 그대로 두고, 없고 레지스트리에만 양의 값이 있으면 { ...existing, contextWindow: publishedWindow }로 시드한 뒤 applyProviderConfigHints로 다시 힌트한다. 그래서 운영자 modelContextWindows 하한과 providerContextCaps 상한이 append 경로와 같은 우선순위로 유지된다. 라이브가 권위라는 규칙도 명확하다. 업스트림이 더 작은 창을 보고하면 레지스트리 숫자로 올리지 않는다. 회귀는 이미 #4962가 만든 sibling tests/codex-integration/catalog-opencode-go-context-window.test.ts에 붙였다. codex-catalog.test.ts는 파일 크기 ratchet cap(7,985줄)에 딱 붙어 있어서 여기에 붙이면 이후 PR이 전부 깨진다. 새 테스트는 창이 Claude 마커까지 도달하는지, 라이브가 이기는지, providerContextCaps가 백필 뒤에도 막는지, in-place 교체로 중복이 안 생기는지까지 본다. types/config 대분할에 걸리지 않는 좁은 catalog gather 수정이고, 중복 PR도 아니다.

라인 routed-gather.ts merge 분기 - 라이브 행에 대해 채우는 필드는 contextWindow뿐이다. 같은 함수의 append 경로는 maxOutputTokens와 inputModalities도 레지스트리에서 가져오는데, discovery가 그 필드도 비운 채 id만 주는 제공자에서는 라이브 행이 여전히 modality/max-out을 못 받는다. #4971 범위로는 맞지만, 소비자 비대칭이 남는다.
라인 테스트 qwen3.8-flash / 1_000_000 - 새 케이스가 생성 메타의 현재 숫자를 하드코딩한다. 스냅샷 리프레시로 창이 바뀌면 이 sibling이 먼저 깨진다. #4944 쪽 EXPECTED_WINDOWS와 같은 결합이니 의도는 보이지만, 한 id만 고정한 점은 나중에 표와 어긋날 여지가 있다.
라인 테스트 providerContextCaps only - 재힌트가 applyProviderConfigHints를 탄다는 것은 캡 테스트로 잘 막혔다. 다만 본문이 강조한 설정된 modelContextWindows가 시드 창을 낮춘다 경로는 별도 assert가 없다. 힌트 함수 자체 계약에 기대는 형태라 지금 당장 틀리진 않지만, merge 전용 회귀로는 한 칸 비어 있다.
라인 liveWindow 판정 - typeof === "number" && > 0이라 0/음수/NaN 라이브 값은 “없음”으로 취급되어 레지스트리로 채워진다. 업스트림이 0을 진짜로 보내는 일은 거의 없지만, “라이브가 권위” 문장과는 살짝 어긋나는 모서리이다.
경로 검증 메모 - 레인 정책상 로컬 test/typecheck를 돌리지 않았고 hosted CI가 실행 검증이다. 정적 추적(힌트 우선순위, 기존 caller, ratchet/layout, union-with-dev)은 본문에 자세히 적혀 있어 리뷰 신뢰도는 높다.

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

너의 추천
CI 초록 확인 후 merge 쪽으로 가도 된다. 범위가 한 함수+sibling 회귀로 아주 좁고, #4944 데이터 수정과 읽기 경로를 정확히 갈라 놓았다. 머지 전에 가능하면 modelContextWindows 하한 테스트 한 줄과, modality/max-out은 “의도적 비범위”인지 한 줄 코멘트만 남기면 후속 오진을 더 줄일 수 있다. types.ts/config.ts 대분할에 무효화되지 않으므로 close-don't-rebase 대상이 아니다.

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 06e9c6f737

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

: undefined;
const liveWindow = typeof existing.contextWindow === "number" && existing.contextWindow > 0;
if (liveWindow || publishedWindow === undefined) continue;
const seeded: CatalogModel = { ...existing, contextWindow: publishedWindow };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Document the client-visible catalog backfill

When discovery omits a window, this new merge changes user-visible behavior in Claude model labels and generated Grok/client configurations, but the commit updates neither docs-site/ nor the structure documentation mapped to src/codex/ in structure/INDEX.md. Add the backfill and precedence contract to the applicable structure documents and update the public documentation for the affected client projections so the documented catalog behavior does not drift from runtime behavior. The scoped source instructions explicitly require mapped structure documents to be updated in the same change.

AGENTS.md reference: src/AGENTS.md:L11-L11

Useful? React with 👍 / 👎.

@lidge-jun

Copy link
Copy Markdown
Owner Author

Merging with macOS legs outstanding, and recording why rather than leaving it implicit.

At this exact head the full Linux suite (test 1/4 through 4/4), gates, storage policy, enforce-target, the docs build, and the keyring and npm-global smokes are green. The macOS legs are queued behind a saturated hosted-runner pool shared by several concurrent lanes, and the sharded macOS legs are separately known to go silent mid-suite and be cancelled at their job budget — a long-standing defect recorded with six occurrences in #4956, including two from the 2.58.0 round that were previously written off as capacity.

This change is platform-neutral, so waiting on a queue that is both saturated and known-unreliable would delay the work without adding information. The evidence that governs the release is not per-PR macOS legs; it is the full-platform lane=all dispatch at the frozen release candidate, which is held until #4956 has a named cause. Nothing is promoted on the strength of this merge.

Stating the boundary plainly: this is merged on Linux, gates and cross-platform smoke evidence at its exact head, with macOS coverage deferred to the candidate run rather than claimed here.

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant