Codex Desktop reasoning level resets from xhigh/high to low in the same active thread after delegations/continuations

Open 💬 10 comments Opened Jun 8, 2026 by jimmycarp966
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

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?

  1. Open a Codex Desktop thread.
  2. Set reasoning/thinking level to xhigh or high.
  3. Use the thread as a coordinator and send/receive codex_delegation messages to/from other active Codex threads.
  4. Continue working while background captain/worker threads are active.
  5. Observe that the same thread’s reasoning level changes back to low without the user selecting low.

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 Sur
  • 019ea4ea-e759-7290-9579-11f5b206913b — Worker - Avícola producción viva QA integral no-RRHH
  • 019e8081-6810-70d2-a27e-40d8c5697024 — Capitán - Turilo ERP
  • 019ea4ea-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_delegation messages,
  • 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:

  • #12042 — Codex CLI resets model_reasoning_effort
  • #21093 — Codex Desktop new-chat reasoning selector does not apply to initial prompt
  • #13536 — Codex app automations run at medium reasoning even when global effort is xhigh
  • #26718 — resume_agent restores subagent history but not original model/reasoning settings
  • #25583 — Codex iOS remote task on Windows 26.527 ignores xhigh and starts gpt-5.5 medium

View original on GitHub ↗

10 Comments

github-actions[bot] contributor · 2 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #26718
  • #25583

Powered by Codex Action

Jackten · 2 months ago

I found a narrower app-server-level reproduction that may explain at least one class of the reasoning-effort resets described here.

Environment:

  • Codex App 26.608.12217 (CFBundleVersion 3722)
  • Bundled CLI codex-cli 0.138.0-alpha.7
  • macOS Darwin 25.5.0 arm64 arm

Observed path:

  1. Initialize app-server as clientInfo.name = "Codex Desktop".
  2. Start or resume a thread.
  3. Set the thread effort intentionally with thread/settings/update, for example high.
  4. Send a later turn/start for the same thread where the request still carries a stale effort, for example low.
  5. The stale turn/start.effort is treated as a thread-settings override, so the next Responses request uses low even though the user did not explicitly change the setting back.

Expected behavior:

  • Explicit user picker changes should persist through thread/settings/update.
  • A normal Codex Desktop turn/start should 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 after thread/settings/update sets effort to high, a later Codex Desktop turn/start carrying stale low still sends reasoning.effort = high to Responses.

Local validation I ran:

  • just fmt
  • just test -p codex-app-server (842 passed, 1 skipped)
  • just fix -p codex-app-server
  • release build of codex-cli

I did not open a PR because docs/contributing.md says 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 · 2 months ago

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:

  • Codex Desktop 26.616.32156 (CFBundleVersion 4157)
  • Bundled CLI codex-cli 0.142.0-alpha.1
  • macOS app, ChatGPT auth

User config already forces xhigh:

model = "gpt-5.5"
model_reasoning_effort = "xhigh"
plan_mode_reasoning_effort = "xhigh"
service_tier = "priority"

Observed local state:

  • Active visible thread metadata is mostly persisted as high: high=780, xhigh=52, low=4, medium=1.
  • Two affected user threads that were explicitly changed in the UI to Extra High still have persisted metadata reasoning_effort='high':
  • 019e7d97-2e06-7e11-8d9a-447a11d5c390 ($do .../issues/364) -> high
  • 019e7fde-ecdb-7511-a367-0c66f1d95383 ($do .../issues/362) -> high
  • The app logs show the UI did send XHigh settings updates for those threads:
  • 2026-06-19 21:23:01 thread 019e7d97-...: op: ThreadSettings { ... model: Some("gpt-5.5"), effort: Some(Some(XHigh)) ... }
  • 2026-06-19 21:22:56 thread 019e7fde-...: op: ThreadSettings { ... model: Some("gpt-5.5"), effort: Some(Some(XHigh)) ... }
  • A related active thread had an actual later turn run as high:
  • codex.turn.reasoning_effort=high
  • codex.request.reasoning_effort=high
  • then a later ThreadSettings update for the same thread set effort: Some(Some(XHigh)), but the persisted thread row remained high.

Source-level observations from current main (64bdeed9f7adbe60c725153b3fb74ed044a36221):

  • app-server/src/request_processors/turn_processor.rs passes params.effort from turn/start directly into build_thread_settings_overrides, so a stale Desktop turn/start.effort can still override an intentional setting. This matches the narrower reproduction in the previous comment.
  • app-server/src/bespoke_event_handling.rs handles EventMsg::ThreadSettingsApplied by emitting thread/settings/updated, but I do not see it patching thread metadata.
  • Persisted metadata appears to get reasoning_effort from 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()
  • On resume, request_processors/thread_processor.rs::merge_persisted_resume_metadata merges persisted metadata.reasoning_effort back into model_reasoning_effort.

That would explain the user-visible behavior:

  1. User changes an existing Desktop thread from High to Extra High.
  2. Core session accepts ThreadSettings ... XHigh.
  3. Thread metadata/list row still says high because it reflects last turn context, not the settings update.
  4. On resume/reload, persisted high is merged back into config and the session appears to have reverted.
  5. If a later turn/start also carries stale effort: 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.effort from the Desktop client should not lower an already-explicit thread setting, and thread/settings/update/ThreadSettingsApplied should update the durable thread metadata or resume should avoid treating last-observed turn effort as the authoritative current thread setting.

Potential fix shape:

  • Keep/land the app-server guard from the previous repro: Desktop turn/start should not let stale params.effort override saved thread settings.
  • Also persist the effective ThreadSettingsApplied.thread_settings.reasoning_effort into thread metadata, or distinguish "last observed turn effort" from "current explicit thread setting" so resume/list cannot resurrect the old value.
ghitafilali · 2 months ago

Still reproducible on a newer Desktop build, with evidence that this is not just UI display state.

Environment:

  • Codex Desktop 26.616.41845 (CFBundleVersion 4198) from /Applications/Codex.app
  • Global config still forces xhigh:
  • model_reasoning_effort = "xhigh"
  • plan_mode_reasoning_effort = "xhigh"

Additional observations from local state/logs:

  • Before local mitigation, visible unarchived thread rows in ~/.codex/state_5.sqlite were still mostly persisted as high: high=781, xhigh=61, low=4, medium=1, blank=1.
  • The previously reported threads still had persisted reasoning_effort='high' even after later explicit XHigh UI updates:
  • 019e7d97-2e06-7e11-8d9a-447a11d5c390 (issue 364) was high; logs show explicit ThreadSettings ... effort: Some(Some(XHigh)) at 2026-06-19 21:22:23, 2026-06-19 21:23:01, and 2026-06-20 11:28:14.
  • 019e7fde-ecdb-7511-a367-0c66f1d95383 (issue 362) was high; logs show explicit ThreadSettings ... effort: Some(Some(XHigh)) at 2026-06-19 21:22:28, 2026-06-19 21:22:56, and 2026-06-20 11:28:09.
  • A newer affected thread also actually ran with high effort:
  • 019ee549-215d-7750-85a3-a31e5d6b6208 (issue 545) had codex.turn.reasoning_effort=high and codex.request.reasoning_effort=high at 2026-06-20 13:56:59, despite the user expectation that all active sessions are xhigh.

I think there are two related persistence paths involved:

  1. The stale Desktop turn/start.effort override path described by @Jackten is real. In current origin/main, codex-rs/app-server/src/request_processors/turn_processor.rs still passes params.effort into build_thread_settings_overrides for method: "turn/start"; build_thread_settings_overrides then treats any non-None effort as a settings override. Jackten's candidate commit 0f573314e6678e5b76b7af71b431831c992e16d3 addresses this class by ignoring turn/start.effort for clientInfo.name == "Codex Desktop" and adds codex_desktop_turn_start_effort_does_not_override_thread_settings.
  1. There also appears to be a durable metadata gap after an explicit settings update. EventMsg::ThreadSettingsApplied in codex-rs/app-server/src/bespoke_event_handling.rs updates in-memory thread state and emits thread/settings/updated, but does not update the persistent thread metadata row. Separately, codex-rs/thread-store/src/thread_metadata_sync.rs records TurnContext.effort into ThreadMetadataPatch.reasoning_effort, and codex-rs/app-server/src/request_processors/thread_processor.rs later uses persisted metadata.reasoning_effort to set model_reasoning_effort during 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:

  • Land the stale-Desktop-turn/start.effort guard from Jackten's branch, adjusted for current origin/main.
  • Also persist the effective reasoning effort from 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.
  • Add a regression that starts/runs High, applies thread/settings/update to 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 · 1 month ago

Controlled reproduction: send_message_to_thread copies sender effort into recipient callback turn

I 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_applied event and the callback turn actually runs at the sender's effort.

Environment

  • Codex in ChatGPT desktop: 26.707.71524 (bundle 5263)
  • Bundled CLI: codex-cli 0.144.2
  • Subscription: ChatGPT Pro
  • Platform: Darwin 25.5.0 arm64 arm
  • Model in every test thread: gpt-5.6-sol

Test protocol

  1. Create a fresh parent thread at max, let its initial no-op turn complete, and leave it idle.
  2. Create a fresh child thread at the test effort.
  3. Have the child call:
send_message_to_thread({
  threadId: "<parent-thread-id>",
  prompt: "<fixed marker>"
  // model and thinking intentionally omitted
})
  1. Inspect the recipient parent's rollout around the resulting codex_delegation.
  2. Repeat with a max-effort child as the control.

No thread inspected or modified repository files. No user changed either parent's settings between turns.

| Parent before callback | Child sender | Parent callback turn |
|---|---|---|
| Sol / max | Sol / medium | Sol / medium |
| Sol / max | Sol / max | Sol / max |

Reproduction evidence

Disposable sessions:

  • Parent: 019f5f05-1ea5-7ee3-acac-4dffdcd36ec7
  • Medium child: 019f5f05-508a-7b03-8aa3-884e9f9deecd

Sanitized parent event sequence:

2026-07-14T05:06:40.085Z  turn_context              model=gpt-5.6-sol effort=max
2026-07-14T05:07:02.044Z  thread_settings_applied   model=gpt-5.6-sol reasoning_effort=medium
2026-07-14T05:07:02.058Z  turn_context              model=gpt-5.6-sol effort=medium
2026-07-14T05:07:02.064Z  codex_delegation received from medium child

Control evidence

Disposable sessions:

  • Parent: 019f5f06-1e51-7601-9e43-68f8cc9a136c
  • Max child: 019f5f07-04c9-7923-984b-a53f5d6cca59
2026-07-14T05:07:45.548Z  turn_context              model=gpt-5.6-sol effort=max
2026-07-14T05:08:55.124Z  thread_settings_applied   model=gpt-5.6-sol reasoning_effort=max
2026-07-14T05:08:55.138Z  turn_context              model=gpt-5.6-sol effort=max
2026-07-14T05:08:55.144Z  codex_delegation received from max child

This 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_thread omits 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 · 1 month ago

is there a single human person in this thread at all, lmao.

Jackten · 1 month ago

No, we're all algorithms

On Tue, Jul 28, 2026, 11:41 psychananaz @.***> wrote:

psychananaz left a comment (openai/codex#26930) <https://github.com/openai/codex/issues/26930#issuecomment-5106319624> is there a single human person in this thread at all, lmao. — Reply to this email directly, view it on GitHub <https://github.com/openai/codex/issues/26930?email_source=notifications&email_token=AAWC45YIACXZQFG5RSUOPWD5HDCUFA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKMJQGYZTCOJWGI2KM4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLDGN5XXIZLSL5RWY2LDNM#issuecomment-5106319624>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AAWC45YHZBN7N7LLBY2VSN35HDCUFAVCNFSNUABFKJSXA33TNF2G64TZHM4TMNJUGE2TMNBZHNEXG43VMU5TINRQHE2TCMBSGU32C5QC> . Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS <https://github.com/notifications/mobile/ios/AAWC455E43GFHJTHR3I4JA35HDCUFA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKMJQGYZTCOJWGI2KM4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJKTGN5XXIZLSL5UW64Y> and Android <https://github.com/notifications/mobile/android/AAWC456FO7YDVKED6GXRTXT5HDCUFA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKMJQGYZTCOJWGI2KM4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLTGN5XXIZLSL5QW4ZDSN5UWI>. Download it today! You are receiving this because you were mentioned.Message ID: @.***>
kyleAmadeous · 28 days ago

I traced a separate, deterministic thread/settings/update writer that matches the reported parent-thread downgrade and is still present on current main (4642370542739d5dd080b0c87a9de06a6435d3db).

Evidence

The affected rollout was produced by originator=codex-tui, cli_version=0.144.1. A parent running gpt-5.6-sol/xhigh recorded this sequence during an already-active turn:

2026-07-14T10:15:16.274Z thread_settings_applied model=gpt-5.6-luna reasoning_effort=xhigh
2026-07-14T10:15:16.276Z thread_settings_applied model=gpt-5.6-luna reasoning_effort=low

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 in codex-rs/tui/src/app/event_dispatch.rs (lines 340-356). On current main, the same logic lives in codex-rs/tui/src/app/safety_buffering.rs:

if !self.chat_widget.can_retry_safety_buffered_turn(&turn_id) {
    self.app_event_tx.send(AppEvent::UpdateModel(model));
    self.app_event_tx.send(AppEvent::UpdateReasoningEffort(Some(
        ReasoningEffortConfig::Low,
    )));
    return;
}

model is the one-off safety-buffering fasterModel (Luna in the reproduction), while low is 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:

SafetyBufferingEvent.faster_model
  -> RetrySafetyBufferedTurn
  -> stale retry branch emits UpdateModel + UpdateReasoningEffort(Low)
  -> sync_active_thread_*_setting
  -> ThreadSettingsUpdateParams(active parent thread id)
  -> thread/settings/update (twice)
  -> Op::ThreadSettings
  -> thread_settings_applied (twice)

A subsequent turn/start then reuses the contaminated local TUI settings. This is distinct from the sender-effort delegation path and from the stale Desktop turn/start.effort path 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 low effort.

Branch: https://github.com/kyleAmadeous/codex/tree/fix/kyle/tui-parent-settings-safety-retry
Commit: https://github.com/kyleAmadeous/codex/commit/666b6cc2e7a44af2f47bf4d8294d117c2983ba2f

The regression configures the parent as Sol/xhigh and the stale retry as Luna/low, then verifies that no persistent UpdateModel or UpdateReasoningEffort event is emitted and the parent remains Sol/xhigh.

Pre-fix, the new test failed with:

stale worker retry must not enqueue persistent parent settings: UpdateModel("gpt-5.6-luna")

Post-fix validation:

  • focused regression: 1 passed
  • safety retry tests: 7 passed
  • just test -p codex-tui: 3304 passed, 4 skipped
  • just fix -p codex-tui
  • just fmt
  • git diff --check

Per 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.

dogwuyi · 8 days ago

Additional reproduction on the current ChatGPT desktop app for macOS:

  • App version: 26.814.41407
  • Platform: macOS, arm64
  • Subscription: ChatGPT Pro
  • Model: GPT-5.6 Sol
  • Selected reasoning effort: Extra High (xhigh)

This reproduces in both regular Chat and ChatGPT Work, without delegation or background workers.

Steps:

  1. Start a new chat/task.
  2. Select GPT-5.6 Sol with Extra High reasoning.
  3. Send the first prompt.
  4. While that response is still actively running, observe the composer model control.
  5. The visible reasoning level silently changes to Light without user action.
  6. A subsequent follow-up is then prepared with Light unless the user manually selects Extra High again.

Expected:
The explicitly selected reasoning effort should remain selected for the current thread until the user changes it. At minimum, if the composer is showing a next-turn default rather than the active turn's configuration, the UI should distinguish those states clearly and must not silently reset follow-ups.

Actual:
The control changes from Extra High to Light during the active response. This makes the user believe the running task was downgraded and causes later follow-ups to use Light unless they reselect xhigh before every message.

This suggests the issue is broader than delegation/continuation handling and also affects ordinary Chat and Work flows on a newer desktop build. A screenshot was captured showing the task still running while the composer already displayed "5.6 Sol Light"; it is omitted here because it contains personal sidebar information.

naipi11 · 8 days ago

Until the thread setting is preserved correctly, the safest manual guard is to treat the composer value as a per-turn setting:

  1. Let the current turn finish.
  2. After any delegation, continuation, or background callback, reopen the model picker and reselect the intended reasoning effort.
  3. Confirm the collapsed composer control still shows that effort immediately before sending the next message.
  4. Repeat the check after every callback that wakes the thread.

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.