Skip to content

Feature: newly discovered provider models should arrive disabled (off) by default #2464

Description

@lidge-jun

Area

Catalog / models

What are you trying to accomplish?

When a live catalog refresh discovers models that a provider newly published, those models should arrive disabled (off) in the Codex picker by default, instead of appearing enabled and silently expanding what routes real spend. The user opts new models in as they arrive, rather than racing to turn them off.

What prevents this today?

  • Visibility is two persisted filters composed in filterCatalogVisibleModels (src/codex/catalog/provider-fetch.ts:1555-1577): the per-provider allowlist providers.<name>.selectedModels (empty/absent = ALL discovered models ship — src/types/provider.ts:262-268) and the global blocklist config.disabledModels.
  • With an empty allowlist (the default state for every provider), every newly discovered model is immediately visible.
  • There is no persisted record of which models a provider was known to have: the live model cache is in-memory with a 5-minute TTL (src/codex/model-cache.ts:15), so nothing can distinguish "newly discovered" from "always been there".

What should OpenCodex do?

Full design with a scenario-by-scenario correctness table: devlog/_plan/260824_model_ux_aliases_and_defaults/020_new_models_off.md. Summary:

  1. Persisted known-model baseline: config.modelDiscovery.knownModels[provider] = { ids, updatedAt }, written only after a successful live fetch (a failed/partial fetch must never shrink the baseline). A model is "new" iff discovered now and absent from the baseline.
  2. Policy: modelDiscovery.newModelPolicy: "off" | "on" global + per-provider override providers.<name>.newModelPolicy ("inherit" default). Fresh installs seed "off"; existing installs keep "on" until opted in (zero behavior change).
  3. Application at catalog convergence: for policy "off" with an empty allowlist, append the new ids' routed slugs to disabledModels; with a non-empty allowlist, do nothing (the allowlist already excludes them). The baseline advances monotonically with successful fetches, so each id can be auto-disabled at most once, ever — enabling a new arrival is never undone by the next refresh.
  4. Bootstrap rules: first fetch after upgrade or after an explicit provider add treats everything as known (nothing hidden); disappearance requires N=3 consecutive absent fetches before baseline removal (vendor list flapping); renames are treated as new arrivals (safe reading).
  5. Native exception: bare native OpenAI family models come from the pinned Codex runtime contract, not live discovery — exempt. Custom models are user-created — always on.
  6. Surfacing: GET/PUT /api/model-discovery (+ POST /api/model-discovery/acknowledge for badge clearing), ocx models new-policy [on|off] [--provider] and ocx models new-arrivals, GUI "NEW" badges per arrival with a per-provider count chip ("3 new, off") and a global "New models start disabled" toggle.

Example usage or interface

ocx models new-policy off                 # global: arrivals start disabled
ocx models new-arrivals
# PROVIDER    MODEL              ARRIVED               STATE
# openrouter  x-ai/grok-5        2026-08-24 02:11      auto-disabled
# openrouter  moonshot/kimi-k4   2026-08-24 02:11      auto-disabled

GUI: the new model's row shows a NEW badge and stays off; the user flips it on through the existing visibility toggle, and it stays on across refreshes.

Alternatives or workarounds

  • Maintain a non-empty selectedModels allowlist per provider by hand — works (new models are excluded), but requires the user to give up the "all on" state and manually curate hundreds of rows.
  • Manually disabling each new model after it appears — the race this proposal removes.

Additional context

Design doc: devlog/_plan/260824_model_ux_aliases_and_defaults/020_new_models_off.md. Composes with the default-preset proposal (preset providers keep a non-empty allowlist, so this policy is a structural no-op there) — filed alongside this one.

Checks

  • I searched existing issues and documentation.
  • This request describes a concrete OpenCodex workflow rather than merely naming a desired technology.
  • I removed secrets and personal data.

Activity

  1. lidge-jun commented on Aug 24, 2026

    @lidge-jun
    OwnerAuthor

    리뷰 · 우선순위 51 / 80

    설명: 이 이슈는 산 카탈로그가 새로 발견한 모델을 기본으로 끄자는 것이다. 지금은 허용 목록이 비어 있으면 새로 나온 모델이 바로 피커에 켜진 채로 들어온다. 사용자는 끄려고 서두르지 않으면 그 모델로 요청이 나갈 수 있다. 새 모델은 사용자가 켠 뒤에만 쓰게 하자는 것이다. 지난 시간 2464 조사는 없는 번호였다. 이번 시간에 열렸다. 지금 CURRENT dev HEAD 는 35a89903c 이다. 이번 시간에 origin/dev 는 그대로다. 새 머지는 없다. package.json 은 2.27.0 이다. src/config.ts 는 3238줄이다. src/runtime 폴더는 지금 HEAD 에 없다.

    하려는 일은 이렇다. 성공한 산 가져오기 뒤에만 알려진 아이디 기준을 디스크에 남긴다. 실패한 가져오기는 기준을 줄이지 않는다. 지금 목록에 있고 기준에 없는 아이디가 새 것이다. 정책이 끄기이고 허용 목록이 비어 있으면 그 라우팅 슬러그를 막기 목록에 넣는다. 허용 목록이 이미 있으면 새 아이디는 원래 빠지므로 손대지 않는다. 각 아이디는 한 번만 자동으로 끈다. 사용자가 켠 것을 다음 새로고침이 다시 끄지 않는다. 업그레이드 뒤 첫 가져오기와 제공자 추가는 전부 이미 알려진 것으로 본다. 네이티브 오픈에이아이 패밀리는 산 발견이 아니라서 빼 둔다. 커스텀 모델은 항상 켠다. 새 설치만 끄기가 기본이다. 이미 쓰는 설치는 켜짐을 유지한다.

    지금 HEAD 를 열었다. src/types/provider.ts 262줄 selectedModels 는 비어 있거나 없으면 전부 공개다. src/codex/catalog/provider-fetch.ts 1555줄 filterCatalogVisibleModels 가 허용 목록과 막기 목록을 합친다. src/codex/model-cache.ts 15줄은 산 목록을 5분 메모리에만 둔다. 예전에 있던 아이디 기록이 디스크에 없다. 그래서 새로 온 것과 원래 있던 것을 구별하지 못한다. src/server/management/model-routes.ts 557줄은 빈 목록 저장이 허용 목록을 지운다. 다시 전부 공개다. src/cli/registry.ts 168줄 ocx models 에 enable disable selected 는 있다. new-policy 와 new-arrivals 는 없다. 이슈가 가리키는 설계 문서 폴더는 지금 origin/dev HEAD 에 없다. 본문만 있다.

    1690 과 초안 2122 는 산 목록에서 빠진 설정 모델을 카탈로그에 남기는 길이다. 방향이 반대다. 닫지 말 것. 한 기차에 섞지 말 것. 2465 최신 프리셋은 허용 목록을 채우므로 그 제공자에서는 이번 정책이 구조적으로 일이 없다. 같이 설계됐지만 착지 순서가 있다. 프리셋 없이 끄기만 넣으면 막기 목록이 계속 늘어날 수 있다. 프리셋을 먼저 넣으면 새 제공자는 허용 목록을 가진다. 2463 별칭은 요청 길이다. 이 가시성과 한 기차에 섞지 말 것. 2459 윈도우 모듈 지도와 2460 스냅샷 쓰기와 2462 허브 콘솔과도 섞지 말 것. 2210 과 1049 과 2221 을 닫지 말 것.

    사용자 길이로는 새로 나온 모델이 켜진 채로 들어와 돈을 쓸 수 있어서 51. 지금 설치는 켜짐을 유지하므로 바로 행동이 바뀌지는 않는다. 새 설치와 끄기 정책으로 바꾼 사람만 보호를 받는다. 카탈로그 팁은 Ox Alpha x-preview-f-free + deepseek-v4-flash-vision-exp. Cursor 정적 카탈로그는 opus-4-8-fast / opus-5-fast. 2334 CursorCredentialRouter 는 여전히 src/providers/cursor-pool.ts 모듈+테스트만 있고 어댑터에 연결되지 않았다. 2332 H2 는 discovery 전용. 2320 overflow + 2342 는 이미 dev. 2188 사이드카는 이미 dev. 2361 __omit__ 는 이미 HEAD 다. combo-stream-preflight.ts 는 지금 HEAD 에 있다. src/lib/bounded-body.ts 128줄 취소 없는 던짐과 2426 나가는 크기 가드는 그대로다. 2451 wait 허용 목록은 아직 하이픈이다. 이번 새 모델 끄기는 그 구멍들이 아니다.

    src/types/provider.ts 라인 262 - 빈 허용 목록은 전부 공개다. 새 모델도 바로 켜진다
    src/codex/catalog/provider-fetch.ts 라인 1555 - 가시성은 허용 목록과 막기 목록이다
    src/codex/model-cache.ts 라인 15 - 산 목록은 5분 메모리다. 예전에 있던 아이디 기록이 없다
    src/server/management/model-routes.ts 라인 557 - 빈 저장은 허용 목록을 지운다. 다시 전부다
    src/cli/registry.ts 라인 168 - ocx models 에 enable disable selected 는 있다. new-policy 는 없다
    이슈 1690 / PR 2122 - 빠진 설정을 남기는 길이다. 이번 새 모델 끄기와 반대다. 닫지 말 것
    설계 문서 폴더 - 지금 origin/dev HEAD 에 없다. 본문만 명세다

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

    • 설계 문서를 같은 기차에 먼저 넣을지. 넣는 편이 맞다
    • 2465 프리셋을 이 끄기보다 먼저 넣을지. 먼저 넣는 편이 맞다. 아니면 막기 목록이 커진다
    • 이미 쓰는 설치의 기본을 켜짐으로 둘지. 이슈 본문대로 두는 편이 맞다. 조용히 좁히지 말 것
    • 1690 과 2122 를 이 이슈로 닫을지. 닫지 말 것. 방향이 반대다
    • 2463 별칭과 한 기차에 섞을지. 섞지 말 것. 별칭은 요청 길이다

    너의 추천
    이슈는 열어 둔다. 설계 문서를 origin/dev 에 먼저 넣는다. 알려진 아이디 기준과 빈 허용 목록에서만 한 번 끄기를 좁게 본다. 2465 를 같은 카탈로그 기차의 앞칸으로 둔다. 1690 과 2122 와 2463 과 2451 은 닫지 않는다. 호출 길을 넓히지 말 것. 라벨은 그대로 둔다. 프리뷰 배포가 아니다.

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

  2. lidge-jun commented on Aug 25, 2026

    @lidge-jun
    OwnerAuthor

    Closed on dev by #2609 (squash 9593b244c2efefb8194deb44a56436c0cc9f5055).

    New arrivals from a successful live discovery now land in disabledModels instead of the picker. The blocklist is the right store: it composes with an empty allowlist, whereas seeding selectedModels with the full current roster would have converted "all on" into a frozen snapshot and silently changed what existing state means.

    Three properties carry it, each with its own test: each id can be auto-disabled at most once ever (ids ∪ removed grows monotonically, so the enable-drop-restore case comes back enabled); a degraded fetch never shrinks the baseline; and bootstrap hides nothing. Preset mode is a deliberate, tested no-op — which is why this had to land after #2465 rather than beside it.

    Existing installs have no modelDiscovery key, which means policy "on" — today's behavior exactly, with zero change until you opt in. ocx models new-policy off or the Models toolbar switch turns it on.

    A review pass also caught that reconciliation reported "changed" for every authoritative provider, so convergence rewrote config.json on every catalog write even when the roster was identical. Fixed before merge and pinned by a steady-state test.

    GitHub only auto-closes on merges into main; PRs here target dev, so closing manually.

  3. added a commit that references this issue on Aug 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    catalogModel catalog, slugs, visibility, routed entriesenhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions