Heartbeat automations and goal resumes can downgrade thread permissions after incomplete permission snapshots
Summary
I'm seeing a regression/variant of the permission downgrade issue around unattended resumes.
A thread that was already running with elevated permissions can be resumed from a heartbeat automation or a goal continuation with the lowest/default permission context instead. After that resume, the downgraded permissions appear to become the effective thread settings, so the same chat starts asking for manual approvals again even though it had previously been configured for agent approvals or full access.
This looks related to #24300, but that issue was closed as fixed in the next release and this variant is still present in a newer Windows Desktop build. It also affects heartbeat automations that target an existing thread, not just goal auto-continuations.
Environment
- Codex Desktop for Windows
- App package observed:
OpenAI.Codex_26.616.9593.0_x64__2p2nqsd0c76g0 - Scenario: existing chat/project thread with permissions set above the default prompt-every-time mode
Expected behavior
When an existing thread is resumed by a heartbeat automation or by restarting/continuing a goal, the new turn should preserve the thread's effective permission context.
For example:
- a thread using agent/guardian approvals should keep that
approvalsReviewer - a full-access thread should not fall back to
on-request - a thread should not silently rewrite itself to the lowest/default permission profile just because the resume was triggered by automation rather than by a normal user message
Actual behavior
After an automation sends a message to a chat/project, or after a project goal is restarted, the thread can be downgraded to the lowest/manual approval path. Commands and actions outside the sandbox then require explicit yes/no approval from the user again, even if the chat had been configured for automatic agent review or full access before the automation/goal resume.
Why I think this is happening
The desktop app appears to drop to defaults when the resume path does not have a complete permission snapshot.
The key pattern I found is:
- heartbeat automation state only preserves permissions when
approvalPolicy,approvalsReviewer, andsandboxPolicyare all present - if any field is missing, that helper returns
null - the scheduler then clears the remembered heartbeat permission snapshot for the thread
- when the automation later runs with
permissions: null, it recomputes permissions from saved/default config rather than inheriting the target thread's effective permission context - the resume response is then written back into thread state, making the downgrade visible/effective for later turns
That makes the bug sensitive to thread hydration and resume timing. It also explains why this shows up around automation/project-goal boundaries rather than on ordinary user-submitted turns.
Related issues
- #24300: goal auto-continuations downgrading full-access threads, closed as addressed in a later app release
- #18247: automation permission limitations and heartbeat automation discussion
Impact
This breaks unattended workflows: automations and goals can no longer run reliably without manual intervention, and the user-visible permission setting can drift away from what the next resumed turn actually uses.
Suggested fix direction
The resume paths should treat partial permission snapshots as incomplete data that must not erase the last known complete thread permissions. Heartbeat/goal resumes should prefer the target thread's latest effective permissions and only fall back to app defaults when there is genuinely no thread permission state to inherit.
3 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
I reviewed the duplicate candidates from the bot:
The extra bit I wanted to capture here is the heartbeat automation path for an existing target thread. In that path, a transient or incomplete permission snapshot can become
null; the scheduler then appears to forget the previous per-thread permissions and recompute from defaults/saved config. That is a narrower mechanism than the general “goal resumed with different permissions” symptom, and it can affect heartbeat automations even without the 5-hour/compaction/iOS path.So if maintainers prefer to keep #28296 as the umbrella issue for goal-only drift, this issue can be scoped to heartbeat automation / partial permission snapshot handling and linked back to #28296 for the goal-resume symptom.
I also prepared a small hardening patch for the public repo side here:
I tried opening a PR against
openai/codex, but PR creation appears to be permission-gated for this repository from my account (CreatePullRequestis rejected), so I'm linking the branch here instead.What the patch does:
SessionConfiguredandThreadSettingsAppliedrollout events so full permission snapshots, includingapprovals_reviewer, survive future resumesthread/resumerestore the latest persisted approval/profile settings when the resume request does not explicitly override permissionsValidation run locally:
cargo fmt --manifest-path codex-rs/Cargo.toml -p codex-app-server -p codex-rolloutjust test -p codex-app-server thread_resume_preserves_persisted_permissions_without_overridesjust test -p codex-app-server thread_resume_permission_overrides_win_over_persisted_permissionsjust test -p codex-rolloutgit diff --check