fix: preserve auto review across config and delegation (#26230)

## Why

Auto Review should remain the effective approval reviewer when settings
cross runtime boundaries. A config or app-server round trip must not
change the reviewer identity, and delegated work must not silently fall
back to user review.

This requires both a stable canonical serialized value and propagation
of the effective setting. `auto_review` is the canonical value across
protocol and app-server output, while `guardian_subagent` remains
accepted as backward-compatible input.

## What changed

- serialize `ApprovalsReviewer::AutoReview` consistently as
`auto_review` across core protocol and app-server v2
- continue accepting `guardian_subagent` when reading existing config or
client requests
- carry the active turn's approval reviewer into spawned agents
- update config/debug expectations and add delegated-task regression
coverage

## Scope

This does not change Guardian policy or remove compatibility with
existing `guardian_subagent` inputs. It preserves the selected reviewer
across serialization, config reloads, app-server settings, and delegated
task setup.

Related Guardian changes are split independently:

- #26231 adds denials and soft denials
- #26334 retries transient reviewer failures
- #26333 reuses narrowly scoped low-risk approvals
- #26232 adds TUI denial recovery

## Validation

- `just test -p codex-app-server-protocol` (224 passed)
- regression coverage for delegated task reviewer propagation
- serialization coverage for canonical `auto_review` output and legacy
`guardian_subagent` input

---------

Co-authored-by: saud-oai <saud@openai.com>
This commit is contained in:
viyatb-oai
2026-06-08 18:59:50 +00:00
committed by GitHub
co-authored by saud-oai
parent 2375cb6449
commit 9e0d7f02c9
8 changed files with 16 additions and 10 deletions
@@ -262,6 +262,7 @@ pub(crate) fn apply_spawn_agent_runtime_overrides(
.map_err(|err| {
FunctionCallError::RespondToModel(format!("approval_policy is invalid: {err}"))
})?;
config.approvals_reviewer = turn.config.approvals_reviewer;
config.permissions.shell_environment_policy = turn.shell_environment_policy.clone();
config.codex_linux_sandbox_exe = turn.codex_linux_sandbox_exe.clone();
#[allow(deprecated)]
@@ -24,6 +24,7 @@ use codex_model_provider::create_model_provider;
use codex_model_provider_info::built_in_model_providers;
use codex_protocol::AgentPath;
use codex_protocol::ThreadId;
use codex_protocol::config_types::ApprovalsReviewer;
use codex_protocol::config_types::ServiceTier;
use codex_protocol::config_types::ShellEnvironmentPolicy;
use codex_protocol::models::BaseInstructions;
@@ -2331,6 +2332,9 @@ async fn spawn_agent_reapplies_runtime_sandbox_after_role_config() {
turn.approval_policy
.set(AskForApproval::OnRequest)
.expect("approval policy should be set");
let mut config = (*turn.config).clone();
config.approvals_reviewer = ApprovalsReviewer::AutoReview;
set_turn_config(&mut turn, config);
turn.permission_profile = expected_permission_profile.clone();
assert_ne!(
expected_permission_profile,
@@ -2370,6 +2374,7 @@ async fn spawn_agent_reapplies_runtime_sandbox_after_role_config() {
.await;
assert_eq!(snapshot.sandbox_policy(), expected_sandbox);
assert_eq!(snapshot.approval_policy, AskForApproval::OnRequest);
assert_eq!(snapshot.approvals_reviewer, ApprovalsReviewer::AutoReview);
assert_eq!(snapshot.permission_profile, expected_permission_profile);
let child_thread = manager
.get_thread(agent_id)