Permission / approval mode selection does not persist for new or existing threads

Open 💬 4 comments Opened Jun 24, 2026 by andrewsohrabi

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

  1. Open Codex desktop.
  2. Start a new thread.
  3. In the permission / approval picker, choose a non-workspace mode, for example Full access.
  4. Send the first prompt / start the thread.
  5. 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

  1. Start a thread with default / read-only-ish settings.
  2. Materialize the rollout with a simple turn.
  3. Resume or switch back to the already-loaded thread while requesting different approval / permission settings, for example approvalPolicy plus permissions = ":danger-full-access".
  4. 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:

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

  1. 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_params
  • just test -p codex-app-server-protocol
  • just 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.

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗