Windows Desktop: new threads/handoffs persist as on-request/workspaceWrite instead of inheriting visible Full Access state
Summary
On Windows Codex Desktop, newly created threads can persist as approvalPolicy = on-request / sandboxPolicy.type = workspaceWrite even when the user workflow appears to be operating in a Full Access-like mode.
This is reproducible with:
- ordinary fresh test threads
- handoff-created threads
and does not require Goal auto-continuation.
Environment
- Codex Desktop app:
OpenAI.Codex_26.527.7698.0_x64__2p2nqsd0c76g0 - Platform: Windows
Expected
If the user has switched into a Full Access-like mode in the app, newly created threads should inherit that mode consistently, or the UI should clearly indicate that new threads will still be created with approval prompts.
Actual
Newly created threads are persisted as:
approvalPolicy = on-requestapprovalsReviewer = guardian_subagentsandboxPolicy.type = workspaceWrite
This then causes approval prompts to originate from those new threads.
Repro
- Open Codex Desktop on Windows.
- Work in a thread until the UI/workflow suggests Full Access-like behavior is active.
- Create a new thread from that session, or create a handoff into a new thread.
- Let the new thread do almost nothing, e.g. only answer once with a short reply.
- Inspect persisted thread state in
C:\Users\강인철\.codex\.codex-global-state.json. - Observe that the new thread is stored as
on-request / guardian_subagent / workspaceWrite.
Concrete repro evidence
Host-level persisted mode in app state:
agent-mode-by-host-id.local = guardian-approvalspreferred-non-full-access-agent-mode-by-host-id.local = guardian-approvals
Fresh / recent threads created during reproduction:
- current thread
019e866c-0a1c-7601-a60c-686b686bcc5c->on-request / guardian_subagent / workspaceWrite - test thread
019e8764-2aa9-73d2-9b3d-87ecb202af5a->on-request / guardian_subagent / workspaceWrite - test thread
019e8765-f908-7410-b44d-443df7934c73->on-request / guardian_subagent / workspaceWrite - test thread
019e877d-fb12-7683-8033-3f909b8ca714->on-request / guardian_subagent / workspaceWrite - handoff-derived thread
019e8689-b909-7ed1-a141-d0b3323a21cf->on-request / guardian_subagent / workspaceWrite
Additional pattern:
- among currently stored thread states on this machine, only 1 thread had
approvalPolicy = never - 60 threads had
approvalPolicy = on-request
Why this seems like a bug
This does not look like a one-off stuck thread.
It looks like one of:
- thread creation is not inheriting the currently visible Full Access selection
- host-level permission mode is not being committed correctly before thread creation
- handoff/new-thread creation is using a different policy source than the visible UI state
- the app is exposing a misleading Full Access state to the user while still creating threads in guardian-approvals mode
Related
This may be related to, but broader than, the behavior discussed in #24300.
I also added Windows reproduction details there in a comment:
12 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Small follow-up after checking the related discussion in #24300:
In my reproduction:
Concrete examples from my local persisted thread state:
019e8764-2aa9-73d2-9b3d-87ecb202af5a->on-request / guardian_subagent / workspaceWrite019e8765-f908-7410-b44d-443df7934c73->on-request / guardian_subagent / workspaceWrite019e877d-fb12-7683-8033-3f909b8ca714->on-request / guardian_subagent / workspaceWriteThose were fresh test threads that only received a short user prompt and replied once.
So if this is the same root cause, it seems to affect thread creation / policy inheritance more generally on Windows Desktop, not only the "change permission -> create Goal" path.
Additional Windows Desktop follow-up from June 3, 2026:
I have a clearer split now between
auto-reviewvs thread policy.What I am seeing is not just "auto-review failed to approve something".
Instead, the underlying thread policy itself appears to revert back to restrictive settings after thread execution starts.
New observation
After switching the app-level host mode to Full Access, I could verify the persisted host value became:
agent-mode-by-host-id.local = full-accessI then created fresh threads and briefly saw cases that looked consistent with:
approvalPolicy = neversandboxPolicy.type = dangerFullAccessHowever, after the thread actually started running / continuing, persisted state later showed the same thread as:
approvalPolicy = on-requestsandboxPolicy.type = workspaceWriteWhy this matters
This suggests the problem may be broader than "new thread creation inherits the wrong initial settings".
It may also involve policy reversion during thread start / resume / goal-setting / delegated execution.
So the bug shape now looks more like:
on-request / workspaceWriteImportant distinction: auto-review vs approval policy
At least in my local investigation,
auto-reviewseems to be a separate concept from the popup itself:auto-review/ reviewer affects who reviews an approval requestapprovalPolicydetermines whether the thread is effectivelynevervson-requestSo the user-facing symptom can look like "auto-review is broken", while the deeper issue is actually that the thread got persisted back to
on-request.Scope note
This still does not look Goal-only to me.
I have seen related behavior in:
So Goal may be one trigger path, but not the whole story.
If useful, I can provide another tightly scoped repro sequence specifically for:
never / dangerFullAccesson-request / workspaceWriteAdditional Windows Desktop / mobile-resume detail that seems worth tracking separately even if the original
approvalPolicydrift is fixed.New behavior observed on June 4, 2026 (KST):
C:\Users\<USER>\Documents\Codex\2026-06-03\specialization-layer-handoff-parkedC:\Users\<USER>\Documents\Codex\2026-05-24\hermes-project-os-expert-agent\expert-repository-pocAfter resuming from mobile and sending short messages like:
the parked thread stopped behaving like a parked handoff and resumed actual work. It started trying to read/edit files in the canonical repo outside the current workspace and produced approval prompts such as:
tools/validate.pytools/test_validate_specialization.pylatest.mdIn other words, the trigger was not a broad new user task from scratch; it was a few short follow-up messages inside a parked handoff thread. The app seems to treat that parked thread as a live execution surface even though its workspace is only a handoff shell, not the real repo.
Separately, one of these parked threads later hit a Windows stale-path resume error even though the JSONL file still existed on disk. The error compared:
C:\Users\<USER>\.codex\sessions\...jsonl\\?\C:\Users\<USER>\.codex\sessions\...jsonlThose are the same file, so this looks like another Windows path-normalization bug on top of the workspace mismatch. (Related in spirit to #24178.)
My current working theory:
C:\...vs\\?\C:\...).Practical expectation:
I have an additional macOS reproduction that looks like the same permission
profile reversion class, but through the Goal quota-reset resume path rather
than Windows new-thread creation.
Observed sequence:
feature, and resumed the existing Goal session.
ゴールの使用回数制限あり.I did not confirm what the English UI label says.
permission changes, did not recover this session.
resumed thread asked for approvals.
Sanitized local state:
agent-mode = full-accessandagent-mode-by-host-id.local = full-accessactivePermissionProfile.id = :workspaceapprovalPolicy = on-requestsandboxPolicy.type = workspaceWriteThis seems to add a narrower trigger to the pattern already discussed here:
permission profile,
state,
Expected behavior: after quota reset, resuming an existing Goal session should
either reconcile the thread-specific permission profile with the current
full-access app mode, or make the per-thread restricted override visible before
the resume starts.
I am intentionally not including full local state, thread ids, or prompt
history because those contain sensitive data.
Small macOS follow-up to my previous comment.
I have now seen a similar permission mismatch in existing sessions that were not using Goal Mode. So the paused Goal / quota-reset path does not look like a required trigger. It may still be one trigger or amplifier, but the broader class appears to be existing-session resume / per-thread permission state getting out of sync with the visible app-level Full Access mode.
Additional recovery observation:
After restarting Codex Desktop, the same affected session now appears consistent across the three surfaces I checked:
CODEX_SANDBOX=seatbeltorCODEX_SANDBOX_NETWORK_DISABLED=1.codex-global-state.jsonshows app modefull-access, and this thread has:danger-full-access / never / dangerFullAccessstate_5.sqliteshows this thread asapproval_mode=neverandsandbox_policy={"type":"disabled"}So the current evidence from my macOS side is:
I am still omitting local thread ids and prompt history because they contain private data.
Small follow-up with a local workaround observation after this reproduced again on macOS in a retained existing session.
In the new case, the visible app/host mode was Full Access, but the active thread still launched with a restricted effective policy. The useful split was checking two persisted state surfaces for the affected thread:
state_5.sqliteheartbeat-thread-permissions-by-idin.codex-global-state.jsonThe emergency workaround that recovered the persisted state for me was to back up both files, then align both surfaces for the affected thread:
state_5.sqlite: set the thread toapproval_mode = neverandsandbox_policy = {"type":"disabled"}.codex-global-state.json: set the thread entry toactivePermissionProfile.id = :danger-full-access,approvalPolicy = never, andsandboxPolicy.type = dangerFullAccessA reload/resume of the affected thread is still required after the state repair. The already-running turn does not adopt the new sandbox profile just because the files were changed.
This is only an unsupported local emergency workaround, not a root-cause claim or a suggested product fix. I am not including local thread ids, absolute paths, prompt history, or private state contents.
I am seeing a related inheritance problem with delegated/subagent work.
After granting Full Access, the parent agent appears to acknowledge that the permission state has been updated. But when the parent dispatches a subagent, the subagent does not behave as if it inherited that updated Full Access state. The child still appears to operate under a stale or restricted permission context, which causes delegated work to fail or hit permission boundaries unexpectedly.
This is confusing because the visible state suggests the current workflow is allowed to proceed with Full Access, but newly created child work does not consistently inherit that state.
I found a concrete downgrade path on Windows Codex Desktop 26.803.5235.0 (embedded app-server 0.130.0-alpha.5).
Persisted state from an older installation contained:
The current Desktop code expects an object:
Because the persisted boolean is accepted without schema migration, both mode properties are effectively missing. Full Access briefly appears during startup, then is filtered from the visible agent modes. The reconciliation effect switches the active mode back to
autoand rewrites the current thread permissions toon-request/workspaceWrite.The Codex configuration itself was valid:
configRequirements/readalso returnedrequirements: null.Replacing the legacy boolean with the current object representation fixed the problem, and Full Access now persists across application restarts.
Suggested fix: migrate legacy boolean values to the current object schema, or validate the persisted value type and fall back to the current default object before filtering the available permission modes.
I reproduced a distinct cross-host delegated-creation downgrade in the current signed macOS Desktop build.
Source client:
26.803.41515, bundle6321app.asarSHA-2565f6e773aafd542d3cf09e10b5dca6cabd301d0a155f4b8ce870e3915fc3da25eapproval_policy=never,sandbox_policy=danger-full-access, unrestricted permission profilefull-accessThe source called the official
create_threadtool for a remote HOMELAND Git project without a permission override. The receiver's first turn was nevertheless persisted asapproval_policy=on-request,sandbox_policy.type=workspace-write, managed/restricted filesystem.Read-only inspection of the signed renderer shows the conversion is explicit in the cross-host delegated path. In the minified build,
C5o(...)readsagent-mode-by-host-id, but whenthreadSource === "subagent"it maps a selectedfull-accessmode toautounless a same-host source permission object can be reused. A cross-host source cannot supply that reusable object, sob5o(...)sendsagentMode: "auto"withshouldSendPermissionOverrides: true. This occurs before the destination's healthydefault_permissions = ":danger-full-access"can apply.This also explains why repairing the destination config and the source's per-host selection does not change the result, while a locally created destination task starts correctly with full access.
Expected: cross-host
create_threadshould either preserve the explicitly selected destination-host permission mode, omit the override so destination defaults resolve it, or expose an explicit permission argument/consent path. It should not silently convert the owner's full-access selection toauto.I have installed a local exact-build fail-closed guard so this signed build no longer creates high-authority cross-host tasks. I can provide sanitized rollout excerpts or the relevant minified function body if useful.
Additional deterministic callback/worktree reproduction and narrowed root cause
I reproduced this on Windows with Codex Desktop Store package
26.803.10989.0(embedded app build26.803.81509). This is a controlled same-parent/same-create-payload comparison:approvalPolicy=never,sandboxPolicy=dangerFullAccess, and the built-in:danger-full-accessprofile.create_threadtarget shape. The parent callback turn itself still had effectivenever + dangerFullAccess, but its active permission profile ID was null. The child started ason-request + workspaceWrite + networkAccess:false.never + dangerFullAccess.The renderer's cross-CWD permission-selection step appears to preserve a source selection when either:
:.It rejects the callback representation when source and target CWD differ and the effective mode is Full Access but the profile ID is null. The delegated-child fallback then silently resolves to
auto, which produces the observed managed/workspace-write child.For the observed case, the narrow source correction is conceptually:
Full Access is not CWD-bound. The later worktree-finalization path can rebuild canonical
never + dangerFullAccessfor the new worktree roots.For the durable fix, please also fail closed when a delegated
create_threadhas no recoverable effective source permission snapshot. It should not silently fall back toauto. A safe resolver can recover fieldwise from the source task's latest thread settings, latest turn parameters, and current permissions, but should omit host defaults for delegated creation.Suggested focused regression matrix:
permissionProfileId=nullis inherited;:danger-full-accesscontinues to be inherited;auto;User impact: unattended controller workflows stop and require a human foreground retry even though the parent task had Full Access throughout. A follow-up sent by the parent cannot repair the child because
send_message_to_threadcarries no permission fields.Deterministic macOS Heartbeat trigger and narrowed root cause
I reproduced a deterministic instance of this permission-inheritance bug in the current signed macOS Codex Desktop build.
Environment
26.818.41705(bundle6971)0.149.0-alpha.4.1Darwin 25.5.0 arm64:danger-full-access,approvalPolicy=never,sandboxPolicy=dangerFullAccessReproduction
:danger-full-access.approvalPolicy=neverandsandboxPolicy=dangerFullAccess, but itsactivePermissionProfileis nownull.create_threadtool for a project/worktree whose CWD differs from the parent CWD.approvalPolicy=on-request,sandboxPolicy=workspaceWrite, with a managed/restricted filesystem.:danger-full-access, after which cross-CWD creation inherits Full Access again.The app-level host selection remains
full-accessthroughout. The lost value is specifically the active profile identity on the heartbeat turn.Narrowed renderer path
Static inspection of the signed packaged renderer suggests the following chain. Minified identifiers are included only as clues for this exact build:
R2l/B2l) accepts a latest permission object withactivePermissionProfile: nullas authoritative.heartbeat-thread-permissions-by-idfallback, even when the fallback still contains:danger-full-access.roi) therefore derives Full Access withpermissionProfileId=null.aoi) preserves built-in profiles only when the ID starts with:; it rejects this identity-less Full Access selection.ioi) deliberately maps host-levelfull-accesstoauto, yieldingon-request/workspaceWrite.Conceptually, the heartbeat merge currently distinguishes only between an absent field and a present field, but does not treat a null profile identity as incomplete:
Expected behavior
A Heartbeat should not erase the selected built-in permission profile identity. After a Full Access parent is resumed by Heartbeat, delegated same-host project/worktree creation should inherit
:danger-full-access, just as it does from a foreground parent turn.A focused regression test would cover:
Custom/CWD-bound profiles should retain their existing non-inheritance behavior; this report is about the built-in Full Access profile losing its identity before the existing cross-CWD check.
Current workaround
I found no supported Heartbeat, automation, or
create_threadparameter that pins a permission profile. Prompt instructions cannot repair the turn context. The only reliable user-level workaround found so far is to reselect Full Access on the parent after a Heartbeat and before creating delegated cross-CWD work. Retrying creation while the profile remains null does not help.This report was prepared and submitted by the GPT-5.6 Sol agent running in the Codex Desktop harness, on behalf of the authenticated GitHub account
@albert-zen. Local paths, thread IDs, and prompt history were intentionally omitted.