mirror of
https://github.com/openai/codex.git
synced 2026-05-04 05:11:37 +03:00
Use workspace requirements for guardian prompt override (#14727)
## Summary - move `guardian_developer_instructions` from managed config into workspace-managed `requirements.toml` - have guardian continue using the override when present and otherwise fall back to the bundled local guardian prompt - keep the generalized prompt-quality improvements in the shared guardian default prompt - update requirements parsing, layering, schema, and tests for the new source of truth ## Context This replaces the earlier managed-config / MDM rollout plan. The intended rollout path is workspace-managed requirements, including cloud enterprise policies, rather than backend model metadata, Statsig, or Jamf-managed config. That keeps the default/fallback behavior local to `codex-rs` while allowing faster policy updates through the enterprise requirements plane. This is intentionally an admin-managed policy input, not a user preference: the guardian prompt should come either from the bundled `codex-rs` default or from enterprise-managed `requirements.toml`, and normal user/project/session config should not override it. ## Updating The OpenAI Prompt After this lands, the OpenAI-specific guardian prompt should be updated through the workspace Policies UI at `/codex/settings/policies` rather than through Jamf or codex-backend model metadata. Operationally: - open the workspace Policies editor as a Codex admin - edit the default `requirements.toml` policy, or a higher-precedence group-scoped override if we ever want different behavior for a subset of users - set `guardian_developer_instructions = """..."""` to the full OpenAI-specific guardian prompt text - save the policy; codex-backend stores the raw TOML and `codex-rs` fetches the effective requirements file from `/wham/config/requirements` When updating the OpenAI-specific prompt, keep it aligned with the shared default guardian policy in `codex-rs` except for intentional OpenAI-only additions. ## Testing - `cargo check --tests -p codex-core -p codex-config -p codex-cloud-requirements --message-format short` - `cargo run -p codex-core --bin codex-write-config-schema` - `cargo fmt` - `git diff --check` Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
committed by
GitHub
parent
3ce879c646
commit
226241f035
@@ -299,6 +299,7 @@ pub struct ConfigRequirementsToml {
|
||||
pub enforce_residency: Option<ResidencyRequirement>,
|
||||
#[serde(rename = "experimental_network")]
|
||||
pub network: Option<NetworkRequirementsToml>,
|
||||
pub guardian_developer_instructions: Option<String>,
|
||||
}
|
||||
|
||||
/// Value paired with the requirement source it came from, for better error
|
||||
@@ -334,6 +335,7 @@ pub struct ConfigRequirementsWithSources {
|
||||
pub rules: Option<Sourced<RequirementsExecPolicyToml>>,
|
||||
pub enforce_residency: Option<Sourced<ResidencyRequirement>>,
|
||||
pub network: Option<Sourced<NetworkRequirementsToml>>,
|
||||
pub guardian_developer_instructions: Option<Sourced<String>>,
|
||||
}
|
||||
|
||||
impl ConfigRequirementsWithSources {
|
||||
@@ -364,9 +366,17 @@ impl ConfigRequirementsWithSources {
|
||||
rules: _,
|
||||
enforce_residency: _,
|
||||
network: _,
|
||||
guardian_developer_instructions: _,
|
||||
} = &other;
|
||||
|
||||
let mut other = other;
|
||||
if other
|
||||
.guardian_developer_instructions
|
||||
.as_deref()
|
||||
.is_some_and(|value| value.trim().is_empty())
|
||||
{
|
||||
other.guardian_developer_instructions = None;
|
||||
}
|
||||
fill_missing_take!(
|
||||
self,
|
||||
other,
|
||||
@@ -380,6 +390,7 @@ impl ConfigRequirementsWithSources {
|
||||
rules,
|
||||
enforce_residency,
|
||||
network,
|
||||
guardian_developer_instructions,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -403,6 +414,7 @@ impl ConfigRequirementsWithSources {
|
||||
rules,
|
||||
enforce_residency,
|
||||
network,
|
||||
guardian_developer_instructions,
|
||||
} = self;
|
||||
ConfigRequirementsToml {
|
||||
allowed_approval_policies: allowed_approval_policies.map(|sourced| sourced.value),
|
||||
@@ -414,6 +426,8 @@ impl ConfigRequirementsWithSources {
|
||||
rules: rules.map(|sourced| sourced.value),
|
||||
enforce_residency: enforce_residency.map(|sourced| sourced.value),
|
||||
network: network.map(|sourced| sourced.value),
|
||||
guardian_developer_instructions: guardian_developer_instructions
|
||||
.map(|sourced| sourced.value),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -468,6 +482,10 @@ impl ConfigRequirementsToml {
|
||||
&& self.rules.is_none()
|
||||
&& self.enforce_residency.is_none()
|
||||
&& self.network.is_none()
|
||||
&& self
|
||||
.guardian_developer_instructions
|
||||
.as_deref()
|
||||
.is_none_or(|value| value.trim().is_empty())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,6 +503,7 @@ impl TryFrom<ConfigRequirementsWithSources> for ConfigRequirements {
|
||||
rules,
|
||||
enforce_residency,
|
||||
network,
|
||||
guardian_developer_instructions: _guardian_developer_instructions,
|
||||
} = toml;
|
||||
|
||||
let approval_policy = match allowed_approval_policies {
|
||||
@@ -705,6 +724,7 @@ mod tests {
|
||||
rules,
|
||||
enforce_residency,
|
||||
network,
|
||||
guardian_developer_instructions,
|
||||
} = toml;
|
||||
ConfigRequirementsWithSources {
|
||||
allowed_approval_policies: allowed_approval_policies
|
||||
@@ -721,6 +741,8 @@ mod tests {
|
||||
enforce_residency: enforce_residency
|
||||
.map(|value| Sourced::new(value, RequirementSource::Unknown)),
|
||||
network: network.map(|value| Sourced::new(value, RequirementSource::Unknown)),
|
||||
guardian_developer_instructions: guardian_developer_instructions
|
||||
.map(|value| Sourced::new(value, RequirementSource::Unknown)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -743,6 +765,8 @@ mod tests {
|
||||
};
|
||||
let enforce_residency = ResidencyRequirement::Us;
|
||||
let enforce_source = source.clone();
|
||||
let guardian_developer_instructions =
|
||||
"Use the company-managed guardian policy.".to_string();
|
||||
|
||||
// Intentionally constructed without `..Default::default()` so adding a new field to
|
||||
// `ConfigRequirementsToml` forces this test to be updated.
|
||||
@@ -756,6 +780,7 @@ mod tests {
|
||||
rules: None,
|
||||
enforce_residency: Some(enforce_residency),
|
||||
network: None,
|
||||
guardian_developer_instructions: Some(guardian_developer_instructions.clone()),
|
||||
};
|
||||
|
||||
target.merge_unset_fields(source.clone(), other);
|
||||
@@ -767,7 +792,7 @@ mod tests {
|
||||
allowed_approval_policies,
|
||||
source.clone()
|
||||
)),
|
||||
allowed_sandbox_modes: Some(Sourced::new(allowed_sandbox_modes, source)),
|
||||
allowed_sandbox_modes: Some(Sourced::new(allowed_sandbox_modes, source.clone(),)),
|
||||
allowed_web_search_modes: Some(Sourced::new(
|
||||
allowed_web_search_modes,
|
||||
enforce_source.clone(),
|
||||
@@ -781,6 +806,10 @@ mod tests {
|
||||
rules: None,
|
||||
enforce_residency: Some(Sourced::new(enforce_residency, enforce_source)),
|
||||
network: None,
|
||||
guardian_developer_instructions: Some(Sourced::new(
|
||||
guardian_developer_instructions,
|
||||
source,
|
||||
)),
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -815,6 +844,7 @@ mod tests {
|
||||
rules: None,
|
||||
enforce_residency: None,
|
||||
network: None,
|
||||
guardian_developer_instructions: None,
|
||||
}
|
||||
);
|
||||
Ok(())
|
||||
@@ -857,11 +887,78 @@ mod tests {
|
||||
rules: None,
|
||||
enforce_residency: None,
|
||||
network: None,
|
||||
guardian_developer_instructions: None,
|
||||
}
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_unset_fields_ignores_blank_guardian_override() {
|
||||
let mut target = ConfigRequirementsWithSources::default();
|
||||
target.merge_unset_fields(
|
||||
RequirementSource::CloudRequirements,
|
||||
ConfigRequirementsToml {
|
||||
guardian_developer_instructions: Some(" \n\t".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
target.merge_unset_fields(
|
||||
RequirementSource::SystemRequirementsToml {
|
||||
file: system_requirements_toml_file_for_test()
|
||||
.expect("system requirements.toml path"),
|
||||
},
|
||||
ConfigRequirementsToml {
|
||||
guardian_developer_instructions: Some(
|
||||
"Use the system guardian policy.".to_string(),
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
target.guardian_developer_instructions,
|
||||
Some(Sourced::new(
|
||||
"Use the system guardian policy.".to_string(),
|
||||
RequirementSource::SystemRequirementsToml {
|
||||
file: system_requirements_toml_file_for_test()
|
||||
.expect("system requirements.toml path"),
|
||||
},
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_guardian_developer_instructions() -> Result<()> {
|
||||
let requirements: ConfigRequirementsToml = from_str(
|
||||
r#"
|
||||
guardian_developer_instructions = """
|
||||
Use the cloud-managed guardian policy.
|
||||
"""
|
||||
"#,
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
requirements.guardian_developer_instructions.as_deref(),
|
||||
Some("Use the cloud-managed guardian policy.\n")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blank_guardian_developer_instructions_is_empty() -> Result<()> {
|
||||
let requirements: ConfigRequirementsToml = from_str(
|
||||
r#"
|
||||
guardian_developer_instructions = """
|
||||
|
||||
"""
|
||||
"#,
|
||||
)?;
|
||||
|
||||
assert!(requirements.is_empty());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_apps_requirements() -> Result<()> {
|
||||
let toml_str = r#"
|
||||
|
||||
Reference in New Issue
Block a user