Repository navigation
Description
Summary
review and adversarial-review silently ignore reasoning effort. --effort is not a recognized flag on either command, and adversarial-review omits effort when calling runAppServerTurn — even though the underlying turn/start request already accepts it and task threads it through correctly.
The net effect: the review commands always run at whatever model_reasoning_effort sits in ~/.codex/config.toml, and there is no way to raise effort for a review specifically. For users who treat the adversarial review as their pre-merge quality gate, this is the one place extra reasoning is most worth paying for, and it's the one place they can't buy it.
This is also silent — no "unknown flag" error. ... adversarial-review --effort xhigh is accepted and runs at the config default, which reads as if it worked.
Evidence (v1.0.6)
--model is threaded end-to-end; --effort is missing at every touchpoint on this path.
1. --effort is not parsed — scripts/codex-companion.mjs:714
async function handleReviewCommand(argv, config) {
const { options, positionals } = parseCommandInput(argv, {
valueOptions: ["base", "scope", "model", "cwd"], // <-- no "effort"
booleanOptions: ["json", "background", "wait"],
aliasMap: { m: "model" }
});Compare handleTask (:764), which has valueOptions: ["model", "effort", "cwd", "prompt-file"] and normalizes via normalizeReasoningEffort.
2. The call site doesn't forward it — scripts/codex-companion.mjs:742
executeReviewRun({
cwd,
base: options.base,
scope: options.scope,
model: options.model, // <-- no effort
...
})3. adversarial-review drops it into a call that supports it — scripts/codex-companion.mjs:411
const result = await runAppServerTurn(context.repoRoot, {
prompt,
model: request.model,
sandbox: "read-only", // <-- no effort
outputSchema: readOutputSchema(REVIEW_SCHEMA),
onProgress: request.onProgress
});…but runAppServerTurn (scripts/lib/codex.mjs:1139) already forwards effort to turn/start:
client.request("turn/start", {
threadId,
input: buildTurnInput(prompt),
model: options.model ?? null,
effort: options.effort ?? null, // always null on the review path
outputSchema: options.outputSchema ?? null
});So effort arrives as null and the server falls back to the config default.
Two different problems
adversarial-review — straightforward fix. It already routes through runAppServerTurn → turn/start, which accepts effort. Adding "effort" to valueOptions, normalizing it with the existing normalizeReasoningEffort, and passing it through the two call sites should be sufficient.
review (native) — needs a maintainer call. It goes through runAppServerReview → review/start, whose params are { threadId, delivery, target } — no effort field. Model is applied at startThread, but buildThreadParams (lib/codex.mjs:63) doesn't carry effort either. Does thread/start accept a reasoning effort upstream? If so, the fix is symmetric with model. If not, the limitation is worth documenting, because right now it's indistinguishable from a bug.
Repro
node scripts/codex-companion.mjs adversarial-review --wait --scope branch --effort xhighExpected: flag rejected, or the run honours xhigh.
Actual: flag silently ignored; the run uses model_reasoning_effort from ~/.codex/config.toml.
Why the usual workarounds don't apply
- No effort-suffixed model slugs.
models_cache.jsonlistsgpt-5.6-sol/-terra/-luna,gpt-5.5,gpt-5.4(-mini),gpt-5.3-codex-spark,codex-auto-review— none encode an effort level, so--modelcan't stand in for--effort. - A
[profiles.reviewer]block doesn't work. The companion drives the app-server protocol directly and never passes--profileor reads a profile name, so a reviewer profile is inert config. (This is the same root cause as feat: allow selecting a Codex profile for companion-launched jobs #251, which asks for profile propagation.)
That leaves editing the global model_reasoning_effort as the only lever — which also changes effort for task runs and for the user's interactive Codex sessions, since it's the same file.
Related
- Feature: support default model and effort settings in /codex:setup #44 — asks to persist default model/effort via
/codex:setup. Its premise is that these flags exist per-command today ("have to repeat these flags every time they run/codex:rescue,/codex:review, or/codex:adversarial-review") — for the two review commands, they don't. Fixing this issue is arguably a prerequisite. - feat: allow selecting a Codex profile for companion-launched jobs #251 — profile selection for companion-launched jobs; overlapping root cause, different ask. Independently notes that "a dedicated review profile with a different reasoning effort" is currently impossible.
Happy to send a PR for the adversarial-review half if that framing is agreeable.
Environment
- plugin
codex@openai-codexv1.0.6 codex-cli0.144.1- macOS 25.5.0, Node via npm global
- auth: ChatGPT account
Activity
axisrow commentedon Jul 11, 2026
Confirming this for plugin v1.0.6 / codex-cli 0.144.1, with the extra bit that even if you do plumb
--effortinto the parser and forward it, nativereviewcan never honor it — and the reason is worth pinning down so the fix scope is clear.I went a level deeper than the parser and probed the protocol/server directly:
1.
ReviewStartParamshas nomodel/effortfield. The generated type is{ threadId, target, delivery? }(plugins/codex/.generated/app-server-types/v2/ReviewStartParams.ts).effortonly exists onTurnStartParams. Assigning the literal to the typed params fails type-checking:TS2353: 'model' does not exist in type 'ReviewStartParams'. So for nativereviewthere is simply no field to puteffortin — it's not a plumbing gap, it's absent from the protocol.2. The real app-server silently drops unknown fields. Direct JSON-RPC probe against
codex app-server --stdio(0.144.1):review/startwithoutmodel/effort→ accepted (1 ms).review/startwithmodel+effort→ also accepted (1 ms), no validation error.The Rust side deserializes without
#[serde(deny_unknown_fields)], so unknown fields are ignored rather than rejected. That's exactly why the "silent" behavior in the issue is silent at the RPC layer too — not just at the parser. Adding--effortto the parser + forwarding it (which is what a naive fix does) still results in the field being thrown away server-side for native review.3. The
model-symmetric fix forefforton native review is blocked by #408. The model path isn't actually working either:modelinthread/start/turn/startdoes not select the model — the app-server boots with the CLI's built-in default and the only effective override is-c model="…"at spawn time (#408). So "thread/start accepts model, do the same for effort" doesn't hold up under testing; even--modelon review/task isn't honored today.Scoping implication:
adversarial-revieweffort is fixable here: it routes throughrunAppServerTurn→turn/start, whose params includeeffort. AddingefforttovalueOptions+ forwarding it throughexecuteReviewRun(per the issue) is the right half-fix.review(native) effort is not fixable on the plugin side until/unless the protocol gains an effort field onreview/start(or the model/effort override moves to the-cspawn surface from fix(app-server): pass-c model="..."tocodex app-serverso options.model takes effect #408, applied to both).modelspecifically, the real fix is fix(app-server): pass-c model="..."tocodex app-serverso options.model takes effect #408 regardless of which command — not the RPC field.For transparency: I'd opened #477 to forward
model/efforttoreview/start, and I'm closing it as the wrong fix after this check — the fields are inert on the server. Leaving this here in case it saves the next person the same round trip.axisrow commentedon Jul 12, 2026
Following up on my earlier comment here with two updates after testing the
thread/start.configroute (the approach #471 takes): the effort does reach the model that way, and it surfaces a second, related gap worth linking.Update to my "native review can never honor effort" claim: I've now verified the
thread/startconfig: { model_reasoning_effort: "high" }route end-to-end against codex-cli 0.144.1. Running a realadversarial-review --effort high(via the plugin companion, fork with--effortwired into spawn-c model_reasoning_effort=) and then reading back the Codex rollout, the review'sturn_contextentries carryreasoning_effort: "high"— so the override propagates to the actual model turn. That means #471's mechanism (per-thread config, not a dead top-level field) is the viable path, not thereview/startfield I tried in #477. My "blocked by #408" framing was too pessimistic foreffortspecifically — #408 is about the boot-defaultmodel, which is a separate axis.Related gap — #481 (job records never capture resolved model/effort): the reason it took real work to prove the override took effect is exactly what #481 describes. The plugin's
jobs/<id>.jsonstores no resolvedmodel/effort(onlythreadId/turnId/status), and the only source of truth is the Codex rolloutturn_context— which is painful to join: my review ran on2026-07-12(jobcreatedAtin UTC) but its rollout landed in the2026/07/11/date dir under a session-id that doesn't match the job'sthreadId.ccusagedidn't surface the session at all for the same reason. So #476 (effort ignored) and #481 (effort invisible even when applied) are two faces of the same invisibility: once the override is wired in (#471's route), #481 is what lets anyone confirm it actually ran.Net: #471's
thread/start.configapproach fixes the--efforthalf of this issue; #481 is the observability piece needed to trust it. Worth linking the three.