Skip to content

fix(catalog): explain a suppressed Luna Reserve row in the sync log - #4958

Merged
lidge-jun merged 2 commits into
devfrom
codex/4811-reserve-suppression-diagnostic
Sep 17, 2026
Merged

lidge-jun merged 2 commits into
devfrom
codex/4811-reserve-suppression-diagnostic

Conversation

@lidge-jun

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

Copy link
Copy Markdown
Owner

Summary

Closes #4811.

With codexDesktopAuthless: true on a fresh loopback install, no Luna Reserve row is written and
nothing says why. The Reserve projection is account-qualified — its slug is
<selector>/gpt-reserve — so createReserveCatalogProjection refuses when mainSelectors is
empty (src/codex/catalog/reserve.ts:25). Selectors come from visibleCodexAccountSelectors,
which returns [] while codexAccountPickerEnabled is false, and that predicate requires a
non-empty codexAccountNamespaces map (src/codex/account-namespaces.ts:190-195). A fresh
install has no such map, so the feature the operator just turned on produces nothing.

An omitted row has nowhere to carry a reason, which is the same shape as #4212 for account-gated
natives, so this takes the same answer: catalog sync emits one warn-once line where the selector
inputs are still in scope, naming the cause and the action that restores it. The four causes are
distinguished rather than collapsed — no enabled canonical OpenAI provider, an explicitly
disabled picker, an empty selector map, or a map whose selectors never target the main account —
because they need different fixes.

The diagnostic is scoped to an install where authless Codex Desktop routing is effective. A
default install wants no Reserve row and must never be told about one; an install that stores the
flag where it cannot take effect is a different silence, already reported as inertReason by
describeCodexDesktopSwitches, and is not repeated here.

This deliberately does not decouple the projection from the selector map. Emitting a bare
gpt-reserve row outside the account-qualified path is the capability change requested in #3844,
which was closed as not planned; the issue text asks for the diagnostic and the documentation
instead, and that boundary is respected.

codexAccountPickerEnabled and codexAccountNamespaces were absent from the configuration
reference entirely, which is why the dependency was undiscoverable. Both are now documented, with
the Reserve consequence stated on each.

Files

  • src/codex/catalog/reserve-warn.ts (new) — pure reason resolution plus a warn-once emitter,
    mirroring gated-native-warn.ts. Takes includeAccountBoundNativeOpenAi as an argument so it
    stays off the catalog metadata import graph.
  • src/codex/catalog/retained-sync.ts — calls it where reserveMainSelectors is computed.
  • src/codex/catalog/sync.ts — facade re-export, matching how gated-native-warn is exposed.
  • structure/catalog.md — records the contract under "Accounts, namespaces, and pool rotation";
    src/codex/ is owned by this doc per structure/manifest.json.
  • docs-site/.../reference/configuration/server.md — the two missing keys.
  • tests/codex-integration/reserve-catalog.test.ts — regression coverage.

Verification

Local verification was not run: this lane forbids running the local suite, focused tests,
typecheck, build, install, or the ocx binary. Hosted CI is the executable verification for this
change.

Correctness is argued statically from the source: the suppression predicate reproduces the exact
gate chain in reserve.ts → retained-sync.ts → account-models.ts → account-namespaces.ts,
and returns undefined whenever createReserveCatalogProjection would have produced a row, so
the warning cannot fire on a working install.

Hosted CI jobs that exercise this change:

  • tests/codex-integration/reserve-catalog.test.ts — seven new cases covering each reason, the
    no-suppression case, the never-opted-in case, and warn-once behaviour.
  • tests/ci-workflows/structure-ssot.test.ts / bun run structure:check — structure/catalog.md
    doc-map and ownership gate for the touched src/codex/ area.
  • tests/test-layout.test.ts — unchanged; no new test file was added, so no layout.json or
    fixture bookkeeping is required.
  • bun run typecheck and the full suite on Linux, Windows and macOS.
  • bun run privacy:scan — the new log line contains no account id, credential, or request body;
    it names configuration keys only.

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.

Summary by CodeRabbit

  • New Features

    • Added server configuration options for account-qualified catalog entries and Codex account mappings.
    • Added clear, one-time warnings when the Luna Reserve catalog entry cannot be advertised, including guidance for resolving each condition.
  • Documentation

    • Documented account selectors, account mappings, default behavior, and Luna Reserve availability.
    • Added details about Reserve suppression reasons and corrective actions.

The Luna Reserve projection is account-qualified, so it cannot be written
without a selector that targets the main Codex account. A fresh authless
install has an empty codexAccountNamespaces map, so no selector exists, the
projection returns undefined, and the operator gets no row and no reason.

An omission has no row to carry an explanation, so catalog sync now emits one
warn-once line naming the cause and the action that restores it, in the same
place and the same shape as the account-gated native suppression warning.

Scoped to an install where authless Codex Desktop routing is effective: a
default install wants no Reserve row and is never told about one.

Also documents codexAccountPickerEnabled and codexAccountNamespaces, which
were absent from the configuration reference entirely.
@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 17, 2026 22:27
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 17, 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-17T22:30:37.401180Z c6dde0c 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.

@coderabbitai

coderabbitai Bot commented Sep 17, 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: 791b7cd8-9bb5-4ead-845b-0a8bf3471876

📥 Commits

Reviewing files that changed from the base of the PR and between 61ee647 and c6dde0c.

📒 Files selected for processing (6)
  • docs-site/src/content/docs/reference/configuration/server.md
  • src/codex/catalog/reserve-warn.ts
  • src/codex/catalog/retained-sync.ts
  • src/codex/catalog/sync.ts
  • structure/catalog.md
  • tests/codex-integration/reserve-catalog.test.ts

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


📝 Walkthrough

Walkthrough

The change adds reason classification and warn-once diagnostics for omitted Luna Reserve catalog rows. Catalog sync invokes the diagnostics, exports the new API, adds coverage for each suppression case, and documents the related configuration fields and behavior.

Changes

Luna Reserve suppression diagnostics

Layer / File(s) Summary
Suppression classification and warning state
src/codex/catalog/reserve-warn.ts
Defines four suppression reasons. It returns no reason when authless routing is ineffective or a main-account selector exists. It emits one warning per reason and provides a test reset function.
Catalog sync integration and validation
src/codex/catalog/retained-sync.ts, src/codex/catalog/sync.ts, tests/codex-integration/reserve-catalog.test.ts
Retained catalog writes compute the suppression reason and emit a warning. The catalog sync surface re-exports the diagnostic API and types. Tests cover reason precedence, opt-in gating, main selectors, and warn-once behavior.
Configuration and catalog documentation
docs-site/src/content/docs/reference/configuration/server.md, structure/catalog.md
Documents codexAccountPickerEnabled, codexAccountNamespaces, Reserve row suppression, warning causes, and restoration actions.

Priority: ➖ Normal

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

Change: Bug fix · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant CatalogSync
  participant ReserveWarn
  participant Console
  CatalogSync->>ReserveWarn: classify omitted Reserve row
  ReserveWarn-->>CatalogSync: suppression reason
  CatalogSync->>ReserveWarn: warn once for reason
  ReserveWarn->>Console: write cause and restoration action
Loading

Merge Risk: ⚪ Minimal · up to c6dde

The diagnostic is wired to the retained catalog sync path and uses the same state that controls Reserve-row generation. The change is ready to merge.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 4 files. (2 skipped: 2… 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 describes the main change: adding a sync-log explanation for a suppressed Luna Reserve catalog row.
Linked Issues check ✅ Passed Issue #4811 permits either an independent Reserve row or a visible diagnostic. This PR implements the diagnostic path. src/codex/catalog/reserve-warn.ts limits diagnostics to effective authless Code…
Out of Scope Changes check ✅ Passed The reviewed changes stay within issue #4811. The new warning module, retained-sync integration, related exports, configuration documentation, catalog documentation, and regression tests all support d…
Full details: Docstring Coverage

Explanation

Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 4 files. (2 skipped: 2 unsupported.)

✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • 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는 #4811을 닫기 위한 진단 로그입니다. 지금 dev HEAD(61ee64747, tip #4948 CLI cold-status 측정) 위에서 Luna Reserve 카탈로그 행은 createReserveCatalogProjection이 만듭니다. 그 함수는 src/codex/catalog/reserve.ts 25행에서 isEffectiveCodexDesktopAuthless(config)가 참이고 mainSelectors가 비어 있지 않을 때만 행을 씁니다. 슬러그는 <selector>/gpt-reserve처럼 계정에 묶인 형태라서, 메인 계정을 가리키는 selector가 없으면 행 자체가 생기지 않습니다.

selector는 retained-sync.ts에서 visibleCodexAccountSelectors로 모은 뒤 isMainCodexAccountTarget으로 거릅니다. 그 selector 목록은 codexAccountPickerEnabled와 codexAccountNamespaces에 달려 있습니다. account-namespaces.ts 190–195행의 codexAccountPickerEnabled()는 플래그가 꺼져 있지 않으면서 네임스페이스 맵이 비어 있지 않을 때만 참입니다. 방금 켠 codexDesktopAuthless: true 루프백 설치는 보통 맵이 비어 있어서, 운영자가 기대한 Reserve 행이 조용히 빠집니다. 빠진 행에는 이유를 붙일 칸이 없습니다. 그래서 #4212 계정 게이트 네이티브와 같은 답을 씁니다. 입력이 아직 보이는 writeRetainedCatalogSync 자리에서 warn-once 한 줄을 남깁니다.

새 파일 src/codex/catalog/reserve-warn.ts는 gated-native-warn.ts와 같은 모양입니다. reserveCatalogSuppressionReason이 네 가지 원인을 나눕니다. 캐노니컬 OpenAI 프로바이더 없음, picker 명시적 끔, 네임스페이스 맵 비어 있음, 맵은 있는데 메인 계정 selector가 없음. 순서가 중요합니다. picker를 끈 사람에게 “맵을 손수 채워라”고 보내면 잘못된 수리 경로가 됩니다. 그래서 account-picker-disabled를 빈 맵보다 먼저 검사합니다. 경고 문구는 원인과 복구 행동을 같이 말합니다. 기본 설치(codexDesktopAuthless 미설정)나 효과 없는 바인드에 저장된 플래그(inertReason)는 여기 반복하지 않습니다. #3844처럼 계정 자격 없이 bare gpt-reserve를 푸는 능력 변경도 하지 않습니다. 이슈가 원한 것은 진단과 문서였고, 그 경계를 지킵니다.

설정 참고에 없던 codexAccountPickerEnabled와 codexAccountNamespaces를 docs-site/.../server.md에 넣었고, Reserve가 계정 자격에 묶인다는 결과를 각 키에 적었습니다. structure/catalog.md의 Accounts/namespaces 절에도 warn-once 계약을 적었습니다. src/codex/는 structure/manifest.json상 이 문서 소유라서 structure 게이트와 맞습니다. 테스트는 reserve-catalog.test.ts에 일곱 케이스를 더했습니다. 각 원인, 억제 없음, 미옵트인 침묵, warn-once입니다. 로컬 스위트는 이 레인 금지라 hosted CI가 검증입니다. 지금 체크는 대부분 pending입니다.

라인 48 - reserveCatalogSuppressionReason이 mainSelectors.length > 0이면 바로 undefined를 돌려줍니다. 그런데 createReserveCatalogProjection은 selector가 있어도 observedSource와 lunaSource가 둘 다 없으면 27행에서 또 undefined를 냅니다. 그 “메타데이터 소스 없음” 침묵은 #4811 범위 밖이라 의도일 수 있지만, 운영자 입장에서는 Reserve가 또 안 보일 수 있습니다.
라인 52 - picker false를 빈 맵보다 먼저 검사하는 순서는 테스트로 잠겼고 수리 경로가 맞습니다. 다만 codexAccountPickerEnabled === false이면서 손수 채운 비어 있지 않은 맵이 있으면, 실제 visibleCodexAccountSelectors 동작과 이 reason 문자열이 어긋날 여지가 있는지 한 번만 대조하면 좋습니다.
reserve-warn.ts / warnedReserveSuppression - 프로세스 전역 Set warn-once는 gated-native-warn과 동일합니다. 워커가 여러 개면 프로세스마다 한 번씩 찍힐 수 있습니다. 기존 패턴을 따른 것이라 새 위험은 작습니다.
retained-sync.ts 호출 위치 - reserveMainSelectors 계산 직후, createReserveCatalogProjection 호출 전에 경고합니다. selector 입력이 아직 스코프에 있어 #4811이 원한 자리입니다.
docs-site 두 키 - 기본값이 unset이고 Reserve 결과를 각 행에 적어 의존성이 더 이상 숨지 않습니다. 참고 문서만으로 복구 경로가 보입니다.

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

  • hosted CI(특히 tests/codex-integration/reserve-catalog.test.ts와 structure/typecheck)가 초록이 된 뒤 바로 merge할지, 아니면 CI만 보고 바로 랜딩할지
  • selector는 있는데 Luna/observed 소스가 없어 projection이 또 빠지는 경우를 같은 warn 계열로 확장할지, Luna Reserve catalog row is silently suppressed unless the undocumented account-picker map is populated #4811 밖으로 두고 이 PR만 닫을지
  • #4811을 Closes로 닫을 때, “행을 무조건 보이게”가 아니라 “왜 안 보이는지 알려 주기”로 이슈 기대치를 이 PR 경계에 맞춘 것으로 확정할지

너의 추천
CI 초록 확인 후 merge하세요. #4811은 이 진단+문서 범위로 닫는 것이 맞고, #3844 능력 변경은 열지 마세요. types/config 분할에 무효화되는 변경도 아닙니다.

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

@github-actions

Copy link
Copy Markdown
Contributor

✅ Deterministic PR hygiene checks passed.

@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: c6dde0cb44

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

config: ReserveSuppressionConfig,
input: ReserveSuppressionInput,
): ReserveCatalogSuppressionReason | undefined {
if (!isEffectiveCodexDesktopAuthless(config) || input.mainSelectors.length > 0) return undefined;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Suppress warnings when Reserve is explicitly disabled

When disabledModels contains the bare gpt-reserve slug, the final catalog policy intentionally hides every account-qualified Reserve row, but this predicate ignores that setting. An authless installation without a main selector therefore logs instructions to restore a row the operator explicitly disabled, and following the instructions only creates a hidden row. Pass the disabled-model state into this check and omit the warning when bare Reserve is disabled.

Useful? React with 👍 / 👎.

@github-actions github-actions Bot added the bug Something isn't working label Sep 17, 2026
The facade re-export pushed src/codex/catalog/sync.ts from 52 to 58 lines,
past its file-size ratchet cap in tests/fixtures/file-size-baseline.json.
The ratchet only moves downward by design, so the cap cannot be raised.

The re-export was never load-bearing: retained-sync.ts imports the warning
directly from ./reserve-warn, and the reserve tests already import the
projection helpers from ./reserve rather than through the facade.
@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.

@lidge-jun
lidge-jun merged commit c0e0401 into dev Sep 17, 2026
27 checks passed
@lidge-jun
lidge-jun deleted the codex/4811-reserve-suppression-diagnostic branch September 17, 2026 23:41
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