Repository navigation
design codex config
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).
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).
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 fromModelProviderInfo; 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_metadatais a validated string map, not a body passthrough). -
model_catalog_json— REJECTED for now: the catalog REPLACES codex's whole model metadata, andModelInfodeserialization REQUIRESbase_instructions(ormodel_messages.instructions_template) per entry —get_model_instructionsreturns 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.
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.
applyCodexConfig extends the W3 writer:
-
Types:
mergeTomlRootKeysnow 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
nullvalue 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_windowfalls back to the #51modelEntriesseed 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.
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
/modelpicker 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
1000000and auto-fills900000for 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.
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.