Skip to content

permissions: move workspace roots onto thread state#22401

Open
bolinfest wants to merge 1 commit into
mainfrom
pr22401
Open

permissions: move workspace roots onto thread state#22401
bolinfest wants to merge 1 commit into
mainfrom
pr22401

Conversation

@bolinfest
Copy link
Copy Markdown
Collaborator

@bolinfest bolinfest commented May 13, 2026

Why

PermissionProfile is becoming the source of truth for a thread's effective permissions, but workspace-write roots were still split across multiple representations. SandboxPolicy::WorkspaceWrite carried its own writable_roots, rollout data did not treat workspace roots as first-class thread state, and status / compatibility code had to reconstruct the current roots indirectly.

That split makes resume, fork, memories, and app-server lifecycle responses harder to keep consistent. This PR moves workspace roots onto thread state so the current thread owns that data and legacy sandbox projections become derived compatibility output.

What Changed

  • Persist workspace_roots alongside thread/session state in rollout and thread-store metadata, and carry them through resume/fork reconstruction.
  • Stop storing writable roots inside SandboxPolicy::WorkspaceWrite; the policy now carries only sandbox flags, and project roots are materialized from the current thread's workspace_roots.
  • Track whether workspace roots were supplied explicitly or defaulted from cwd, so cwd-only updates can rebind the implicit default root while preserving explicit/persisted roots unless workspaceRoots is updated.
  • Simplify active-profile bookkeeping by removing ActivePermissionProfileModification and tightening Permissions access around the canonical PermissionProfile.
  • Thread lifecycle responses now expose workspaceRoots, activePermissionProfile, the legacy sandbox projection, and a read-only exact permissionProfile so clients can recover non-lossy effective permissions; app-server request APIs still cannot replace that profile value.
  • Update sandbox/status summarization to derive from PermissionProfile + workspace_roots, including hiding internal writable roots such as Codex memories from /status.

Compatibility and Migration

  • Legacy rollout/session data that serialized SandboxPolicy::WorkspaceWrite.writable_roots is migrated into thread workspace_roots when loaded.
  • Older stored sessions that do not yet have explicit workspace_roots continue to fall back to cwd where the legacy format implied that behavior, including old SessionMeta lines.
  • Explicit empty workspace_roots still round trips as empty; this matters for read-only / full-access cases where workspace roots are intentionally irrelevant.
  • Existing legacy SessionMeta aliases such as agent_type continue to deserialize while the custom workspace-roots migration runs.
  • This keeps resume, fork, and memory reconstruction on the thread-owned root list instead of depending on the removed sandbox-policy field.
  • Clients that understand lifecycle permissionProfile use that exact read-only field; older clients can continue reconstructing from the legacy sandbox projection.

Verification

The targeted regression coverage here focused on the persistence, migration, and response-projection paths most likely to silently break stored threads or memories:

  • turn_context_item_migrates_legacy_workspace_write_writable_roots
  • turn_context_item_migrates_missing_workspace_roots_to_cwd
  • session_meta_migrates_missing_workspace_roots_to_cwd
  • session_meta_preserves_explicit_empty_workspace_roots
  • session_meta_preserves_legacy_agent_type_alias
  • deserialize_legacy_session_configured_event_migrates_workspace_write_writable_roots
  • workspace_write_summary_hides_internal_writable_roots
  • session_configuration_apply_rebinds_implicit_workspace_root_on_cwd_update
  • session_configuration_apply_preserves_explicit_workspace_roots_on_cwd_update
  • thread_resume_preserves_persisted_workspace_roots_when_request_omits_them
  • thread_fork_preserves_persisted_workspace_roots_when_request_omits_them
  • codex-exec session_configured_from_thread_*_uses_workspace_roots_for_workspace_sandbox
  • codex-exec session_configured_from_thread_response_prefers_permission_profile_field
  • focused codex-rollout / codex-thread-store resume and metadata tests, including empty-root round trips

Stack created with Sapling. Best reviewed with ReviewStack.

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f68960302a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread codex-rs/app-server-protocol/src/protocol/v2/thread.rs
Comment thread codex-rs/app-server-protocol/src/protocol/v2/turn.rs Outdated
@bolinfest bolinfest force-pushed the pr22401 branch 6 times, most recently from bb76782 to b9f3284 Compare May 13, 2026 03:52
@bolinfest bolinfest requested a review from viyatb-oai May 13, 2026 03:57
@bolinfest bolinfest force-pushed the pr22401 branch 9 times, most recently from e8ce411 to dfbc24a Compare May 13, 2026 16:14
),
)?;
} else {
if cwd_changed
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This rebind path no longer runs for ordinary workspace-write profiles. current_file_system_sandbox_policy above is already materialized against workspace_roots, so the symbolic ProjectRoots entry checked here is gone by the time we reach this condition. As a result, turn/start calls that only change cwd keep write access anchored to the previous roots. Suggest preserving the old cwd-only behavior for implicit/default-seeded roots, or making the rebind decision from the unmaterialized profile so explicit roots can stay sticky without breaking existing callers.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 0f90ce4: the rebind decision now checks the unmaterialized PermissionProfile filesystem policy for the symbolic project-roots write entry, before materialization removes it. I also added workspace_roots_explicit tracking so cwd-only updates rebind only the implicit [cwd] root; explicit or persisted roots remain stable unless workspaceRoots is updated. Covered by session_configuration_apply_rebinds_implicit_workspace_root_on_cwd_update and session_configuration_apply_preserves_explicit_workspace_roots_on_cwd_update.

/// entries for this thread.
#[experimental("thread/start.workspaceRoots")]
#[serde(default)]
pub workspace_roots: Vec<AbsolutePathBuf>,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding workspaceRoots here while removing the full effective permission profile makes this response lossy. sandbox + workspaceRoots + activePermissionProfile cannot represent richer filesystem state such as deny-read overlays or split read/write policies, and downstream code already has to reconstruct from the legacy sandbox projection. Suggest keeping an exact effective-permissions field in these lifecycle responses, even if activePermissionProfile remains the preferred high-level selector.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 0f90ce4: lifecycle responses now include a read-only exact permissionProfile, materialized with the thread workspaceRoots, in addition to activePermissionProfile, workspaceRoots, and the legacy sandbox projection. Exec/TUI clients prefer that exact field when present and keep the legacy sandbox fallback for older servers. The request APIs still only allow selecting by id or updating workspaceRoots, so clients cannot replace the thread PermissionProfile value through app-server calls.

@bolinfest bolinfest force-pushed the pr22401 branch 3 times, most recently from e6b6753 to d11aa33 Compare May 13, 2026 18:52
Comment thread codex-rs/app-server/src/request_processors/thread_processor.rs Outdated
@bolinfest bolinfest force-pushed the pr22401 branch 5 times, most recently from 5e82c0d to f65d5b5 Compare May 13, 2026 19:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants