Repository navigation
Codex Desktop reasoning level resets from xhigh/high to low in the same active thread after delegations/continuations #26930
Description
What version of the Codex App are you using (From “About Codex” dialog)?
Codex App 26.602.40724 (CFBundleVersion 3593) from /Applications/Codex.app.
What subscription do you have?
ChatGPT Pro subscription, USD 200/month.
What platform is your computer?
Darwin 25.5.0 arm64 arm
What issue are you seeing?
In Codex Desktop, the reasoning/thinking level keeps resetting to low inside the same active thread, even after I manually set it to high / xhigh.
This is not only a new-chat/default-setting issue. It happens during an ongoing operational thread with active delegated Codex threads/workers. I set the level to extremely high, continue working, and shortly after a delegation/update/continuation it shows as low again without me changing it.
Why this matters: I use Codex as an operational coordinator across multiple live project threads. Some tasks involve production systems, SQL/Supabase, Vercel/env, auth/secrets, RRHH/payroll, cash/stock/sales/accounting, and push/main decisions. For these workflows, reasoning effort must not silently drop to low, because the user may believe the thread is still running with high/xhigh reasoning.
What steps can reproduce the bug?
- Open a Codex Desktop thread.
- Set reasoning/thinking level to
xhighorhigh. - Use the thread as a coordinator and send/receive
codex_delegationmessages to/from other active Codex threads. - Continue working while background captain/worker threads are active.
- Observe that the same thread’s reasoning level changes back to
lowwithout the user selectinglow.
Active thread IDs at time of issue:
Current/active coordination thread:
019ea4f4-7990-7e32-a582-8ae8c3640303—Coordinar capitanía general
Active captain/worker threads involved:
019e8074-ed13-7193-be78-eae91235c9f9—Capitán - Avícola del Sur019ea4ea-e759-7290-9579-11f5b206913b—Worker - Avícola producción viva QA integral no-RRHH019e8081-6810-70d2-a27e-40d8c5697024—Capitán - Turilo ERP019ea4ea-fa86-7af0-b0ee-bca2cf822480—Worker - Turilo producción viva entrega mañana
Related/historical CAPITAN GENERAL thread used by delegations:
019e7c7d-b636-7fe0-98a8-e20dab577fb1—CAPITAN GENERAL
What is the expected behavior?
If I manually set reasoning to xhigh or high in an active Codex Desktop thread, it should remain at that level unless I explicitly change it.
At minimum, Codex should not silently reset the visible/current reasoning effort to low during the same thread after:
- thread continuation,
- context compaction,
- receiving
codex_delegationmessages, - reading/sending messages to background threads,
- active worker/captain updates,
- or returning from tool activity.
Additional information
This may be related to thread rehydration, continuation, context compaction, or delegation handling resetting the active mode settings.
This is similar in spirit to existing reasoning-effort persistence issues, but the observed bug is specifically:
- Codex Desktop
- same active thread
- user manually set high/xhigh
- resets to low later without user action
- during delegation/background-thread workflows
Related issues:
- Codex CLI resets
model_reasoning_effortto Medium (with proof) #12042 — Codex CLI resetsmodel_reasoning_effort - Codex Desktop new-chat reasoning selector does not apply to initial prompt #21093 — Codex Desktop new-chat reasoning selector does not apply to initial prompt
- Codex app automations run at medium reasoning even when global model_reasoning_effort is xhigh #13536 — Codex app automations run at medium reasoning even when global effort is xhigh
- resume_agent restores subagent history but not original model/reasoning settings #26718 — resume_agent restores subagent history but not original model/reasoning settings
- Codex iOS remote task on Windows 26.527 ignores xhigh and starts gpt-5.5 medium #25583 — Codex iOS remote task on Windows 26.527 ignores xhigh and starts gpt-5.5 medium
Activity
github-actions commentedon Jun 8, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Jackten commentedon Jun 11, 2026
I found a narrower app-server-level reproduction that may explain at least one class of the reasoning-effort resets described here.
Environment:
26.608.12217(CFBundleVersion3722)codex-cli 0.138.0-alpha.7Darwin 25.5.0 arm64 armObserved path:
clientInfo.name = "Codex Desktop".thread/settings/update, for examplehigh.turn/startfor the same thread where the request still carries a staleeffort, for examplelow.turn/start.effortis treated as a thread-settings override, so the next Responses request usesloweven though the user did not explicitly change the setting back.Expected behavior:
thread/settings/update.turn/startshould not silently overwrite the saved thread effort with stale composer state.I pushed a small fork branch with a regression test and candidate app-server fix here:
https://github.com/Jackten/codex/tree/per-thread-reasoning-effort
The test added is
codex_desktop_turn_start_effort_does_not_override_thread_settings, and it verifies that afterthread/settings/updatesets effort tohigh, a later Codex Desktopturn/startcarrying stalelowstill sendsreasoning.effort = highto Responses.Local validation I ran:
just fmtjust test -p codex-app-server(842passed,1skipped)just fix -p codex-app-servercodex-cliI did not open a PR because
docs/contributing.mdsays unsolicited external PRs will be closed without review. If this root cause matches the intended behavior, I can open a PR or adjust the branch if invited.ghitafilali commentedon Jun 20, 2026
I can reproduce a closely related reset on current Codex Desktop, and the local state suggests there may be two separate persistence paths involved.
Environment:
26.616.32156(CFBundleVersion4157)codex-cli 0.142.0-alpha.1User config already forces xhigh:
Observed local state:
high:high=780,xhigh=52,low=4,medium=1.reasoning_effort='high':019e7d97-2e06-7e11-8d9a-447a11d5c390($do .../issues/364) ->high019e7fde-ecdb-7511-a367-0c66f1d95383($do .../issues/362) ->high2026-06-19 21:23:01thread019e7d97-...:op: ThreadSettings { ... model: Some("gpt-5.5"), effort: Some(Some(XHigh)) ... }2026-06-19 21:22:56thread019e7fde-...:op: ThreadSettings { ... model: Some("gpt-5.5"), effort: Some(Some(XHigh)) ... }codex.turn.reasoning_effort=highcodex.request.reasoning_effort=highThreadSettingsupdate for the same thread seteffort: Some(Some(XHigh)), but the persisted thread row remainedhigh.Source-level observations from current
main(64bdeed9f7adbe60c725153b3fb74ed044a36221):app-server/src/request_processors/turn_processor.rspassesparams.effortfromturn/startdirectly intobuild_thread_settings_overrides, so a stale Desktopturn/start.effortcan still override an intentional setting. This matches the narrower reproduction in the previous comment.app-server/src/bespoke_event_handling.rshandlesEventMsg::ThreadSettingsAppliedby emittingthread/settings/updated, but I do not see it patching thread metadata.reasoning_effortfrom turn context/history:thread-store/src/thread_metadata_sync.rs:update.reasoning_effort = turn_ctx.effort.clone()state/src/extract.rs:metadata.reasoning_effort = turn_ctx.effort.clone()request_processors/thread_processor.rs::merge_persisted_resume_metadatamerges persistedmetadata.reasoning_effortback intomodel_reasoning_effort.That would explain the user-visible behavior:
ThreadSettings ... XHigh.highbecause it reflects last turn context, not the settings update.highis merged back into config and the session appears to have reverted.turn/startalso carries staleeffort: high, it can reinforce the downgrade.Expected behavior: an explicit per-thread UI setting to Extra High should persist across sidebar reloads, thread resume, wakeups/continuations, and future turns unless the user changes it again. A stale
turn/start.effortfrom the Desktop client should not lower an already-explicit thread setting, andthread/settings/update/ThreadSettingsAppliedshould update the durable thread metadata or resume should avoid treating last-observed turn effort as the authoritative current thread setting.Potential fix shape:
turn/startshould not let staleparams.effortoverride saved thread settings.ThreadSettingsApplied.thread_settings.reasoning_effortinto thread metadata, or distinguish "last observed turn effort" from "current explicit thread setting" so resume/list cannot resurrect the old value.ghitafilali commentedon Jun 20, 2026
Still reproducible on a newer Desktop build, with evidence that this is not just UI display state.
Environment:
26.616.41845(CFBundleVersion4198) from/Applications/Codex.appmodel_reasoning_effort = "xhigh"plan_mode_reasoning_effort = "xhigh"Additional observations from local state/logs:
~/.codex/state_5.sqlitewere still mostly persisted ashigh:high=781,xhigh=61,low=4,medium=1, blank=1.reasoning_effort='high'even after later explicit XHigh UI updates:019e7d97-2e06-7e11-8d9a-447a11d5c390(issue 364) washigh; logs show explicitThreadSettings ... effort: Some(Some(XHigh))at2026-06-19 21:22:23,2026-06-19 21:23:01, and2026-06-20 11:28:14.019e7fde-ecdb-7511-a367-0c66f1d95383(issue 362) washigh; logs show explicitThreadSettings ... effort: Some(Some(XHigh))at2026-06-19 21:22:28,2026-06-19 21:22:56, and2026-06-20 11:28:09.019ee549-215d-7750-85a3-a31e5d6b6208(issue 545) hadcodex.turn.reasoning_effort=highandcodex.request.reasoning_effort=highat2026-06-20 13:56:59, despite the user expectation that all active sessions are xhigh.I think there are two related persistence paths involved:
The stale Desktop
turn/start.effortoverride path described by @Jackten is real. In currentorigin/main,codex-rs/app-server/src/request_processors/turn_processor.rsstill passesparams.effortintobuild_thread_settings_overridesformethod: "turn/start";build_thread_settings_overridesthen treats any non-Noneeffort as a settings override. Jackten's candidate commit0f573314e6678e5b76b7af71b431831c992e16d3addresses this class by ignoringturn/start.effortforclientInfo.name == "Codex Desktop"and addscodex_desktop_turn_start_effort_does_not_override_thread_settings.There also appears to be a durable metadata gap after an explicit settings update.
EventMsg::ThreadSettingsAppliedincodex-rs/app-server/src/bespoke_event_handling.rsupdates in-memory thread state and emitsthread/settings/updated, but does not update the persistent thread metadata row. Separately,codex-rs/thread-store/src/thread_metadata_sync.rsrecordsTurnContext.effortintoThreadMetadataPatch.reasoning_effort, andcodex-rs/app-server/src/request_processors/thread_processor.rslater uses persistedmetadata.reasoning_effortto setmodel_reasoning_effortduring resume. That means a user can set XHigh successfully in the UI, but if the latest persisted turn context was High, resume/list rehydration can continue to present or restore High.Suggested fix shape:
turn/start.effortguard from Jackten's branch, adjusted for currentorigin/main.ThreadSettingsApplied, or better split "current explicit per-thread reasoning setting" from "latest observed turn reasoning effort" so rollout-derived metadata cannot overwrite the user's selected setting.thread/settings/updateto XHigh without a subsequent turn, then resumes/lists the thread and expects XHigh.Impact: this is user-visible and can silently lower actual request effort. The user believes a session is Extra High, but logs show some requests are sent as Medium, which is extremely frustrating.
ferueda commentedon Jul 14, 2026
Controlled reproduction:
send_message_to_threadcopies sender effort into recipient callback turnI can reproduce the delegation-related reasoning reset described here with a fresh, read-only A/B test. The result is not only UI state: the recipient's rollout records the changed
thread_settings_appliedevent and the callback turn actually runs at the sender's effort.Environment
26.707.71524(bundle5263)codex-cli 0.144.2Darwin 25.5.0 arm64 armgpt-5.6-solTest protocol
max, let its initial no-op turn complete, and leave it idle.codex_delegation.No thread inspected or modified repository files. No user changed either parent's settings between turns.
maxmediummediummaxmaxmaxReproduction evidence
Disposable sessions:
019f5f05-1ea5-7ee3-acac-4dffdcd36ec7019f5f05-508a-7b03-8aa3-884e9f9deecdSanitized parent event sequence:
Control evidence
Disposable sessions:
019f5f06-1e51-7601-9e43-68f8cc9a136c019f5f07-04c9-7923-984b-a53f5d6cca59This isolates sender effort as the changing variable. It also rules out a permission-profile transition in this reproduction: both callback settings events had no active permission profile.
Expected
Cross-thread message delivery should not mutate the recipient thread's model or reasoning effort when
send_message_to_threadomits model/thinking overrides. The max parent should process the callback at max unless the user explicitly changes that parent.Actual
The callback turn's effort matches the sending child. A medium executor can therefore silently downgrade a max orchestrator, and a different sender effort can silently alter an otherwise pinned recipient.
One additional contract clue: tool guidance exposed to different threads was inconsistent—one description said omitted settings preserve the target thread, while another said they inherit the calling thread's compatible settings. Runtime behavior in this test matches sender inheritance. This looks like sender settings being carried into the destination turn rather than content-only delivery.
I originally noticed the issue when a long-running max orchestrator changed to medium immediately after a medium executor checkpoint. The disposable reproduction above confirms the trigger without relying on that private operational thread. Full rollouts are intentionally not attached because they contain unrelated local context; the event sequence and disposable session IDs above should allow internal inspection.
psychananaz commentedon Jul 28, 2026
is there a single human person in this thread at all, lmao.
Jackten commentedon Jul 28, 2026
kyleAmadeous commentedon Jul 31, 2026
I traced a separate, deterministic
thread/settings/updatewriter that matches the reported parent-thread downgrade and is still present on currentmain(4642370542739d5dd080b0c87a9de06a6435d3db).Evidence
The affected rollout was produced by
originator=codex-tui,cli_version=0.144.1. A parent runninggpt-5.6-sol/xhighrecorded this sequence during an already-active turn:The next explicit turn then started as Luna/low. The two settings events being 2 ms apart is significant: the TUI emits a model-only update first (preserving xhigh), followed by an effort-only low update.
Root writer
On
rust-v0.144.1, the first writer is the stale safety-buffer retry branch incodex-rs/tui/src/app/event_dispatch.rs(lines 340-356). On currentmain, the same logic lives incodex-rs/tui/src/app/safety_buffering.rs:modelis the one-off safety-bufferingfasterModel(Luna in the reproduction), whilelowis hard-coded by the TUI. If agent output starts before the user accepts the retry, the retry becomes stale, but this branch turns those one-off retry settings into global UI setting events.The exact path is:
A subsequent
turn/startthen reuses the contaminated local TUI settings. This is distinct from the sender-effort delegation path and from the stale Desktopturn/start.effortpath discussed above.Candidate fix and regression
I prepared an atomic fix that makes a stale retry a no-op instead of applying its one-off model/effort to the parent. Normal, still-valid retries continue to fork using the faster model and
loweffort.Branch: https://github.com/kyleAmadeous/codex/tree/fix/kyle/tui-parent-settings-safety-retry
Commit: kyleAmadeous@666b6cc
The regression configures the parent as Sol/xhigh and the stale retry as Luna/low, then verifies that no persistent
UpdateModelorUpdateReasoningEffortevent is emitted and the parent remains Sol/xhigh.Pre-fix, the new test failed with:
Post-fix validation:
just test -p codex-tui: 3304 passed, 4 skippedjust fix -p codex-tuijust fmtgit diff --checkPer
docs/contributing.md, I have not opened an unsolicited PR. If a maintainer confirms this approach and invites the contribution, I can open the PR immediately from the prepared branch.naipi11 commentedon Aug 19, 2026
Until the thread setting is preserved correctly, the safest manual guard is to treat the composer value as a per-turn setting:
Verification: the next turn should start with the intended effort in the UI/turn metadata, if metadata is available. Limitation: this does not repair persistence, and a turn that already started at the lower effort cannot be upgraded retroactively.
przadka commentedon Sep 1, 2026
Linux CLI 0.149.0 has a deterministic automatic-goal-continuation variant of this reset.
The same process and rollout started with
-c model_reasoning_effort=ultra. Its first fourturn_contextrecords wereultra. After the first goal turn completed, the automatic continuation emitted:All 19 later turn contexts recorded
xhigh. There was no process restart or user model-selection event at that boundary.Expected: an automatic goal continuation preserves the thread's effective CLI override unless the user explicitly changes it. For a thread launched with
model_reasoning_effort=ultra, the continuation should not apply the globalxhighsetting.