Route AGENTS.md loading through environment filesystems (#26205)

## Why

Workspace-specific `AGENTS.md` loading needs to use the selected
environment filesystem so remote workspaces and child agents read
instructions from their actual environment instead of the host
filesystem. The app-server should report the same instruction sources
the initialized thread actually loaded, rather than independently
rescanning configuration and filesystem state.

## What changed

- Introduce `LoadedAgentsMd` to retain ordered user, project, and
internal instructions with their provenance.
- Load and canonicalize workspace `AGENTS.md` paths through the primary
`EnvironmentManager` environment, then render the loaded instructions
when constructing turn context.
- Expose cached loaded instruction sources from initialized threads and
use them for app-server start, resume, and fork responses.
- Preserve global `CODEX_HOME` loading and separator behavior while
excluding empty project files that did not supply model-visible
instructions.
- Add integration coverage for CLI injection, selected-environment
provenance and rendering, empty environment selection, and cached
sources on loaded-thread resume.

## Validation

- `just test -p codex-core agents_md`
- `just test -p codex-core
selected_environment_sources_match_model_visible_instructions`
- `just test -p codex-exec agents_md`
- `just test -p codex-app-server instruction_sources`
- `just test -p codex-app-server --status-level fail`
This commit is contained in:
Adam Perry @ OpenAI
2026-06-04 12:43:07 -07:00
committed by GitHub
Unverified
parent c3fcb0e745
commit e64b469bbc
22 changed files with 740 additions and 137 deletions
+17 -9
View File
@@ -208,10 +208,8 @@ async fn load_config_normalizes_relative_cwd_override() -> std::io::Result<()> {
#[tokio::test]
async fn load_config_loads_global_agents_instructions() -> std::io::Result<()> {
let codex_home = tempdir()?;
std::fs::write(
codex_home.path().join(DEFAULT_AGENTS_MD_FILENAME),
"\n global instructions \n",
)?;
let global_agents_path = codex_home.abs().join(DEFAULT_AGENTS_MD_FILENAME);
std::fs::write(&global_agents_path, "\n global instructions \n")?;
let mut config = Config::load_from_base_config_with_overrides(
ConfigToml::default(),
@@ -221,9 +219,14 @@ async fn load_config_loads_global_agents_instructions() -> std::io::Result<()> {
.await?;
let _ = config.features.enable(Feature::MemoryTool);
let user_instructions = config
.user_instructions
.as_ref()
.expect("global instructions expected");
assert_eq!(user_instructions.text(), "global instructions");
assert_eq!(
config.user_instructions.as_deref(),
Some("global instructions")
user_instructions.sources().collect::<Vec<_>>(),
vec![&global_agents_path]
);
Ok(())
}
@@ -235,7 +238,7 @@ async fn load_config_prefers_global_agents_override_instructions() -> std::io::R
codex_home.path().join(DEFAULT_AGENTS_MD_FILENAME),
"global instructions",
)?;
let global_agents_override_path = codex_home.path().join(LOCAL_AGENTS_MD_FILENAME);
let global_agents_override_path = codex_home.abs().join(LOCAL_AGENTS_MD_FILENAME);
std::fs::write(&global_agents_override_path, "local override instructions")?;
let config = Config::load_from_base_config_with_overrides(
@@ -245,9 +248,14 @@ async fn load_config_prefers_global_agents_override_instructions() -> std::io::R
)
.await?;
let user_instructions = config
.user_instructions
.as_ref()
.expect("global override instructions expected");
assert_eq!(user_instructions.text(), "local override instructions");
assert_eq!(
config.user_instructions.as_deref(),
Some("local override instructions")
user_instructions.sources().collect::<Vec<_>>(),
vec![&global_agents_override_path]
);
Ok(())
}
+3 -3
View File
@@ -1,4 +1,5 @@
use crate::agents_md::AgentsMdManager;
pub use crate::agents_md::LoadedAgentsMd;
use crate::config::edit::ConfigEdit;
use crate::config::edit::ConfigEditsBuilder;
use crate::path_utils::normalize_for_native_workdir;
@@ -645,7 +646,7 @@ pub struct Config {
pub show_raw_agent_reasoning: bool,
/// User-provided instructions from AGENTS.md.
pub user_instructions: Option<String>,
pub user_instructions: Option<LoadedAgentsMd>,
/// Base instructions override.
pub base_instructions: Option<String>,
@@ -2605,8 +2606,7 @@ impl Config {
Some(&codex_home),
&mut startup_warnings,
)
.await
.map(|loaded| loaded.contents);
.await;
// Destructure ConfigOverrides fully to ensure all overrides are applied.
let ConfigOverrides {