From abd6d5355c5120a93ecdf8f03267ba4eaa21f437 Mon Sep 17 00:00:00 2001 From: jif-oai Date: Thu, 9 Jul 2026 13:07:17 +0100 Subject: [PATCH] Keep thread skill inspection cache-neutral --- .../request_processors/catalog_processor.rs | 15 ++++-- .../suite/v2/selected_capability_stack.rs | 19 +++++++ codex-rs/core-skills/src/service.rs | 49 ++++++++++++++----- 3 files changed, 68 insertions(+), 15 deletions(-) diff --git a/codex-rs/app-server/src/request_processors/catalog_processor.rs b/codex-rs/app-server/src/request_processors/catalog_processor.rs index 4ce6fe76cd..cccdff72e8 100644 --- a/codex-rs/app-server/src/request_processors/catalog_processor.rs +++ b/codex-rs/app-server/src/request_processors/catalog_processor.rs @@ -508,6 +508,7 @@ impl CatalogRequestProcessor { Some(thread_id) => self.thread_skills(thread_id).await?, None => (Vec::new(), Vec::new()), }; + let inspect_thread = thread_id.is_some(); let config = self.load_latest_config(/*fallback_cwd*/ None).await?; let auth = self.auth_manager.auth().await; @@ -521,7 +522,7 @@ impl CatalogRequestProcessor { .environment_manager() .default_environment() .map(|environment| { - if thread_id.is_some() { + if inspect_thread { environment.get_filesystem_without_reconnect() } else { environment.get_filesystem() @@ -568,9 +569,15 @@ impl CatalogRequestProcessor { config_layer_stack, config.bundled_skills_enabled(), ); - let snapshot = skills_service - .snapshot_for_cwd(&skills_input, force_reload, fs) - .await; + let snapshot = if inspect_thread { + skills_service + .inspect_snapshot_for_cwd(&skills_input, force_reload, fs) + .await + } else { + skills_service + .snapshot_for_cwd(&skills_input, force_reload, fs) + .await + }; let outcome = snapshot.outcome(); let errors = errors_to_info(&outcome.errors); let skills = skills_to_info(&outcome.skills, &outcome.disabled_paths); diff --git a/codex-rs/app-server/tests/suite/v2/selected_capability_stack.rs b/codex-rs/app-server/tests/suite/v2/selected_capability_stack.rs index c95c9240a3..50e2faab42 100644 --- a/codex-rs/app-server/tests/suite/v2/selected_capability_stack.rs +++ b/codex-rs/app-server/tests/suite/v2/selected_capability_stack.rs @@ -210,6 +210,25 @@ async fn selected_capability_stack_tracks_environment_availability_and_resume() (Vec::new(), Vec::new()) ); exec_server_proxy.resume()?; + wait_for_selected_mcp_server(&mut app_server, &inspection_thread_id).await?; + + let request_id = app_server + .send_skills_list_request(SkillsListParams { + cwds: Vec::new(), + force_reload: false, + thread_id: None, + }) + .await?; + let response = timeout( + READ_TIMEOUT, + app_server.read_stream_until_response_message(RequestId::Integer(request_id)), + ) + .await??; + let SkillsListResponse { data, .. } = to_response(response)?; + assert!( + data.iter() + .any(|entry| { entry.skills.iter().any(|skill| skill.name == SKILL_NAME) }) + ); exec_server.kill().await?; drop(app_server); diff --git a/codex-rs/core-skills/src/service.rs b/codex-rs/core-skills/src/service.rs index b4e0d7396c..03e2a6e730 100644 --- a/codex-rs/core-skills/src/service.rs +++ b/codex-rs/core-skills/src/service.rs @@ -180,8 +180,43 @@ impl SkillsService { return snapshot; } + let snapshot = self.load_snapshot_for_cwd(input, fs).await; + if use_cwd_cache { + let mut cache = self + .cache_by_cwd + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + cache.insert(input.cwd.clone(), snapshot.clone()); + } + snapshot + } + + /// Loads a cwd snapshot without updating the shared cwd cache. + /// + /// Existing cached data can still be reused unless `force_reload` is set. + pub async fn inspect_snapshot_for_cwd( + &self, + input: &SkillsLoadInput, + force_reload: bool, + fs: Option>, + ) -> HostSkillsSnapshot { + if fs.is_some() + && !force_reload + && let Some(snapshot) = self.cached_snapshot_for_cwd(&input.cwd) + { + return snapshot; + } + + self.load_snapshot_for_cwd(input, fs).await + } + + async fn load_snapshot_for_cwd( + &self, + input: &SkillsLoadInput, + fs: Option>, + ) -> HostSkillsSnapshot { let mut roots = skill_roots( - fs.clone(), + fs, &input.config_layer_stack, &input.cwd, input.effective_skill_roots.clone(), @@ -192,18 +227,10 @@ impl SkillsService { roots.retain(|root| root.scope != SkillScope::System); } let skill_config_rules = skill_config_rules_from_stack(&input.config_layer_stack); - let snapshot = HostSkillsSnapshot::new(Arc::new( + HostSkillsSnapshot::new(Arc::new( self.build_skill_outcome(input, roots, &skill_config_rules) .await, - )); - if use_cwd_cache { - let mut cache = self - .cache_by_cwd - .write() - .unwrap_or_else(std::sync::PoisonError::into_inner); - cache.insert(input.cwd.clone(), snapshot.clone()); - } - snapshot + )) } #[instrument(level = "trace", skip_all)]