diff --git a/codex-rs/core-skills/src/loader.rs b/codex-rs/core-skills/src/loader.rs index aa663e3e60..ed2b657cab 100644 --- a/codex-rs/core-skills/src/loader.rs +++ b/codex-rs/core-skills/src/loader.rs @@ -536,6 +536,9 @@ async fn load_skills_under_root( for warning in warnings { error!("{warning}"); } + if skills.is_empty() { + return; + } let root_uri = PathUri::from_abs_path(root); let resolved_skills = futures::stream::iter(skills) .map(|skill| async move { diff --git a/codex-rs/core-skills/tests/environment_loader.rs b/codex-rs/core-skills/tests/environment_loader.rs index a2c9903aea..ed37fa99a3 100644 --- a/codex-rs/core-skills/tests/environment_loader.rs +++ b/codex-rs/core-skills/tests/environment_loader.rs @@ -412,6 +412,49 @@ async fn reads_skill_files_while_resolving_plugin_namespaces() { ); } +#[tokio::test] +async fn loading_empty_root_skips_namespace_probes() { + use std::sync::Arc; + + use codex_core_skills::loader::SkillRoot; + use codex_core_skills::loader::load_skills_from_roots; + use codex_protocol::protocol::SkillScope; + use codex_utils_absolute_path::test_support::PathBufExt; + + let root = tempdir().expect("tempdir"); + let skills_root = root.path().join("skills"); + fs::create_dir_all(&skills_root).expect("skills dir"); + + let recording = Arc::new(RecordingFileSystem::new( + LOCAL_FS.as_ref(), + ManifestMetadataBehavior::Immediate, + )); + let file_system: Arc = recording.clone(); + let outcome = load_skills_from_roots( + [SkillRoot { + path: skills_root.abs(), + scope: SkillScope::User, + file_system, + plugin_id: None, + plugin_namespace: None, + plugin_root: None, + }], + /*plugin_skill_snapshots*/ None, + ) + .await; + + assert!(outcome.skills.is_empty()); + assert!(outcome.errors.is_empty()); + assert_eq!( + recording.calls(), + FileSystemCalls { + walks: 1, + read_files: Vec::new(), + metadata_files: Vec::new(), + } + ); +} + #[cfg(unix)] #[tokio::test] async fn host_loading_reuses_walk_inventory_for_symlinked_skill_pack() {