Heartbeat automations and goal resumes can downgrade thread permissions after incomplete permission snapshots

Resolved 💬 3 comments Opened Jun 23, 2026 by Peter1169 Closed Jul 13, 2026
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

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:

  1. heartbeat automation state only preserves permissions when approvalPolicy, approvalsReviewer, and sandboxPolicy are all present
  2. if any field is missing, that helper returns null
  3. the scheduler then clears the remembered heartbeat permission snapshot for the thread
  4. 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
  5. 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.

View original on GitHub ↗

3 Comments

github-actions[bot] contributor · 2 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #28296
  • #28574
  • #28177

Powered by Codex Action

Peter1169 · 2 months ago

I reviewed the duplicate candidates from the bot:

  • #28296 covers the goal-continuation/restart side of this class of bug. This issue is related, but it is not meant to be only another goal-resume report.
  • #28574 is a broader report around usage-limit/next-day goal recovery plus the iOS controls gap.
  • #28177 is primarily about auto-compact failure/connectivity, with approval prompts appearing after the goal is manually resumed.

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.

Peter1169 · 2 months ago

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 (CreatePullRequest is rejected), so I'm linking the branch here instead.

What the patch does:

  • persists SessionConfigured and ThreadSettingsApplied rollout events so full permission snapshots, including approvals_reviewer, survive future resumes
  • makes thread/resume restore the latest persisted approval/profile settings when the resume request does not explicitly override permissions
  • keeps explicit resume overrides higher priority than persisted settings
  • adds regression coverage for both preserving persisted settings and override precedence

Validation run locally:

  • cargo fmt --manifest-path codex-rs/Cargo.toml -p codex-app-server -p codex-rollout
  • just test -p codex-app-server thread_resume_preserves_persisted_permissions_without_overrides
  • just test -p codex-app-server thread_resume_permission_overrides_win_over_persisted_permissions
  • just test -p codex-rollout
  • git diff --check