Summary
Within a single Codex desktop task, the effective permission state (activePermissionProfile, approvalPolicy, approvalsReviewer, writableRoots) silently degrades over successive turns — even when no files, settings, or the model are changed, and even though the on-disk config.toml defaults never change. Over three consecutive read-only status requests in one task, the state went from the correct configured profile down to bare :workspace with sandbox_approval: false and almost all writable roots removed.
This was independently confirmed by reading .codex-global-state.json directly, not just from the app's own self-reported status — the per-thread heartbeat entry in the state file matches the degraded values exactly.
Environment
Platform: Windows, Codex desktop app (please fill in exact build number from Settings → About — omitted here since it wasn't captured during the investigation)
Account: authenticated via ChatGPT; no organization-managed requirements.toml or managed_config.toml present at any documented system/user location
config.toml throughout this entire test: approval_policy = "on-request", approvals_reviewer = "user", default_permissions = "husum-property-files" — unchanged at every turn
Steps to reproduce
Start a new Codex task with a profile that has an explicit default_permissions set in config.toml (in this case, a custom profile husum-property-files).
Ask for a read-only status report of activePermissionProfile, approvalPolicy, approvalsReviewer, sandbox approval status, and writable roots.
Without making any file changes, permission changes, or model changes, ask for the identical read-only status report again.
Repeat once more.
Observed behavior (this run)
Turn 1 Turn 2 Turn 3
activePermissionProfile husum-property-files (extends :workspace) null / missing :workspace
approvalPolicy "on-request" "on-request" {"granular": {"sandbox_approval": false, "rules": false, "skill_approval": false, "request_permissions": true, "mcp_elicitations": true}}
approvalsReviewer "user" "user" "user"
writableRoots 7 entries, including the task folder, F:\Pictures\My Pictures, two OneDrive project folders, and .codex\memories same 7, plus an unrequested broader root (C:\Users\Dave Gibney\Documents\Codex, the parent of the task folder) only the task's own temp visualization directory — all 6 other roots gone, including F:\Pictures\My Pictures
Turn 3's values were independently verified against .codex-global-state.json → electron-persisted-atom-state.heartbeat-thread-permissions-by-id["<thread-id>"] directly on disk, and match the app's self-report exactly:
{
"activePermissionProfile": { "id": ":workspace", "extends": null },
"approvalPolicy": {
"granular": {
"sandbox_approval": false,
"rules": false,
"skill_approval": false,
"request_permissions": true,
"mcp_elicitations": true
}
},
"approvalsReviewer": "user",
"sandboxPolicy": {
"type": "workspaceWrite",
"writableRoots": ["C:\\Users\\<user>\\.codex\\visualizations\\2026\\08\\25\\<thread-id>"],
"networkAccess": false,
"excludeTmpdirEnvVar": false,
"excludeSlashTmp": false
}
}
Expected behavior
A task's effective permission profile, approval policy, reviewer, and writable-root allowlist should not change between turns unless the user, the config, or the model changes. Three plain read-only status requests in the same task should return identical values each time.
Additional related finding (separate but adjacent bug)
Before the above was reproduced, a different desync was found and had to be manually patched: two host-scoped keys in the same state file — electron-persisted-atom-state.agent-mode-by-host-id.local and electron-persisted-atom-state.preferred-non-full-access-agent-mode-by-host-id.local — remained stuck at "guardian-approvals" (left over from earlier "Approve for me" usage) even after permission-selection-by-host-id:local was cleared and config.toml correctly specified approvals_reviewer = "user". As long as those two keys held "guardian-approvals", new tasks' approvalsReviewer came back as auto_review regardless of config.toml. This suggests the app keeps more than one on-disk representation of "current permission mode" per host, and they can desync from each other and from config.toml. Setting agent-mode-by-host-id.local to "auto" and preferred-non-full-access-agent-mode-by-host-id.local to null (matching a known-good backup from earlier the same day) resolved that specific symptom — approvalsReviewer correctly held at "user" through all three turns of the test above, even while the separate per-turn degradation (this report's main subject) still occurred.
Impact
Permission-dependent file operations (in this case, work on personal photo files) cannot be safely performed partway through a task, since the writable-root allowlist and sandbox approval behavior cannot be trusted to hold steady even across read-only turns. The only currently-known-reliable point is the first turn of a brand-new task.
Summary
Within a single Codex desktop task, the effective permission state (
activePermissionProfile,approvalPolicy,approvalsReviewer,writableRoots) silently degrades over successive turns — even when no files, settings, or the model are changed, and even though the on-diskconfig.tomldefaults never change. Over three consecutive read-only status requests in one task, the state went from the correct configured profile down to bare:workspacewithsandbox_approval: falseand almost all writable roots removed.This was independently confirmed by reading
.codex-global-state.jsondirectly, not just from the app's own self-reported status — the per-thread heartbeat entry in the state file matches the degraded values exactly.Environment
Platform: Windows, Codex desktop app (please fill in exact build number from Settings → About — omitted here since it wasn't captured during the investigation)
Account: authenticated via ChatGPT; no organization-managed
requirements.tomlormanaged_config.tomlpresent at any documented system/user locationconfig.tomlthroughout this entire test:approval_policy = "on-request",approvals_reviewer = "user",default_permissions = "husum-property-files"— unchanged at every turnSteps to reproduce
Start a new Codex task with a profile that has an explicit
default_permissionsset inconfig.toml(in this case, a custom profilehusum-property-files).Ask for a read-only status report of
activePermissionProfile,approvalPolicy,approvalsReviewer, sandbox approval status, and writable roots.Without making any file changes, permission changes, or model changes, ask for the identical read-only status report again.
Repeat once more.
Observed behavior (this run)
Turn 1 Turn 2 Turn 3
activePermissionProfile
husum-property-files(extends:workspace)null/ missing:workspaceapprovalPolicy
"on-request""on-request"{"granular": {"sandbox_approval": false, "rules": false, "skill_approval": false, "request_permissions": true, "mcp_elicitations": true}}approvalsReviewer
"user""user""user"writableRoots 7 entries, including the task folder,
F:\Pictures\My Pictures, two OneDrive project folders, and.codex\memoriessame 7, plus an unrequested broader root (C:\Users\Dave Gibney\Documents\Codex, the parent of the task folder) only the task's own temp visualization directory — all 6 other roots gone, includingF:\Pictures\My PicturesTurn 3's values were independently verified against
.codex-global-state.json→electron-persisted-atom-state.heartbeat-thread-permissions-by-id["<thread-id>"]directly on disk, and match the app's self-report exactly:{ "activePermissionProfile": { "id": ":workspace", "extends": null }, "approvalPolicy": { "granular": { "sandbox_approval": false, "rules": false, "skill_approval": false, "request_permissions": true, "mcp_elicitations": true } }, "approvalsReviewer": "user", "sandboxPolicy": { "type": "workspaceWrite", "writableRoots": ["C:\\Users\\<user>\\.codex\\visualizations\\2026\\08\\25\\<thread-id>"], "networkAccess": false, "excludeTmpdirEnvVar": false, "excludeSlashTmp": false } }Expected behavior
A task's effective permission profile, approval policy, reviewer, and writable-root allowlist should not change between turns unless the user, the config, or the model changes. Three plain read-only status requests in the same task should return identical values each time.
Additional related finding (separate but adjacent bug)
Before the above was reproduced, a different desync was found and had to be manually patched: two host-scoped keys in the same state file —
electron-persisted-atom-state.agent-mode-by-host-id.localandelectron-persisted-atom-state.preferred-non-full-access-agent-mode-by-host-id.local— remained stuck at"guardian-approvals"(left over from earlier "Approve for me" usage) even afterpermission-selection-by-host-id:localwas cleared andconfig.tomlcorrectly specifiedapprovals_reviewer = "user". As long as those two keys held"guardian-approvals", new tasks'approvalsReviewercame back asauto_reviewregardless ofconfig.toml. This suggests the app keeps more than one on-disk representation of "current permission mode" per host, and they can desync from each other and fromconfig.toml. Settingagent-mode-by-host-id.localto"auto"andpreferred-non-full-access-agent-mode-by-host-id.localtonull(matching a known-good backup from earlier the same day) resolved that specific symptom —approvalsReviewercorrectly held at"user"through all three turns of the test above, even while the separate per-turn degradation (this report's main subject) still occurred.Impact
Permission-dependent file operations (in this case, work on personal photo files) cannot be safely performed partway through a task, since the writable-root allowlist and sandbox approval behavior cannot be trusted to hold steady even across read-only turns. The only currently-known-reliable point is the first turn of a brand-new task.