[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(