Add managed developer instructions to requirements (#39755)

## What changed

- Add `additional_developer_instructions` to managed requirements and expose it through `configRequirements/read` as `additionalDeveloperInstructions`, independently of ordinary developer instructions.
- Include the managed instructions in model context, emit explicit replacement or removal messages when requirements change, and preserve the current value across compaction, resume, and agent forks without duplication.
- Reject managed instructions whose rendered context exceeds 10,000 estimated tokens.

## Testing

- Cover requirements layering and API serialization, context updates and removal, size validation, repeated model requests, compaction and resume, rollout migration, and agent forks.

GitOrigin-RevId: bc0b70fb7988944c2f68176dff55f5ed61eb46c8
This commit is contained in:
joeflorencio-openai authored and copyberry committed 2026-08-20 17:36:22 +00:00
1 parent 85a1b0e33d
commit ce950dcf26
31 files changed
+540 -19

No files matched your search

+33
View File
@@ -9858,6 +9858,7 @@ async fn test_requirements_web_search_mode_allowlist_does_not_warn_when_unset()
permissions: None,
auto_review: None,
models: None,
additional_developer_instructions: None,
guardian_policy_config: None,
};
let requirement_source = codex_config::RequirementSource::Unknown;
@@ -10518,6 +10519,38 @@ async fn test_untrusted_project_gets_unless_trusted_approval_policy() -> anyhow:
Ok(())
}
#[tokio::test]
async fn oversized_managed_developer_instructions_are_rejected_during_config_load()
-> std::io::Result<()> {
let codex_home = TempDir::new()?;
// The policy text alone fits; rendering its required context markers must not.
let instructions = "x".repeat(codex_utils_string::approx_bytes_for_tokens(
/*tokens*/ 10_000,
));
let error = ConfigBuilder::without_managed_config_for_tests()
.codex_home(codex_home.path().to_path_buf())
.fallback_cwd(Some(codex_home.path().to_path_buf()))
.cloud_config_bundle(
CloudConfigBundleFixture::loader_with_enterprise_requirement(format!(
"additional_developer_instructions = {instructions:?}"
)),
)
.build()
.await
.expect_err("oversized managed instructions must fail during config loading");
assert_eq!(error.kind(), std::io::ErrorKind::InvalidInput);
let message = error.to_string();
assert!(
message.starts_with(
"`additional_developer_instructions` from enterprise-managed requirements Base requirements (req_1) exceeds the model-context limit of 10000 estimated tokens"
),
"unexpected config-load error: {message}"
);
Ok(())
}
#[tokio::test]
async fn requirements_disallowing_default_sandbox_falls_back_to_required_default()
-> std::io::Result<()> {
+8
View File
@@ -1,5 +1,6 @@
use crate::config::edit::ConfigEdit;
use crate::config::edit::ConfigEditsBuilder;
use crate::context::world_state::validate_managed_developer_instructions;
use crate::guardian::BUNDLED_GUARDIAN_POLICY;
use crate::path_utils::normalize_for_native_workdir;
use crate::unified_exec::DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS;
@@ -3108,6 +3109,12 @@ impl Config {
std::io::Error::new(std::io::ErrorKind::InvalidInput, message)
})?;
}
validate_managed_developer_instructions(
config_layer_stack
.requirements()
.additional_developer_instructions
.as_ref(),
)?;
let orchestrator = cfg.orchestrator.as_ref();
let orchestrator_skills_enabled =
resolve_orchestrator_feature_enabled(orchestrator.and_then(|value| value.skills.as_ref()));
@@ -3156,6 +3163,7 @@ impl Config {
enforce_residency,
network: network_requirements,
filesystem: filesystem_requirements,
additional_developer_instructions: _,
guardian_policy_config_source: _,
} = config_layer_stack.requirements().clone();