[codex] Remove child AGENTS.md prompt experiment (#28993)

## Why

`child_agents_md` is a disabled, under-development experiment that adds
a second model-visible explanation of hierarchical `AGENTS.md` behavior.
Keeping it leaves unused prompt, configuration, documentation, and test
surface.

## What changed

- remove the `ChildAgentsMd` feature and `child_agents_md` config schema
entry
- remove the hierarchical prompt asset, export, and instruction
injection
- remove feature-specific tests and documentation
- keep the generic unstable-feature warning coverage using
`apply_patch_streaming_events`

Normal project `AGENTS.md` discovery and composition are unchanged.

## Testing

- `just test -p codex-features`
- `just test -p codex-prompts`
- `just test -p codex-core agents_md`
- `just test -p codex-core unstable_features_warning`
This commit is contained in:
pakrym-oai
2026-06-18 16:13:07 -07:00
committed by GitHub
Unverified
parent 772c5c5195
commit bb72e151e5
12 changed files with 11 additions and 239 deletions
-6
View File
@@ -446,9 +446,6 @@
"browser_use_external": {
"type": "boolean"
},
"child_agents_md": {
"type": "boolean"
},
"chronicle": {
"type": "boolean"
},
@@ -4689,9 +4686,6 @@
"browser_use_external": {
"type": "boolean"
},
"child_agents_md": {
"type": "boolean"
},
"chronicle": {
"type": "boolean"
},
-9
View File
@@ -26,8 +26,6 @@ use codex_config::merge_toml_values;
use codex_config::project_root_markers_from_config;
use codex_exec_server::ExecutorFileSystem;
use codex_extension_api::UserInstructions;
use codex_features::Feature;
use codex_prompts::HIERARCHICAL_AGENTS_MESSAGE;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_path_uri::PathUri;
use std::io;
@@ -72,13 +70,6 @@ pub(crate) async fn load_project_instructions(
}
}
if config.features.enabled(Feature::ChildAgentsMd) {
loaded.entries.push(InstructionEntry {
contents: HIERARCHICAL_AGENTS_MESSAGE.to_string(),
provenance: InstructionProvenance::Internal,
});
}
(!loaded.is_empty()).then_some(loaded)
}
-96
View File
@@ -909,32 +909,6 @@ async fn secondary_environment_invalid_utf8_does_not_suppress_other_docs() {
assert!(loaded.text().contains("secondary\u{FFFD}doc"));
}
#[tokio::test]
async fn child_agents_guidance_is_appended_once_after_environment_groups() {
let primary = tempfile::tempdir().expect("primary tempdir");
let secondary = tempfile::tempdir().expect("secondary tempdir");
fs::write(primary.path().join("AGENTS.md"), "primary doc").unwrap();
fs::write(secondary.path().join("AGENTS.md"), "secondary doc").unwrap();
let mut config = make_config(&primary, /*limit*/ 4096, /*instructions*/ None).await;
config.features.enable(Feature::ChildAgentsMd).unwrap();
let environments = resolved_local_environments([
("primary", config.cwd.clone()),
("secondary", secondary.abs()),
]);
let loaded = load_project_instructions(
&config.config,
/*user_instructions*/ None,
&environments,
)
.await
.expect("instructions expected");
let text = loaded.text();
assert_eq!(text.matches(HIERARCHICAL_AGENTS_MESSAGE).count(), 1);
assert!(text.ends_with(HIERARCHICAL_AGENTS_MESSAGE));
}
/// If there are existing system instructions but AGENTS.md docs are
/// missing we expect the original instructions to be returned unchanged.
#[tokio::test]
@@ -1098,32 +1072,6 @@ async fn agents_md_paths_preserve_symlinked_cwd() {
assert_eq!(res, "project doc");
}
#[tokio::test]
async fn child_agents_message_after_global_instructions_uses_plain_separator() {
let tmp = tempfile::tempdir().expect("tempdir");
let mut cfg = make_config(&tmp, /*limit*/ 4096, Some("global doc")).await;
cfg.features.enable(Feature::ChildAgentsMd).unwrap();
let loaded = load_agents_md(&cfg).await.expect("instructions expected");
let global_agents = cfg.codex_home.join(DEFAULT_AGENTS_MD_FILENAME);
let expected = LoadedAgentsMd {
user_instructions: Some(UserInstructions {
text: "global doc".to_string(),
source: global_agents,
}),
entries: vec![InstructionEntry {
contents: HIERARCHICAL_AGENTS_MESSAGE.to_string(),
provenance: InstructionProvenance::Internal,
}],
};
assert_eq!(loaded, expected);
assert_eq!(
loaded.text(),
format!("global doc\n\n{HIERARCHICAL_AGENTS_MESSAGE}")
);
}
#[tokio::test]
async fn instruction_sources_include_global_before_agents_md_docs() {
let tmp = tempfile::tempdir().expect("tempdir");
@@ -1162,50 +1110,6 @@ async fn instruction_sources_include_global_before_agents_md_docs() {
);
}
#[tokio::test]
async fn child_agents_message_after_project_docs_is_not_an_instruction_source() {
let tmp = tempfile::tempdir().expect("tempdir");
fs::write(tmp.path().join("AGENTS.md"), "project doc").unwrap();
let mut cfg = make_config(&tmp, /*limit*/ 4096, Some("global doc")).await;
cfg.features.enable(Feature::ChildAgentsMd).unwrap();
let global_agents = cfg.codex_home.join(DEFAULT_AGENTS_MD_FILENAME);
fs::create_dir_all(&cfg.codex_home).unwrap();
fs::write(&global_agents, "global doc").unwrap();
let loaded = load_agents_md(&cfg).await.expect("instructions expected");
let project_agents = cfg.cwd.join("AGENTS.md");
let expected = LoadedAgentsMd {
user_instructions: Some(UserInstructions {
text: "global doc".to_string(),
source: global_agents.clone(),
}),
entries: vec![
InstructionEntry {
contents: "project doc".to_string(),
provenance: project_provenance(project_agents.clone(), cfg.cwd.clone()),
},
InstructionEntry {
contents: HIERARCHICAL_AGENTS_MESSAGE.to_string(),
provenance: InstructionProvenance::Internal,
},
],
};
assert_eq!(loaded, expected);
assert_eq!(
loaded.sources().collect::<Vec<_>>(),
vec![
PathUri::from_abs_path(&global_agents),
PathUri::from_abs_path(&project_agents),
]
);
assert_eq!(
loaded.text(),
format!("global doc{AGENTS_MD_SEPARATOR}project doc\n\n{HIERARCHICAL_AGENTS_MESSAGE}")
);
}
/// AGENTS.override.md is preferred over AGENTS.md when both are present.
#[tokio::test]
async fn agents_local_md_preferred() {
@@ -1,97 +0,0 @@
use codex_features::Feature;
use codex_utils_path_uri::PathUri;
use core_test_support::responses::ev_completed;
use core_test_support::responses::ev_response_created;
use core_test_support::responses::mount_sse_once;
use core_test_support::responses::sse;
use core_test_support::responses::start_mock_server;
use core_test_support::test_codex::test_codex;
const HIERARCHICAL_AGENTS_SNIPPET: &str =
"Files called AGENTS.md commonly appear in many places inside a container";
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn hierarchical_agents_appends_to_project_doc_in_user_instructions() {
let server = start_mock_server().await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let mut builder = test_codex()
.with_config(|config| {
config
.features
.enable(Feature::ChildAgentsMd)
.expect("test config should allow feature update");
})
.with_workspace_setup(|cwd, fs| async move {
let agents_md = cwd.join("AGENTS.md");
let agents_md_uri = PathUri::from_path(&agents_md)?;
fs.write_file(&agents_md_uri, b"be nice".to_vec(), /*sandbox*/ None)
.await?;
Ok::<(), anyhow::Error>(())
});
let test = builder
.build_with_remote_env(&server)
.await
.expect("build test codex");
test.submit_turn("hello").await.expect("submit turn");
let request = resp_mock.single_request();
let user_messages = request.message_input_texts("user");
let instructions = user_messages
.iter()
.find(|text| text.starts_with("# AGENTS.md instructions"))
.expect("instructions message");
assert!(
instructions.contains("be nice"),
"expected AGENTS.md text included: {instructions}"
);
let snippet_pos = instructions
.find(HIERARCHICAL_AGENTS_SNIPPET)
.expect("expected hierarchical agents snippet");
let base_pos = instructions
.find("be nice")
.expect("expected AGENTS.md text");
assert!(
snippet_pos > base_pos,
"expected hierarchical agents message appended after base instructions: {instructions}"
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn hierarchical_agents_emits_when_no_project_doc() {
let server = start_mock_server().await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let mut builder = test_codex().with_config(|config| {
config
.features
.enable(Feature::ChildAgentsMd)
.expect("test config should allow feature update");
});
let test = builder
.build_with_remote_env(&server)
.await
.expect("build test codex");
test.submit_turn("hello").await.expect("submit turn");
let request = resp_mock.single_request();
let user_messages = request.message_input_texts("user");
let instructions = user_messages
.iter()
.find(|text| text.starts_with("# AGENTS.md instructions"))
.expect("instructions message");
assert!(
instructions.contains(HIERARCHICAL_AGENTS_SNIPPET),
"expected hierarchical agents message appended: {instructions}"
);
}
-1
View File
@@ -57,7 +57,6 @@ mod extension_sandbox;
mod fork_thread;
#[cfg(not(target_os = "windows"))]
mod guardian_review;
mod hierarchical_agents;
#[cfg(not(target_os = "windows"))]
mod hooks;
#[cfg(not(target_os = "windows"))]
@@ -21,14 +21,14 @@ async fn emits_warning_when_unstable_features_enabled_via_config() {
let mut config = load_default_config_for_test(&home).await;
config
.features
.enable(Feature::ChildAgentsMd)
.enable(Feature::ApplyPatchStreamingEvents)
.expect("test config should allow feature update");
let user_config_path =
AbsolutePathBuf::from_absolute_path(config.codex_home.join(CONFIG_TOML_FILE))
.expect("absolute user config path");
config.config_layer_stack = config.config_layer_stack.with_user_config(
&user_config_path,
toml! { features = { child_agents_md = true } }.into(),
toml! { features = { apply_patch_streaming_events = true } }.into(),
);
let thread_manager = codex_core::test_support::thread_manager_with_models_provider(
@@ -56,7 +56,7 @@ async fn emits_warning_when_unstable_features_enabled_via_config() {
let EventMsg::Warning(WarningEvent { message }) = warning else {
panic!("expected warning event");
};
assert!(message.contains("child_agents_md"));
assert!(message.contains("apply_patch_streaming_events"));
assert!(message.contains("Under-development features enabled"));
assert!(message.contains("suppress_unstable_features_warning = true"));
}
@@ -67,7 +67,7 @@ async fn suppresses_warning_when_configured() {
let mut config = load_default_config_for_test(&home).await;
config
.features
.enable(Feature::ChildAgentsMd)
.enable(Feature::ApplyPatchStreamingEvents)
.expect("test config should allow feature update");
config.suppress_unstable_features_warning = true;
let user_config_path =
@@ -75,7 +75,7 @@ async fn suppresses_warning_when_configured() {
.expect("absolute user config path");
config.config_layer_stack = config.config_layer_stack.with_user_config(
&user_config_path,
toml! { features = { child_agents_md = true } }.into(),
toml! { features = { apply_patch_streaming_events = true } }.into(),
);
let thread_manager = codex_core::test_support::thread_manager_with_models_provider(
-8
View File
@@ -132,8 +132,6 @@ pub enum Feature {
LocalThreadStoreCompression,
/// Enable the Chronicle sidecar for passive screen-context memories.
Chronicle,
/// Append additional AGENTS.md guidance to user instructions.
ChildAgentsMd,
/// Compress request bodies (zstd) when sending streaming requests to codex-backend.
EnableRequestCompression,
/// Start the managed network proxy for sandboxed sessions.
@@ -910,12 +908,6 @@ pub const FEATURES: &[FeatureSpec] = &[
stage: Stage::UnderDevelopment,
default_enabled: false,
},
FeatureSpec {
id: Feature::ChildAgentsMd,
key: "child_agents_md",
stage: Stage::UnderDevelopment,
default_enabled: false,
},
FeatureSpec {
id: Feature::ApplyPatchFreeform,
key: "apply_patch_freeform",
+6 -3
View File
@@ -707,12 +707,15 @@ fn materialize_resolved_enabled_writes_all_features_and_preserves_custom_config(
#[test]
fn unstable_warning_event_only_mentions_enabled_under_development_features() {
let mut configured_features = Table::new();
configured_features.insert("child_agents_md".to_string(), TomlValue::Boolean(true));
configured_features.insert(
"apply_patch_streaming_events".to_string(),
TomlValue::Boolean(true),
);
configured_features.insert("personality".to_string(), TomlValue::Boolean(true));
configured_features.insert("unknown".to_string(), TomlValue::Boolean(true));
let mut features = Features::with_defaults();
features.enable(Feature::ChildAgentsMd);
features.enable(Feature::ApplyPatchStreamingEvents);
let warning = unstable_features_warning_event(
Some(&configured_features),
@@ -725,7 +728,7 @@ fn unstable_warning_event_only_mentions_enabled_under_development_features() {
let EventMsg::Warning(WarningEvent { message }) = warning.msg else {
panic!("expected warning event");
};
assert!(message.contains("child_agents_md"));
assert!(message.contains("apply_patch_streaming_events"));
assert!(!message.contains("personality"));
assert!(message.contains("/tmp/config.toml"));
}
-1
View File
@@ -1 +0,0 @@
pub const HIERARCHICAL_AGENTS_MESSAGE: &str = include_str!("../templates/agents/hierarchical.md");
-2
View File
@@ -1,4 +1,3 @@
mod agents;
mod apply_patch;
mod compact;
mod goals;
@@ -7,7 +6,6 @@ mod realtime;
mod review_exit;
mod review_request;
pub use agents::HIERARCHICAL_AGENTS_MESSAGE;
pub use apply_patch::APPLY_PATCH_TOOL_INSTRUCTIONS;
pub use compact::SUMMARIZATION_PROMPT;
pub use compact::SUMMARY_PREFIX;
@@ -1,7 +0,0 @@
Files called AGENTS.md commonly appear in many places inside a container - at "/", in "~", deep within git repositories, or in any other directory; their location is not limited to version-controlled folders.
Their purpose is to pass along human guidance to you, the agent. Such guidance can include coding standards, explanations of the project layout, steps for building or testing, and even wording that must accompany a GitHub pull-request description produced by the agent; all of it is to be followed.
Each AGENTS.md governs the entire directory that contains it and every child directory beneath that point. Whenever you change a file, you have to comply with every AGENTS.md whose scope covers that file. Naming conventions, stylistic rules and similar directives are restricted to the code that falls inside that scope unless the document explicitly states otherwise.
When two AGENTS.md files disagree, the one located deeper in the directory structure overrides the higher-level file, while instructions given directly in the prompt by the system, developer, or user outrank any AGENTS.md content.
-4
View File
@@ -1,7 +1,3 @@
# AGENTS.md
For information about AGENTS.md, see [this documentation](https://developers.openai.com/codex/guides/agents-md).
## Hierarchical agents message
When the `child_agents_md` feature flag is enabled (via `[features]` in `config.toml`), Codex appends additional guidance about AGENTS.md scope and precedence to the user instructions message and emits that message even when no AGENTS.md is present.