- Codex footer model detection (c28): the modelDetect.screenLine effort
alternation is now built from CODEX_REASONING_EFFORTS plus 'default', so
'ultra' (offered by the codexReasoningEffort App Setting and codex's own
/model picker) is read and the launch enum and the footer reader cannot
drift again. Still one capture group, 125 characters, no new quantifier.
New session-display-model case loops every effort level, ultra included.
- No CLI-id branching for launch defaults (c27): the two mode === 'codex'
branches the synced codex model/effort defaults added to the create and
quick-start routes are replaced by a registry capability,
capabilities.launchDefaults (launch param -> settings key, values from a
closed enum), declared on the codex entry only. The resolver moved from
web/codex-launch-defaults.ts to web/launch-defaults.ts as
applyLaunchDefaults(mode, configs, customEndpoint), filling the entry's
legacyConfigField object through legacyConfigAliases, still re-validating
with SettingsUpdateSchema and never overwriting a caller's value. The
route exclusions are unchanged (create: not remote; quick-start: not
remote, not Docker, not a custom model endpoint), and quick-start still
derives the session model from a bag without ompConfig, as before.
schema.ts refuses an undeclared param, an unknown settings key, an empty
map, and launchDefaults on an entry with no legacyConfigField.
- The no-id-branching guard now carries an exact occurrence count per
allowlisted key, so a new copy of an already approved expression fails
instead of riding the old approval, with a synthetic anti-vacuity case.
- SettingsUpdateSchema JSDoc (c21/c29): 'classic' is the tabArrangement
default and 'compact' the headerStatsStyle default, matching the
resolvers and the pre-paint script; state/case/ledger are marked opt-in.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Pin the App Settings codexModel guard to the schema. The 28df21f4 landing fix
added a client check in saveAppSettings() that copies the pattern of
SettingsUpdateSchema.codexModel, so one bad character no longer 400s the whole
.strict() settings PUT behind a "Settings saved" toast. Nothing tied the copy
to the schema: a looser copy would bring the silent 400 back, and a stricter
one would refuse valid model ids.
The new test extracts the client pattern from the saveAppSettings() body,
checks it agrees with the schema on eight samples (empty, dotted, slashed,
colon, space, semicolon, leading dash, non-ASCII), and asserts the guard runs
before the localStorage write. Length is left out on purpose, since the
input's maxlength="100" covers .max(100). Both a loosened pattern and a guard
moved after the write turn the test red.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>