Source Guardian REPL policy from model metadata (#41919)

## What changed

- Add `auto_review.node_repl_policy` to model messages for `node_repl` and `cua_repl` reviews.
- Use the reviewer model's configured policy, falling back to the bundled policy when the field is absent and skipping injection when it is explicitly empty.
- Include the effective policy in Guardian session reuse checks and reject unsafe parent-model fallback transitions that would change it.

## Testing

- Cover catalog, bundled, and explicitly empty policies across Node and CUA review paths.
- Verify that policy changes invalidate cached reviewer sessions and incompatible fallback transitions.

GitOrigin-RevId: 51c0bdfed9b464091b6eb47d91ca5a0990d7a315
This commit is contained in:
olliem-oai authored and copyberry committed 2026-08-31 22:08:44 +00:00
1 parent 746798b2f7
commit 865bbf9a69
11 files changed
+144 -24

No files matched your search

@@ -8,6 +8,7 @@ use super::step_settings::StepSettingsUpdate;
use super::turn_context::TurnContext;
use crate::config::Config;
use crate::config::ConstraintResult;
use crate::context::GuardianNodeReplPolicy;
use crate::exec_policy::AllowPrefixRules;
use crate::guardian::BUNDLED_GUARDIAN_POLICY_TEMPLATE;
use codex_features::Feature;
@@ -173,6 +174,17 @@ fn check_legacy_model_safety(
// catalog refresh. V1 uses the admitted config; V2 can use the live config.
// An unchanged explicit reviewer override prevents both fallback paths.
if destination.auto_review_model_override.is_none() {
let destination_node_repl_policy =
GuardianNodeReplPolicy::from_model_messages(destination.model_messages.as_ref());
for model in retained_models {
let policy = GuardianNodeReplPolicy::from_model_messages(model.model_messages.as_ref());
if policy != destination_node_repl_policy {
return Err(
"the destination changes the Guardian parent-fallback node REPL policy"
.to_string(),
);
}
}
for config in [admitted_config, live_config] {
let destination_policy =
config.resolve_guardian_policy(destination.model_messages.as_ref());