Permission / approval mode selection does not persist for new or existing threads
Summary
Permission / approval mode selection is not reliably persisted for either new threads or already-loaded existing threads.
This originally looked like only a loaded-thread thread/resume problem, but there is a broader desktop-visible failure:
- On an existing loaded thread, changing approval / permission mode through resume appears to work in the UI, but the live thread keeps the old effective settings.
- On a new thread, the permission picker allows selecting non-workspace options such as Full access, but after the thread starts / first prompt executes, the effective state falls back to workspace.
- The dropdown state can therefore look selectable before submission while the running thread state does not reflect the selected mode.
New-thread repro
- Open Codex desktop.
- Start a new thread.
- In the permission / approval picker, choose a non-workspace mode, for example Full access.
- Send the first prompt / start the thread.
- Reopen or inspect the permission picker.
Observed: the running thread shows workspace selected again instead of the selected mode.
Expected: the selected permission profile should be sent to app-server, reflected in the thread/start response, and used as the thread's effective permission state. If policy or requirements disallow the requested mode, the UI should show that explicitly rather than appearing to accept it and silently reverting.
Existing loaded-thread repro
- Start a thread with default / read-only-ish settings.
- Materialize the rollout with a simple turn.
- Resume or switch back to the already-loaded thread while requesting different approval / permission settings, for example
approvalPolicypluspermissions = ":danger-full-access". - Inspect the resume response or subsequent effective settings.
Observed: loaded-thread thread/resume previously ignored supported overrides and kept the old effective permission mode.
Expected: supported resume overrides should be validated and applied to the live thread, and the response should reflect the effective settings.
Suggested fix candidates
Two linked commits on a fork cover the two parts found so far:
- Loaded-thread backend fix:
https://github.com/andrewsohrabi/codex/commit/94e81ea40cc0fbb88f01aaa035b32157e5b34419
This adds a regression test for loaded thread/resume applying approvalPolicy + permissions, then routes supported loaded-resume overrides through the same validated thread-settings path used by thread/settings/update. Startup-only fields such as config, baseInstructions, and developerInstructions remain ignored while the thread is already loaded.
- New-thread schema / client-contract fix:
https://github.com/andrewsohrabi/codex/commit/d39d601907a5328bb74be44c296c29c579b56558
This adds coverage proving stable generated TypeScript includes permission-profile selection for thread lifecycle requests, then exposes permissions plus activePermissionProfile for thread/start, thread/resume, and thread/fork in the stable protocol fixtures. Without this, generated stable clients can list/read permission profiles but cannot cleanly send the selected named profile or read profile provenance from lifecycle responses.
Compare:
https://github.com/openai/codex/compare/main...andrewsohrabi:codex/permissions-fix
Validation run locally
Focused checks passed:
just test -p codex-app-server-protocol generated_ts_optional_nullable_fields_only_in_paramsjust test -p codex-app-server-protocoljust test -p codex-app-server thread_start_applies_named_permission_profile thread_resume_loaded_thread_applies_permission_overrides
Full just test -p codex-app-server was attempted. In this managed sandbox it ended with unrelated local environment failures: nested macOS sandbox-exec returns exit 71 (sandbox_apply: Operation not permitted), one executor MCP test cannot locate target/debug/codex, and a few file-watch / sandboxed image or file-change tests time out. The permission regressions above passed inside that run.
just fix -p ... and just fmt were also attempted but are blocked by the local toolchain / sandbox (cargo clippy and cargo fmt missing, dotslash missing, and uv cannot initialize its cache). git diff --check passes.
5 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Not a duplicate
Correction:
@codexmentions on GitHub issues do not trigger the Codex review workflow. Codex review requests are PR-based, so the actionable fix links are in the suggested-fix comment below.Suggested fix candidates:
The first commit adds a regression test for loaded
thread/resumeapplyingapprovalPolicy+permissions, then routes supported loaded-resume overrides through the same validated thread-settings path used bythread/settings/update. Startup-only fields such asconfig,baseInstructions, anddeveloperInstructionsremain ignored while the thread is already loaded.The second commit covers the new-thread symptom: stable generated protocol fixtures now expose
permissionsandactivePermissionProfileforthread/start,thread/resume, andthread/fork, so generated clients can send the selected named permission profile and read the effective profile provenance from lifecycle responses.Focused protocol and app-server regression tests pass locally. Full
codex-app-serverwas attempted, but this managed sandbox hits unrelated local failures around nested macOSsandbox-exec, a missingtarget/debug/codexhelper binary, and a few file-watch / sandboxed file-read timeouts.Additional macOS reproduction on the current Codex Desktop build, linked to a private feedback upload.
Feedback ID:
019fc598-b009-7150-a7a9-c83dacc379a6Environment:
26.727.51351, build61190.146.0-alpha.9.225.6.0, arm64Relevant configuration:
Observed in the same existing non-Goal thread without changing
config.toml:activePermissionProfile.id = "Auto"and the profile's network policy is enabled.activePermissionProfile = null,approvalPolicy = "on-request", andsandboxPolicy.type = "workspaceWrite". The UI then shows Approve for me.activePermissionProfile = nullagain.The persisted and per-turn state showed this transition repeatedly across consecutive turns. This appears to match the thread lifecycle / permission-profile provenance problem described here: Desktop alternates between a named permission-profile payload and a legacy approval/sandbox override.
Expected behavior: the selected named permission profile remains attached across subsequent turns until the user explicitly changes it.
The private feedback contains the same sanitized reproduction. Session logs and browser logs were not included.