Repository navigation
[Bug]: ocx sync emits duplicate catalog slugs when modelAliases differs from canonical form #4730
Description
Client or integration
Codex CLI
Area
Catalog / models
Summary
ocx sync on 2.56.0 generated opencodex-catalog.json with 507 entries but only 72 unique slugs (e.g. CC-MiniMaxAI-MiniMax-M3 repeated 14x). I expected one entry per slug. The duplicates are byte-identical, so they are safe to drop — but the sync should never emit them.
Root cause (verified against pristine 2.56.0 tarball): my config uses the official modelAliases feature (providers.command-code.modelAliases = {"MiniMaxAI/MiniMax-M3": "CC-MiniMaxAI-MiniMax-M3", ...}, 17 entries). The alias form becomes the public slug via model.alias (catalogModelSlug in src/codex/catalog/parsing.ts:843: model.alias ?? routedSlug(provider, id)). The sync ALSO emits the canonical command-code/... form via routedSlug, and the merge (mergeCatalogEntriesFromObservedState in src/codex/catalog/build-entries.ts, key slugEquivalenceKey in src/providers/slug-codec.ts) never unifies them:
slugEquivalenceKey("CC-X")→["exact","CC-X"](no slash = exact key)slugEquivalenceKey("command-code/...")→["routed","command-code",...]
Three different keys for the same model = three rows.
Reproduction
- Config with provider
command-code+modelAliasesmappingVendor/Model→CC-Vendor-Model(17 entries), plus provideralias: "CC". ocx sync --restart-codex && ocx sync-cache --restart-codex(config validates{"ok": true}).jq '.models|length' ~/.codex/opencodex-catalog.json→ 507;jq '[.models[].slug]|unique|length'→ 72.- Note:
ocx sync-cachealone kept 72/72; the explosion came fromocx sync. A laterocx syncon the same config stayed 72/72, so it is intermittent (possibly first sync after upgrade, or cache-state dependent).
Version
2.56.0 (@bitkyc08/opencodex, npm latest 2026-09-15)
Operating system
Ubuntu 24.04 (VPS, HestiaCP)
Provider and model
command-code provider (gateway), any model with a modelAliases entry
Logs or error output
# before dedup
$ jq '.models|length' ~/.codex/opencodex-catalog.json
507
$ jq '[.models[].slug]|unique|length' ~/.codex/opencodex-catalog.json
72
$ jq -r '.models[].slug' ~/.codex/opencodex-catalog.json | sort | uniq -c | sort -rn | head -3
15 agy-gemini-3.8-flash
14 agy-claude-sonnet-4-6
14 CC-MiniMaxAI-MiniMax-M3
# entries are byte-identical per slug:
$ jq -c '[.models | group_by(.slug) | map(select(length>1)) | .[0][]] | unique | length'
1Local validator correctly refused to publish: publish-catalog.sh: source-invalid (requires unique slugs).
Screenshots and supporting files
Sample pair for the same model (same display_name, description, context_window):
{"slug":"CC-MiniMaxAI-MiniMax-M3","display_name":"CC-MiniMaxAI-MiniMax-M3","description":"Routed via opencodex → command-code (command-code)."}
{"slug":"command-code/MiniMaxAI-MiniMax-M3","display_name":"CC-MiniMaxAI-MiniMax-M3","description":"Routed via opencodex → command-code (command-code)."}Full 11 MB pre-dedup catalog available on request (507 entries).
Redacted configuration
{
"providers": {
"command-code": {
"alias": "CC",
"liveModels": true,
"selectedModels": ["MiniMaxAI/MiniMax-M3", "deepseek/deepseek-v4-flash"],
"modelAliases": {
"MiniMaxAI/MiniMax-M3": "CC-MiniMaxAI-MiniMax-M3",
"deepseek/deepseek-v4-flash": "CC-deepseek-deepseek-v4-flash"
}
}
}
}Proposed fix
In syncCatalogModels/retained-sync.ts, final dedup by slug after merge — the model.alias (user personalization, e.g. CC-X) wins over the canonical form when content is identical; warn with removed count. Preserves personalization, kills duplicates, single change point. Local test proving safe discard (507→72, 0 unique slugs lost, personalized content byte-identical) available as input for the PR.
Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
lidge-jun commentedon Sep 15, 2026
리뷰 · 우선순위 76 / 80
이 이슈는
ocx sync가 카탈로그를 쓸 때, 같은 모델을 서로 다른 공개 슬러그로 여러 번 넣는 버그를 보고한다. 작성자rrmlima는 2.56.0에서opencodex-catalog.json이 507개 행인데 고유 슬러그는 72개뿐이었다고 했다. 중복 행은 바이트 단위로 같고, 로컬publish-catalog.sh는 unique slug 규칙 때문에source-invalid로 거절했다. 재현 설정은providers.command-code.modelAliases로MiniMaxAI/MiniMax-M3→CC-MiniMaxAI-MiniMax-M3처럼 별칭을 주고, provideralias: "CC"까지 켠 경우다.지금
devtip(45cfb04e9, package 2.57.0)의 코드를 보면 원인 설명이 맞다.src/codex/catalog/parsing.ts의catalogModelSlug는model.alias ?? routedSlug(provider, id)라서, 별칭이 있으면 공개 슬러그가CC-…같은 슬래시 없는 문자열이 된다. 그런데 sync 경로에서는 같은 모델의 정규command-code/…형태도 같이 들어올 수 있다. 합치는 쪽은mergeCatalogEntriesFromObservedState(src/codex/catalog/build-entries.ts)이고, 키는slugEquivalenceKey(src/providers/slug-codec.ts)다.여기가 핵심이다.
slugEquivalenceKey는 슬래시가 없으면["exact","CC-X"]이고, 슬래시가 있으면["routed","command-code",…]다. 의도적으로 “별칭 문자열”과 “라우티드 슬러그”를 같은 모델로 묶지 않는다. 그래서 병합이 끝나도CC-MiniMaxAI-MiniMax-M3와command-code/MiniMaxAI-MiniMax-M3가 둘 다 남는다. 작성자가 본 것처럼 같은 표시 이름/설명을 가진 행이 여러 개 쌓이고, 한 번ocx sync에서 터졌다가 이후 sync에서는 72/72로 안정된 것처럼 보이면, 관측 상태·캐시·이전 카탈로그 잔여가 섞인 간헐 재현과도 잘 맞는다.ocx sync-cache만으로는 안 터졌다는 점도, 전체 sync의 merge 입력이 더 넓다는 설명과 맞다.제안 수정(“최종적으로 exact slug로 한 번 더 dedup하고, 내용이 같으면 alias 쪽을 남긴다”)은 증상 차단으로는 빠르고 안전하다. 다만 근본은 “별칭이 붙은 모델의 정규 routed 행을 언제 빼고 언제 남길지”를 catalog 빌드/머지 한곳에서 정하는 일이다. 최종 slug 문자열 dedup만 하면, 내용이 살짝 다른 twin(표시명만 다름 등)이 오면 어느 쪽을 살릴지 또 정책이 필요하다. types.ts/config.ts 분할 캠페인과는 무관하고, 닫아 버릴 중복 이슈로 보이지도 않는다. 카탈로그/CLI 버그로 열어 두는 것이 맞다.
경로/심볼로 보면 아래가 맞다.
parsing.ts · catalogModelSlug - alias가 있으면 슬래시 없는 공개 슬러그가 된다. 이 동작 자체는 의도된 개인화다.
slug-codec.ts · slugEquivalenceKey - 슬래시 유무로 exact/routed 키가 갈라져, alias↔canonical을 같은 모델로 보지 않는다. 작성자 분석과 일치한다.
build-entries.ts · mergeCatalogEntriesFromObservedState - equivalence key로만 묶이므로 twin slug가 그대로 살아남을 수 있다.
retained-sync.ts · syncCatalogModels - 최종 write 전에 exact-slug unique를 강제하지 않으면 publish validator와 어긋난 파일을 남긴다.
재현/증상 - 507→72, byte-identical duplicates, sync-cache만으로는 안정. 간헐성은 캐시/관측 잔여 가설이 타당하다.
메인테이너의 판단이 필요한 지점
너의 추천
버그로 받고, 작은 PR을
dev로 받는 쪽을 권한다. 1)modelAliases가 있는 provider에서 sync 결과가 slug unique인지 실패하는 회귀 테스트부터 넣고, 2) sync 최종 단계에서 exact slug dedup(동일 내용이면 alias 슬러그 우선)으로 증상을 막은 뒤, 3) 가능하면 merge/build에서 alias↔routed twin이 생기지 않게 한곳에서 정리한다. 지금 tip 기준 재현만 확인되면 우선순위 높은 catalog 버그다. types/config 분할에 걸려 닫을 대상은 아니다.이 댓글은 grok-bot이 작성했습니다
test(catalog): real-sync regression and divergent-duplicate warning for
fix(catalog): write every slug exactly once from retained sync (lidge…
test(catalog): real-sync regression and divergent-duplicate warning for
lidge-jun commentedon Sep 16, 2026
Fixed on
dev, unreleased at the time of writing.src/codex/catalog/aggregation.tsnow skips a slug it has already emitted (if (seen.has(entry.slug)) continue, lines 233-244), andsrc/codex/catalog/retained-sync.ts:523is the caller that consumes the de-duplicated list, so amodelAliasesspelling that differs from the canonical form no longer produces a second row.Landed as
fab7e427c7(#4799), whose head had Cross-platform CI success in run35084581608. It ships in 2.57.0. Reopen if you still see duplicate slugs after upgrading.