Skip to content

design codex config

sinrimin edited this page Oct 20, 2026 · 1 revision

design-codex-config.md — codex dedicated runtime-config panel (#63)

Third per-target panel, the #60/#62 pattern applied to codex. Durable companions: design-phase-9-harness-runtime.md (§4.3 the W3 writer this extends), research-provider-model-params.md (§3.4 codex ground truth + §5 follow-up tiers), design-claude-code-config.md / design-opencode-config.md (the pattern's first two instances).

Division of the form

The provider row keeps ONLY the connection (provider / credential / base URL). The codex panel owns everything model-shaped plus codex's own knobs. The generic default-model grid and extra-models rows are hidden for target === 'codex' — the panel renders its own (same interaction as the other two panels).

Ground truth (codex-cli 0.154.0 on the rig; verified 2026-10-16)

Source at tag rust-v0.154.0 (codex-rs/config/src/config_toml.rs, protocol/src/{openai_models,config_types}.rs) + the official config reference. Keys unchanged through 0.162.1 (research §3.4).

Exposed as panel knobs — all TOP-LEVEL ~/.codex/config.toml keys, single global value per install:

spec field config.toml key values
reasoningEffort model_reasoning_effort minimal | low | medium | high | xhigh
reasoningSummary model_reasoning_summary auto | concise | detailed | none
verbosity model_verbosity low | medium | high
contextWindow model_context_window int (i64)
autoCompactTokenLimit model_auto_compact_token_limit int (i64)
switches.updateCheckDisabled check_for_update_on_startup = false bool

Deliberately NOT exposed (source-verified):

  • model_max_output_tokens — the key never existed (zero occurrences in the repo); serde does not deny unknown fields, so writing it would be a silently-ignored dead key.
  • disable_response_storage — REMOVED from codex (absent from 0.154's root config AND from ModelProviderInfo; absent from the current official config reference). Notably cc-switch's codex template still writes it at root — a dead key on current codex; we do not repeat it.
  • temperature / top_p / arbitrary body merge — no surface anywhere in codex config (responses_api_metadata is a validated string map, not a body passthrough).
  • model_catalog_json — REJECTED for now: the catalog REPLACES codex's whole model metadata, and ModelInfo deserialization REQUIRES base_instructions (or model_messages.instructions_template) per entry — get_model_instructions returns the template verbatim with no built-in fallback (no template → empty instructions + a warn log). Owning the catalog means owning the SYSTEM PROMPT, fragile across versions. Stays a research §5 design question; the switchable extras keep riding the W13 daemon-side ACP picker rewrite.
  • model_auto_compact_token_limit_scope, plan_mode_reasoning_effort, profiles — exist but niche; out of scope for the panel.

The wire

spec.codex — additive optional field on runtimeConfigSpecSchema (codexConfigSchema), zod-validated BOTH ends; old daemons strip the unknown field, so the wire stays compatible. Enum consts (CODEX_REASONING_EFFORTS / CODEX_REASONING_SUMMARIES / CODEX_VERBOSITIES) live in shared, re-exported by the sdk, and are the panel selects' single source. The full four-spot wiring (#53 lesson): core CodexConfigData mirror, service row construction (codex: spec.codex ?? null), route toView passthrough, sdk re-export.

Writer semantics

applyCodexConfig extends the W3 writer:

  • Types: mergeTomlRootKeys now renders TOML-native values — strings JSON-quoted (valid TOML basic strings), integers and booleans BARE. A quoted "1000000" is a TOML string, not an i64 — codex would reject or misparse it; the cli tests pin the bare rendering.
  • Revert (unset-REMOVES-ours): a null value in the map REMOVES the key's line. Every knob is platform-owned while a spec exists — configured writes ours, cleared removes ours (the claude-code contract, NOT opencode's fall-back contract; codex has a sane built-in default for every dial, so removal is a real revert).
  • The one layered exception: model_context_window falls back to the #51 modelEntries seed of the DEFAULT model before removing — a real registry window beats codex's 272k assumption for unknown slugs (research §5 Tier 1, now shipped). An untouched panel with a seeded registry row therefore WRITES the window; panel value beats the seed.
  • Idempotent and merge-preserving as always: user comments, profiles, [mcp_servers.*] sections and their keys survive byte-for-byte.

Rig-verified end to end (survey-box, 2026-10-16): filled panel → PUT → spec.codex persisted → apply-config job → on-disk TOML with quoted strings + bare ints/bools, 0600, user block intact → reload prefill round-trip exact → clear-all re-apply returned config.toml to its byte-identical baseline (md5 match) with spec.codex → null.

Panel UX

Sections mirror the sibling panels (codex-panel.tsx, i18n cx* keys, en/zh side by side):

  • 模型 — default model (registry select when the provider has chat models, free-text otherwise) + the extra-models rows. Hint: extras switch in PLATFORM chat sessions; codex's own terminal /model picker keeps its built-in catalog (the honest boundary until catalog adoption).
  • 推理与输出 — three dial selects, each with a「codex 默认」unset option (Radix needs a non-empty value → __default__ sentinel mapped back to unset). Wire values stay English/mono in both locales.
  • 上下文 — the 1M quick-set checkbox (cc-switch's interaction): checking sets 1000000 and auto-fills 900000 for the compaction threshold when empty; unchecking clears both. The checkbox is DERIVED (contextWindow === '1000000'), so hand-editing the number stays primary. Two numeric inputs with token hints.
  • 行为 — the update-check switch.

Numeric inputs keep STRING state while typing (the #51 form idiom); cxIssue gates apply on integer bounds (1–10,000,000, matching the zod max); serializeCxDraft yields undefined for an all-default draft, so untouched rows keep their pre-#63 shape byte-identically.

Rig note (test-rig.md)

The rig daemon answers apply jobs with the code it was STARTED with — during #63 verification the first apply "succeeded" against the OLD daemon (byte-identical output masked the missing knobs). The dist overlay + daemon restart recipe in docs/dev/test-rig.md is REQUIRED before any on-disk verification of a new writer.

Clone this wiki locally