Allow updating the approval reviewer for active turns (#42121)

## What changed

- Add `approvalsReviewer` to the experimental `turn/settings/update` API.
- Apply reviewer changes to subsequently captured steps and new background approval requests while preserving pending approvals and future-thread defaults.
- Allow reviewer-only updates without `step_model_switching`, while continuing to enforce managed reviewer restrictions and model-required auto review.
- Use an explicit live reviewer update for MCP approvals while retaining refreshed thread defaults for clients that have not set one.

## Testing

- Cover switching between user and automatic review during a live turn, future-turn isolation, and managed-authority rejection.

GitOrigin-RevId: e1216b09c6a7972c2c9eaf4955d8d57d60604ca1
This commit is contained in:
jif authored and copyberry committed 2026-09-01 19:19:52 +00:00
1 parent bc39b0ed59
commit 9112564114
14 files changed
+267 -28

No files matched your search

+15 -8
View File
@@ -48,18 +48,13 @@ fn check_legacy_turn_safety(
|| ignored_models.is_some_and(|models| models.contains(&model.slug))
};
// tools::approvals and guardian::review still route approvals using the
// originating turn's policy, reviewer, and required-model classification.
// Approval policy and required-model classification still have consumers
// using the originating turn. The reviewer is captured separately.
if destination.constrained_approval_policy() != current.constrained_approval_policy()
|| destination.approval_policy() != turn_context.approval_policy()
{
return Err("the destination changes the admitted approval policy".to_string());
}
if destination.approvals_reviewer() != current.approvals_reviewer()
|| destination.approvals_reviewer() != turn_context.config.approvals_reviewer
{
return Err("the destination changes the admitted approvals reviewer".to_string());
}
if required_review
!= requirements
.auto_review_required_for_model(current.selected_collaboration_mode().model())
@@ -237,7 +232,12 @@ impl Session {
turn_id: &str,
update: TurnSettingsUpdate,
) -> TurnSettingsUpdateOutcome {
if !self.features.enabled(Feature::StepModelSwitching) {
let reviewer_only = update.approvals_reviewer.is_some()
&& update.model.is_none()
&& update.effort.is_none()
&& update.summary.is_none()
&& update.service_tier.is_none();
if !reviewer_only && !self.features.enabled(Feature::StepModelSwitching) {
return TurnSettingsUpdateOutcome::Rejected {
reason: "turn settings updates require the step_model_switching feature"
.to_string(),
@@ -265,12 +265,14 @@ impl Session {
return TurnSettingsUpdateOutcome::TargetUnavailable;
};
let TurnSettingsUpdate {
approvals_reviewer,
model,
effort,
summary,
service_tier,
} = update;
let update = StepSettingsUpdate {
approvals_reviewer,
model,
effort,
reasoning_summary: summary,
@@ -313,6 +315,11 @@ impl Session {
)
.map_err(|error| error.to_string())
.and_then(|()| {
// A reviewer-only patch cannot change any model-owned authority.
// Managed reviewer restrictions were checked above.
if reviewer_only {
return Ok(());
}
check_legacy_turn_safety(
&turn_context,
&current,