From 9d317b19b6486ded2be53f8fd7ecb8cce543b717 Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Mon, 15 Jun 2026 20:12:56 +0800 Subject: [PATCH] {"schema":"decodex/commit/1","summary":"Persist upstream Radar review artifacts","authority":"manual"} --- .../github/bundles/openai-codex-pr-27607.json | 117 ++++++++++++ .../github/bundles/openai-codex-pr-27870.json | 150 +++++++++++++++ .../github/bundles/openai-codex-pr-27884.json | 179 ++++++++++++++++++ .../github/bundles/openai-codex-pr-28001.json | 42 ++++ .../github/impact/openai-codex-pr-27607.json | 47 +++++ .../github/impact/openai-codex-pr-27870.json | 47 +++++ .../github/impact/openai-codex-pr-27884.json | 47 +++++ .../github/impact/openai-codex-pr-28001.json | 38 ++++ .../review-queue/openai-codex-latest.json | 173 +++++++++-------- .../reviews/openai-codex-pr-27607.review.json | 71 +++++++ .../reviews/openai-codex-pr-27870.review.json | 75 ++++++++ .../reviews/openai-codex-pr-27884.review.json | 74 ++++++++ .../reviews/openai-codex-pr-28001.review.json | 52 +++++ .../openai-codex-pr-27607.json | 59 ++++++ .../openai-codex-pr-27870.json | 59 ++++++ .../openai-codex-pr-27884.json | 59 ++++++ 16 files changed, 1212 insertions(+), 77 deletions(-) create mode 100644 artifacts/github/bundles/openai-codex-pr-27607.json create mode 100644 artifacts/github/bundles/openai-codex-pr-27870.json create mode 100644 artifacts/github/bundles/openai-codex-pr-27884.json create mode 100644 artifacts/github/bundles/openai-codex-pr-28001.json create mode 100644 artifacts/github/impact/openai-codex-pr-27607.json create mode 100644 artifacts/github/impact/openai-codex-pr-27870.json create mode 100644 artifacts/github/impact/openai-codex-pr-27884.json create mode 100644 artifacts/github/impact/openai-codex-pr-28001.json create mode 100644 artifacts/github/reviews/openai-codex-pr-27607.review.json create mode 100644 artifacts/github/reviews/openai-codex-pr-27870.review.json create mode 100644 artifacts/github/reviews/openai-codex-pr-27884.review.json create mode 100644 artifacts/github/reviews/openai-codex-pr-28001.review.json create mode 100644 artifacts/github/social-candidates/openai-codex-pr-27607.json create mode 100644 artifacts/github/social-candidates/openai-codex-pr-27870.json create mode 100644 artifacts/github/social-candidates/openai-codex-pr-27884.json diff --git a/artifacts/github/bundles/openai-codex-pr-27607.json b/artifacts/github/bundles/openai-codex-pr-27607.json new file mode 100644 index 000000000..0aebde8e4 --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27607.json @@ -0,0 +1,117 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "felixxia-oai", + "committed_at": "2026-06-11T13:39:30Z", + "message": "Dedupe plugin MCPs by app declaration name", + "sha": "6090e9d2498277d9f8720d860e6e89142d9e45a9", + "url": "https://github.com/openai/codex/commit/6090e9d2498277d9f8720d860e6e89142d9e45a9" + }, + { + "author": "felixxia-oai", + "committed_at": "2026-06-12T10:09:46Z", + "message": "Use app declarations for plugin app parsing", + "sha": "97a3f22d9eaa499ee384674a4f47788a2c0e219e", + "url": "https://github.com/openai/codex/commit/97a3f22d9eaa499ee384674a4f47788a2c0e219e" + }, + { + "author": "xl-openai", + "committed_at": "2026-06-13T03:39:58Z", + "message": "cleanup", + "sha": "8235bab021b5b77dd1999487ab3082fafca3069a", + "url": "https://github.com/openai/codex/commit/8235bab021b5b77dd1999487ab3082fafca3069a" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "MCP", + "SIWC", + "PR1", + "PR2", + "PR3", + "PR4", + "PR5", + "--all", + "--test", + "--check", + "LOCAL_FS", + "CONFIG_TOML_FILE", + "SAMPLE_PLUGIN_CONFIG_NAME", + "MAX_CAPABILITY_SUMMARY_DESCRIPTION_LEN" + ], + "files": [ + { + "additions": 53, + "deletions": 48, + "patch_excerpt": "@@ -1442,7 +1442,9 @@ impl PluginRequestProcessor {\n .await;\n }\n \n- let plugin_apps = load_plugin_apps(result.installed_path.as_path()).await;\n+ let plugin_app_declarations = load_plugin_apps(result.installed_path.as_path()).await;\n+ let plugin_apps =\n+ codex_plugin::app_connector_ids_from_declarations(&plugin_app_declarations);\n let auth = self.auth_manager.auth().await;\n let apps_needing_auth = self\n .plugin_apps_needing_auth_for_install(\n@@ -1559,52 +1561,55 @@ impl PluginRequestProcessor {\n }\n \n let is_chatgpt_auth = auth.as_ref().is_some_and(CodexAuth::is_chatgpt_auth);\n- let apps_needing_auth =\n- if let Some(app_ids_needing_auth) = install_result.app_ids_needing_auth {\n- if app_ids_needing_auth.is_empty()\n- || !config.features.apps_...", + "path": "codex-rs/app-server/src/request_processors/plugins.rs", + "status": "modified" + }, + { + "additions": 32, + "deletions": 42, + "patch_excerpt": "@@ -25,13 +25,15 @@ use codex_exec_server::LOCAL_FS;\n use codex_mcp::PluginMcpServerPlacement;\n use codex_mcp::parse_plugin_mcp_config;\n use codex_plugin::AppConnectorId;\n+use codex_plugin::AppDeclaration;\n use codex_plugin::LoadedPlugin;\n use codex_plugin::PluginCapabilitySummary;\n use codex_plugin::PluginHookSource;\n use codex_plugin::PluginId;\n use codex_plugin::PluginIdError;\n use codex_plugin::PluginLoadOutcome;\n use codex_plugin::PluginTelemetryMetadata;\n+use codex_plugin::app_connector_ids_from_declarations;\n use codex_protocol::protocol::Product;\n use codex_protocol::protocol::SkillScope;\n use codex_utils_absolute_path::AbsolutePathBuf;\n@@ -63,12 +65,6 @@ pub struct PluginHookLoadOutcome {\n pub hook_load_warnings: Vec,\n }\n \n-#[derive(Debug, Clone, PartialEq, Eq)]\n-pub struct PluginAppMetadata {\n- pub id: AppConnectorId,\n- pub category: Option,\n-}\n-\n enum...", + "path": "codex-rs/core-plugins/src/loader.rs", + "status": "modified" + }, + { + "additions": 21, + "deletions": 8, + "patch_excerpt": "@@ -5,7 +5,7 @@ use crate::loader::PluginHookLoadOutcome;\n use crate::loader::configured_curated_plugin_ids_from_codex_home;\n use crate::loader::curated_plugin_cache_version;\n use crate::loader::installed_plugin_telemetry_metadata;\n-use crate::loader::load_plugin_app_metadata;\n+use crate::loader::load_plugin_apps;\n use crate::loader::load_plugin_hooks;\n use crate::loader::load_plugin_hooks_from_layer_stack;\n use crate::loader::load_plugin_mcp_servers;\n@@ -63,6 +63,7 @@ use codex_plugin::AppConnectorId;\n use codex_plugin::PluginCapabilitySummary;\n use codex_plugin::PluginId;\n use codex_plugin::PluginIdError;\n+use codex_plugin::app_connector_ids_from_declarations;\n use codex_plugin::prompt_safe_plugin_description;\n use codex_protocol::protocol::HookEventName;\n use codex_protocol::protocol::Product;\n@@ -214,7 +215,14 @@ fn project_plugin_load_outcome_for_auth(\n for plugin in &mut plugin...", + "path": "codex-rs/core-plugins/src/manager.rs", + "status": "modified" + }, + { + "additions": 189, + "deletions": 22, + "patch_excerpt": "@@ -30,6 +30,7 @@ use codex_config::McpServerOAuthConfig;\n use codex_config::McpServerToolConfig;\n use codex_config::types::McpServerTransportConfig;\n use codex_login::CodexAuth;\n+use codex_plugin::AppDeclaration;\n use codex_protocol::protocol::HookEventName;\n use codex_protocol::protocol::Product;\n use codex_utils_absolute_path::test_support::PathBufExt;\n@@ -94,10 +95,27 @@ fn write_auth_projection_plugin(codex_home: &Path, name: &str, include_app: bool\n ),\n );\n if include_app {\n- write_file(\n- &plugin_root.join(\".app.json\"),\n- &format!(r#\"{{\"apps\":{{\"{name}\":{{\"id\":\"connector_{name}\"}}}}}}\"#),\n- );\n+ write_auth_projection_app(codex_home, name, name);\n+ }\n+}\n+\n+fn write_auth_projection_app(codex_home: &Path, plugin_name: &str, app_name: &str) {\n+ let plugin_root = codex_home\n+ .join(\"plugins/cache\")\n+ .join(\"...", + "path": "codex-rs/core-plugins/src/manager_tests.rs", + "status": "modified" + }, + { + "additions": 92, + "deletions": 6, + "patch_excerpt": "@@ -86,16 +86,22 @@ fn write_plugin_mcp_plugin(home: &TempDir, command: &str) {\n }\n \n fn write_plugin_app_plugin(home: &TempDir) {\n+ write_plugin_app_plugin_with_name(home, \"sample\");\n+}\n+\n+fn write_plugin_app_plugin_with_name(home: &TempDir, app_name: &str) {\n let plugin_root = write_sample_plugin_manifest_and_config(home);\n std::fs::write(\n plugin_root.join(\".app.json\"),\n- r#\"{\n- \"apps\": {\n- \"calendar\": {\n+ format!(\n+ r#\"{{\n+ \"apps\": {{\n+ \"{app_name}\": {{\n \"id\": \"calendar\"\n- }\n- }\n-}\"#,\n+ }}\n+ }}\n+}}\"#\n+ ),\n )\n .expect(\"write plugin app config\");\n }\n@@ -316,6 +322,86 @@ async fn explicit_plugin_mentions_use_apps_for_chatgpt_dual_surface_plugins() ->\n Ok(())\n }\n \n+#[tokio::test(flavor = \"multi_thread\", worker_threads = 2)]\n+async fn explicit_plugin_mentions_keep_non_conflicting_mcp_for_chatgpt_auth() ->...", + "path": "codex-rs/core/tests/suite/plugins.rs", + "status": "modified" + }, + { + "additions": 22, + "deletions": 0, + "patch_excerpt": "@@ -1,5 +1,7 @@\n //! Shared plugin package models, source providers, identifiers, and telemetry summaries.\n \n+use std::collections::HashSet;\n+\n pub use codex_utils_plugins::mention_syntax;\n pub use codex_utils_plugins::plugin_namespace_for_skill_path;\n \n@@ -26,6 +28,26 @@ pub use provider::ResolvedPluginLocation;\n #[derive(Debug, Clone, PartialEq, Eq, Hash)]\n pub struct AppConnectorId(pub String);\n \n+#[derive(Debug, Clone, PartialEq, Eq)]\n+pub struct AppDeclaration {\n+ pub name: String,\n+ pub connector_id: AppConnectorId,\n+ pub category: Option,\n+}\n+\n+pub fn app_connector_ids_from_declarations<'a>(\n+ app_declarations: impl IntoIterator,\n+) -> Vec {\n+ let mut connector_ids = Vec::new();\n+ let mut seen_connector_ids = HashSet::new();\n+ for app in app_declarations {\n+ if seen_connector_ids.insert(&app.connector_i...", + "path": "codex-rs/plugin/src/lib.rs", + "status": "modified" + }, + { + "additions": 10, + "deletions": 14, + "patch_excerpt": "@@ -5,8 +5,10 @@ use codex_utils_absolute_path::AbsolutePathBuf;\n use codex_utils_plugins::PluginSkillRoot;\n \n use crate::AppConnectorId;\n+use crate::AppDeclaration;\n use crate::PluginCapabilitySummary;\n use crate::PluginHookSource;\n+use crate::app_connector_ids_from_declarations;\n \n const MAX_CAPABILITY_SUMMARY_DESCRIPTION_LEN: usize = 1024;\n \n@@ -22,7 +24,7 @@ pub struct LoadedPlugin {\n pub disabled_skill_paths: HashSet,\n pub has_enabled_skills: bool,\n pub mcp_servers: HashMap,\n- pub apps: Vec,\n+ pub apps: Vec,\n pub hook_sources: Vec,\n pub hook_load_warnings: Vec,\n pub error: Option,\n@@ -53,7 +55,7 @@ fn plugin_capability_summary_from_loaded(\n description: prompt_safe_plugin_description(plugin.manifest_description.as_deref()),\n has_skills: pl...", + "path": "codex-rs/plugin/src/load_outcome.rs", + "status": "modified" + } + ], + "linked_issues": [ + "#27652", + "#27459", + "#27607", + "#27602", + "#27461" + ], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Context\n\nThis is the next step in the plugin auth-routing stack. The earlier PRs make `PluginsManager` auth-aware and move the broad App/MCP surface decision into that layer. This PR narrows the ChatGPT/SIWC behavior so we only hide a plugin MCP server when it conflicts with an App declaration of the same name.\n\nIn product terms: if a plugin exposes both an App route and MCP route for `foo`, ChatGPT/SIWC sessions should use the App route for `foo`. If the same plugin also exposes a separate MCP server like `foo2`, that MCP server should remain available.\n\n```json\n// .app.json\n{\n \"apps\": {\n \"foo\": {\n \"id\": \"connector_abc\"\n }\n }\n}\n```\n\n```json\n// .mcp.json\n{\n \"mcpServers\": {\n \"foo\": {\n \"url\": \"https://mcp.foo.com/mcp\"\n },\n \"foo2\": {\n \"url\": \"https://mcp.foo2.com/mcp\"\n }\n }\n}\n```\n\n## Stack\n\n- PR1: #27652 seed plugin manager auth at construction.\n- PR2: #27459 route plugin surfaces by auth mode.\n- PR3: #27607 dedupe plugin MCP servers by App declaration name.\n- PR4: #27602 preserve plugin Apps in connector listings.\n- PR5: #27461 skip install-time plugin MCP OAuth for matching App routes.\n\n## Summary\n\n- Preserve App declaration names in loaded plugin metadata.\n- Keep public effective App outputs as deduped connector IDs for existing callers.\n- For ChatGPT/SIWC, suppress only plugin MCP servers whose names match declared App names.\n\n## Validation\n\n```bash\ncargo fmt --all\ncargo test -p codex-core-plugins plugin_auth_projection\ncargo test -p codex-core-plugins effective_apps\ncargo test -p codex-core-plugins read_plugin_for_config_installed_git_source_reads_from_cache_without_cloning\ncargo test -p codex-core explicit_plugin_mentions_use_apps_for_chatgpt_dual_surface_plugins\ncargo test -p codex-core explicit_plugin_mentions_keep_non_conflicting_mcp_for_chatgpt_auth\ncargo test -p codex-app-server --test all plugin_install_filters_disallowed_apps_needing_auth\ngit diff --check\n```", + "labels": [], + "merged_at": "2026-06-14T00:53:09Z", + "number": 27607, + "state": "merged", + "title": "[codex] Dedupe plugin MCPs by app declaration name", + "url": "https://github.com/openai/codex/pull/27607" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-27870.json b/artifacts/github/bundles/openai-codex-pr-27870.json new file mode 100644 index 000000000..44db2a1df --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27870.json @@ -0,0 +1,150 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "jif-oai", + "committed_at": "2026-06-12T14:21:48Z", + "message": "Resolve selected plugin MCP registrations", + "sha": "8489afd8e3f3a547b47d569cfc91a389af125b34", + "url": "https://github.com/openai/codex/commit/8489afd8e3f3a547b47d569cfc91a389af125b34" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-12T15:16:17Z", + "message": "Use selected MCP winner approval policy", + "sha": "a7578adf4ccdae18f4cc9d3085d5aac15a431a3d", + "url": "https://github.com/openai/codex/commit/a7578adf4ccdae18f4cc9d3085d5aac15a431a3d" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-12T14:23:19Z", + "message": "Discover MCP servers in selected executor plugins", + "sha": "8b78cf44bc9452427916b53c2a7c1b99a2b89434", + "url": "https://github.com/openai/codex/commit/8b78cf44bc9452427916b53c2a7c1b99a2b89434" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-12T15:18:40Z", + "message": "Narrow executor plugin access to its filesystem", + "sha": "b7180839381890e258ba75bfbf2b825b5064a41f", + "url": "https://github.com/openai/codex/commit/b7180839381890e258ba75bfbf2b825b5064a41f" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-15T09:33:11Z", + "message": "Merge main into jif/selected-executor-plugin-mcp-registration", + "sha": "a5453ca8f4c3c24b473c031811c1e36c3185efb8", + "url": "https://github.com/openai/codex/commit/a5453ca8f4c3c24b473c031811c1e36c3185efb8" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "MCP", + "HTTP", + "DEFAULT_MCP_CONFIG_FILE", + "MCP_CONFIG_CONTENTS", + "CODEX_APPS_MCP_SERVER_NAME", + "LOCAL_ENVIRONMENT_ID" + ], + "files": [ + { + "additions": 8, + "deletions": 0, + "patch_excerpt": "@@ -3268,13 +3268,21 @@ dependencies = [\n \"codex-config\",\n \"codex-core\",\n \"codex-core-plugins\",\n+ \"codex-exec-server\",\n \"codex-extension-api\",\n \"codex-features\",\n \"codex-login\",\n \"codex-mcp\",\n+ \"codex-plugin\",\n+ \"codex-protocol\",\n+ \"codex-utils-absolute-path\",\n+ \"codex-utils-path-uri\",\n \"pretty_assertions\",\n+ \"serde_json\",\n \"tempfile\",\n+ \"thiserror 2.0.18\",\n \"tokio\",\n+ \"tracing\",\n ]\n \n [[package]]", + "path": "codex-rs/Cargo.lock", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -45,3 +45,4 @@ pub use marketplace_upgrade::ConfiguredMarketplaceUpgradeError as PluginMarketpl\n pub use marketplace_upgrade::ConfiguredMarketplaceUpgradeOutcome as PluginMarketplaceUpgradeOutcome;\n pub use provider::ExecutorPluginProvider;\n pub use provider::ExecutorPluginProviderError;\n+pub use provider::ResolvedExecutorPlugin;", + "path": "codex-rs/core-plugins/src/lib.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 8, + "patch_excerpt": "@@ -662,14 +662,7 @@ async fn load_plugin(\n restriction_product,\n skill_config_rules,\n } => {\n- loaded_plugin.manifest_name = manifest\n- .interface\n- .as_ref()\n- .and_then(|interface| interface.display_name.as_deref())\n- .map(str::trim)\n- .filter(|display_name| !display_name.is_empty())\n- .map(str::to_string)\n- .or_else(|| Some(manifest.name.clone()));\n+ loaded_plugin.manifest_name = Some(manifest.display_name().to_string());\n loaded_plugin.manifest_description = manifest.description.clone();\n loaded_plugin.skill_roots = plugin_skill_roots(&plugin_root, manifest_paths);\n let resolved_skills = load_plugin_skills(", + "path": "codex-rs/core-plugins/src/loader.rs", + "status": "modified" + }, + { + "additions": 40, + "deletions": 7, + "patch_excerpt": "@@ -77,22 +77,38 @@ pub struct ExecutorPluginProvider {\n environment_manager: Arc,\n }\n \n+/// A resolved plugin paired with the concrete filesystem used to read it.\n+#[derive(Clone)]\n+pub struct ResolvedExecutorPlugin {\n+ plugin: ResolvedPlugin,\n+ file_system: Arc,\n+}\n+\n+impl ResolvedExecutorPlugin {\n+ /// Returns the source-neutral plugin descriptor.\n+ pub fn plugin(&self) -> &ResolvedPlugin {\n+ &self.plugin\n+ }\n+\n+ /// Returns the concrete filesystem that resolved the descriptor.\n+ pub fn file_system(&self) -> &dyn ExecutorFileSystem {\n+ self.file_system.as_ref()\n+ }\n+}\n+\n impl ExecutorPluginProvider {\n /// Creates a provider backed by the active execution environments.\n pub fn new(environment_manager: Arc) -> Self {\n Self {\n environment_manager,\n ...", + "path": "codex-rs/core-plugins/src/provider.rs", + "status": "modified" + }, + { + "additions": 11, + "deletions": 3, + "patch_excerpt": "@@ -7,21 +7,29 @@ version.workspace = true\n [lib]\n name = \"codex_mcp_extension\"\n path = \"src/lib.rs\"\n-test = false\n doctest = false\n \n [lints]\n workspace = true\n \n [dependencies]\n codex-core = { workspace = true }\n+codex-core-plugins = { workspace = true }\n+codex-config = { workspace = true }\n+codex-exec-server = { workspace = true }\n codex-extension-api = { workspace = true }\n codex-features = { workspace = true }\n codex-mcp = { workspace = true }\n+codex-plugin = { workspace = true }\n+codex-protocol = { workspace = true }\n+codex-utils-absolute-path = { workspace = true }\n+codex-utils-path-uri = { workspace = true }\n+serde_json = { workspace = true }\n+thiserror = { workspace = true }\n+tracing = { workspace = true }\n+tokio = { workspace = true, features = [\"sync\"] }\n \n [dev-dependencies]\n-codex-config = { workspace = true }\n-codex-core-plugins = { workspace = true }\n codex-login = { works...", + "path": "codex-rs/ext/mcp/Cargo.toml", + "status": "modified" + }, + { + "additions": 139, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,139 @@\n+use codex_core::config::Config;\n+use codex_core_plugins::ExecutorPluginProvider;\n+use codex_exec_server::EnvironmentManager;\n+use codex_extension_api::ExtensionDataInit;\n+use codex_extension_api::ExtensionFuture;\n+use codex_extension_api::McpServerContribution;\n+use codex_extension_api::McpServerContributionContext;\n+use codex_extension_api::McpServerContributor;\n+use codex_protocol::capabilities::SelectedCapabilityRoot;\n+use std::collections::HashMap;\n+use std::sync::Arc;\n+use tokio::sync::OnceCell;\n+\n+use self::provider::ExecutorPluginMcpProvider;\n+\n+mod provider;\n+\n+/// Frozen MCP declarations for one selected package.\n+///\n+/// Each server config retains the stable logical environment ID. Reconnection may replace the\n+/// concrete environment instance without changing that authority.\n+#[derive(Clone)]\n+struct SelectedPluginMcpServers {\n+ plugin_id: String,\n+ ...", + "path": "codex-rs/ext/mcp/src/executor_plugin.rs", + "status": "added" + }, + { + "additions": 120, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,120 @@\n+use codex_config::McpServerConfig;\n+use codex_config::McpServerTransportConfig;\n+use codex_core_plugins::ResolvedExecutorPlugin;\n+use codex_exec_server::ExecutorFileSystem;\n+use codex_mcp::PluginMcpServerPlacement;\n+use codex_mcp::parse_plugin_mcp_config;\n+use codex_plugin::PluginResourceLocator;\n+use codex_plugin::ResolvedPlugin;\n+use codex_plugin::ResolvedPluginLocation;\n+use codex_utils_absolute_path::AbsolutePathBuf;\n+use codex_utils_path_uri::PathUri;\n+use std::io;\n+use thiserror::Error;\n+\n+const DEFAULT_MCP_CONFIG_FILE: &str = \".mcp.json\";\n+\n+/// Loads MCP declarations from resolved plugins through their owning executor.\n+#[derive(Clone, Copy, Debug, Default)]\n+pub(super) struct ExecutorPluginMcpProvider;\n+\n+/// Failure to load an executor plugin's MCP declarations.\n+#[derive(Debug, Error)]\n+pub(super) enum ExecutorPluginMcpProviderError {\n+ #[error(\"failed to...", + "path": "codex-rs/ext/mcp/src/executor_plugin/provider.rs", + "status": "added" + }, + { + "additions": 266, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,266 @@\n+use super::DEFAULT_MCP_CONFIG_FILE;\n+use super::ExecutorPluginMcpProviderError;\n+use super::load_from_file_system;\n+use codex_config::McpServerConfig;\n+use codex_config::McpServerTransportConfig;\n+use codex_exec_server::CopyOptions;\n+use codex_exec_server::CreateDirectoryOptions;\n+use codex_exec_server::ExecutorFileSystem;\n+use codex_exec_server::ExecutorFileSystemFuture;\n+use codex_exec_server::FileMetadata;\n+use codex_exec_server::FileSystemResult;\n+use codex_exec_server::FileSystemSandboxContext;\n+use codex_exec_server::ReadDirectoryEntry;\n+use codex_exec_server::RemoveOptions;\n+use codex_plugin::ResolvedPlugin;\n+use codex_plugin::manifest::PluginManifest;\n+use codex_plugin::manifest::PluginManifestPaths;\n+use codex_utils_absolute_path::AbsolutePathBuf;\n+use codex_utils_path_uri::PathUri;\n+use pretty_assertions::assert_eq;\n+use std::collections::HashMap;\n+use std::i...", + "path": "codex-rs/ext/mcp/src/executor_plugin/provider_tests.rs", + "status": "added" + }, + { + "additions": 19, + "deletions": 0, + "patch_excerpt": "@@ -7,6 +7,8 @@ use codex_extension_api::McpServerContributor;\n use codex_mcp::CODEX_APPS_MCP_SERVER_NAME;\n use codex_mcp::hosted_plugin_runtime_mcp_server_config;\n \n+mod executor_plugin;\n+\n struct HostedPluginRuntimeExtension;\n \n impl McpServerContributor for HostedPluginRuntimeExtension {\n@@ -39,3 +41,20 @@ impl McpServerContributor for HostedPluginRuntimeExtension {\n pub fn install(builder: &mut ExtensionRegistryBuilder) {\n builder.mcp_server_contributor(std::sync::Arc::new(HostedPluginRuntimeExtension));\n }\n+\n+/// Installs discovery for MCP servers declared by thread-selected executor plugins.\n+pub fn install_executor_plugins(\n+ builder: &mut ExtensionRegistryBuilder,\n+ environment_manager: std::sync::Arc,\n+) {\n+ builder.mcp_server_contributor(std::sync::Arc::new(\n+ executor_plugin::SelectedExe...", + "path": "codex-rs/ext/mcp/src/lib.rs", + "status": "modified" + }, + { + "additions": 139, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,139 @@\n+use codex_config::test_support::CloudConfigBundleFixture;\n+use codex_core::config::Config;\n+use codex_core::config::ConfigBuilder;\n+use codex_exec_server::EnvironmentManager;\n+use codex_exec_server::LOCAL_ENVIRONMENT_ID;\n+use codex_extension_api::ExtensionDataInit;\n+use codex_extension_api::ExtensionRegistryBuilder;\n+use codex_extension_api::McpServerContribution;\n+use codex_extension_api::McpServerContributionContext;\n+use codex_protocol::capabilities::CapabilityRootLocation;\n+use codex_protocol::capabilities::SelectedCapabilityRoot;\n+use pretty_assertions::assert_eq;\n+use std::sync::Arc;\n+\n+type TestResult = Result<(), Box>;\n+\n+#[derive(Debug, PartialEq, Eq)]\n+struct ContributionSummary {\n+ name: String,\n+ plugin_id: String,\n+ plugin_display_name: String,\n+ selection_order: usize,\n+ enabled: bool,\n+}\n+\n+#[tokio::test]\n+async fn s...", + "path": "codex-rs/ext/mcp/tests/executor_plugin_mcp.rs", + "status": "added" + }, + { + "additions": 10, + "deletions": 0, + "patch_excerpt": "@@ -71,6 +71,16 @@ impl Default for PluginManifestInterface {\n }\n \n impl PluginManifest {\n+ /// Returns the model- and UI-facing package name, falling back to the manifest name.\n+ pub fn display_name(&self) -> &str {\n+ self.interface\n+ .as_ref()\n+ .and_then(|interface| interface.display_name.as_deref())\n+ .map(str::trim)\n+ .filter(|display_name| !display_name.is_empty())\n+ .unwrap_or(&self.name)\n+ }\n+\n pub(crate) fn try_map_resources(\n self,\n mut map: impl FnMut(Resource) -> Result,", + "path": "codex-rs/plugin/src/manifest.rs", + "status": "modified" + } + ], + "linked_issues": [ + "#27884", + "#27863" + ], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Why\n\n**In short:** this PR discovers MCP registrations by reading a selected plugin's `.mcp.json` on its executor. #27884 then resolves those registrations in the shared catalog.\n\n`thread/start.selectedCapabilityRoots` can select a plugin root owned by an executor, and Codex can resolve that package through the executor filesystem. MCP declarations inside the selected plugin are still ignored.\n\nThis PR adds the source-specific discovery layer on top of the selected-plugin catalog boundary in #27884:\n\n```text\nselected capability root\n |\n v\nresolve the plugin through its executor filesystem\n |\n v\nread and normalize its MCP config through the same filesystem\n |\n v\ncontribute stdio registrations bound to that environment ID\n```\n\nThe existing MCP launcher and connection manager remain unchanged. MCP config parsing is shared with local plugins through #27863.\n\n## What changed\n\n- Added an executor plugin MCP provider in the MCP extension.\n- Retained only the exact filesystem capability used for package resolution and reused it for the selected plugin's MCP config, with no host-filesystem fallback or unrelated process/HTTP authority.\n- Read either the manifest-declared MCP config or the default `.mcp.json`; a missing default file means the plugin has no MCP servers.\n- Accepted stdio servers only for this first vertical. Executor-owned HTTP declarations are skipped with a warning until their placement semantics are defined.\n- Normalized stdio registrations with the owning environment's stable logical ID and plugin-root working directory.\n- Resolved environment-variable names on the owning executor and rejected explicit local forwarding for non-local plugins.\n- Froze discovered declarations once per active thread runtime, then applied current managed plugin and MCP requirements when contributing them.\n- Carried the selected root ID, display name, and selection order into the catalog contribution defined by #27884.\n\n## Behavior and scope\n\nThere is intentionally no production behavior change yet. This PR provides the executor provider and contribution boundary, but app-server does not install it in this change. Existing local plugin MCP loading is unchanged, and no MCP process is launched by this PR alone.\n\n## Assumptions\n\n- The selected root ID is the plugin policy identity; the manifest display name is presentation metadata.\n- An environment ID is a stable logical authority. Reconnection or replacement under the same ID does not change ownership.\n- Selected plugin packages and their manifests are trusted inputs.\n- The selected package and MCP discovery snapshot remain frozen for the active thread runtime.\n\n## Follow-up\n\nThe next PR installs this contributor in app-server and adds an end-to-end test proving that a selected plugin MCP tool launches on its owning executor, can be called by the model, survives an explicit MCP refresh, and is invisible when its root was not selected.\n\nResume, fork, environment removal or ID changes, dynamic catalog reload, and executor-owned HTTP MCP placement remain separate lifecycle decisions.\n\n## Verification\n\nFocused tests cover executor-only filesystem reads, missing and malformed config, stdio filtering and normalization, managed requirements, package attribution, and selection order. CI owns execution of the test suite.\n", + "labels": [ + "code-reviewed" + ], + "merged_at": "2026-06-15T09:52:06Z", + "number": 27870, + "state": "merged", + "title": "Discover stdio MCP servers from selected executor plugins", + "url": "https://github.com/openai/codex/pull/27870" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-27884.json b/artifacts/github/bundles/openai-codex-pr-27884.json new file mode 100644 index 000000000..ece3229d5 --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27884.json @@ -0,0 +1,179 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "jif-oai", + "committed_at": "2026-06-12T14:21:48Z", + "message": "Resolve selected plugin MCP registrations", + "sha": "8489afd8e3f3a547b47d569cfc91a389af125b34", + "url": "https://github.com/openai/codex/commit/8489afd8e3f3a547b47d569cfc91a389af125b34" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-12T15:16:17Z", + "message": "Use selected MCP winner approval policy", + "sha": "a7578adf4ccdae18f4cc9d3085d5aac15a431a3d", + "url": "https://github.com/openai/codex/commit/a7578adf4ccdae18f4cc9d3085d5aac15a431a3d" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-12T15:28:43Z", + "message": "Merge branch 'main' into jif/selected-plugin-mcp-catalog", + "sha": "fa026aaac9fd3a6ed649544c7c22df2fb600f9c4", + "url": "https://github.com/openai/codex/commit/fa026aaac9fd3a6ed649544c7c22df2fb600f9c4" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-15T08:40:43Z", + "message": "nits", + "sha": "947796d1630c443c43a45727dd8b2150b5dc00d6", + "url": "https://github.com/openai/codex/commit/947796d1630c443c43a45727dd8b2150b5dc00d6" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-15T08:45:30Z", + "message": "Merge main into jif/selected-plugin-mcp-catalog", + "sha": "e6b462681a96488861b6e2197327a5d0b01cc6f2", + "url": "https://github.com/openai/codex/commit/e6b462681a96488861b6e2197327a5d0b01cc6f2" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "MCP", + "CODEX_APPS_MCP_SERVER_NAME", + "LEGACY_CODEX_APPS_REGISTRATION_ID" + ], + "files": [ + { + "additions": 83, + "deletions": 12, + "patch_excerpt": "@@ -5,18 +5,58 @@ use std::collections::HashMap;\n \n use codex_config::McpServerConfig;\n \n+/// Plugin identity retained with an MCP registration for tool attribution.\n+#[derive(Clone, Debug, PartialEq, Eq)]\n+pub struct McpPluginAttribution {\n+ plugin_id: String,\n+ display_name: String,\n+}\n+\n+impl McpPluginAttribution {\n+ pub fn new(plugin_id: String, display_name: String) -> Self {\n+ Self {\n+ plugin_id,\n+ display_name,\n+ }\n+ }\n+\n+ pub fn plugin_id(&self) -> &str {\n+ &self.plugin_id\n+ }\n+\n+ pub fn display_name(&self) -> &str {\n+ &self.display_name\n+ }\n+}\n+\n /// The component that declared an MCP server registration.\n #[derive(Clone, Debug, PartialEq, Eq)]\n pub enum McpServerSource {\n- Plugin { plugin_id: String },\n+ /// A plugin discovered through the process-wide legacy plugin manager.\n+ Plugin(McpPluginAtt...", + "path": "codex-rs/codex-mcp/src/catalog.rs", + "status": "modified" + }, + { + "additions": 151, + "deletions": 13, + "patch_excerpt": "@@ -8,6 +8,7 @@ use codex_config::McpServerToolConfig;\n use codex_config::McpServerTransportConfig;\n use pretty_assertions::assert_eq;\n \n+use super::McpPluginAttribution;\n use super::McpServerConflict;\n use super::McpServerConflictAction;\n use super::McpServerRegistration;\n@@ -44,10 +45,16 @@ fn server(url: &str) -> McpServerConfig {\n }\n }\n \n+fn plugin(plugin_id: &str) -> McpPluginAttribution {\n+ McpPluginAttribution::new(plugin_id.to_string(), plugin_id.to_string())\n+}\n+\n fn plugin_source(plugin_id: &str) -> McpServerSource {\n- McpServerSource::Plugin {\n- plugin_id: plugin_id.to_string(),\n- }\n+ McpServerSource::Plugin(plugin(plugin_id))\n+}\n+\n+fn selected_plugin_source(plugin_id: &str) -> McpServerSource {\n+ McpServerSource::SelectedPlugin(plugin(plugin_id))\n }\n \n fn compatibility_source(id: &str) -> McpServerSource {\n@@ -69,8 +76,8 @@ fn remove(source: McpServe...", + "path": "codex-rs/codex-mcp/src/catalog_tests.rs", + "status": "modified" + }, + { + "additions": 16, + "deletions": 0, + "patch_excerpt": "@@ -381,6 +381,22 @@ impl McpConnectionManager {\n .plugin_id_for_mcp_server_name(server_name)\n }\n \n+ pub fn is_selected_plugin_mcp_server(&self, server_name: &str) -> bool {\n+ self.tool_plugin_provenance\n+ .is_selected_plugin_mcp_server(server_name)\n+ }\n+\n+ pub fn tool_approval_mode(\n+ &self,\n+ server_name: &str,\n+ tool_name: &str,\n+ ) -> codex_config::AppToolApproval {\n+ self.server_metadata\n+ .get(server_name)\n+ .map(|metadata| metadata.tool_approval_mode(tool_name))\n+ .unwrap_or_default()\n+ }\n+\n pub fn is_host_owned_codex_apps_server(&self, server_name: &str) -> bool {\n self.host_owned_codex_apps_enabled && server_name == CODEX_APPS_MCP_SERVER_NAME\n }", + "path": "codex-rs/codex-mcp/src/connection_manager.rs", + "status": "modified" + }, + { + "additions": 28, + "deletions": 0, + "patch_excerpt": "@@ -12,14 +12,18 @@ use crate::elicitation::elicitation_is_rejected_by_policy;\n use crate::rmcp_client::AsyncManagedClient;\n use crate::rmcp_client::ManagedClient;\n use crate::rmcp_client::StartupOutcomeError;\n+use crate::server::EffectiveMcpServer;\n+use crate::server::McpServerMetadata;\n use crate::server::McpServerOrigin;\n use crate::tools::ToolFilter;\n use crate::tools::ToolInfo;\n use crate::tools::filter_tools;\n use crate::tools::normalize_tools_for_model_with_prefix;\n use crate::tools::tool_with_model_visible_input_schema;\n+use codex_config::AppToolApproval;\n use codex_config::Constrained;\n use codex_config::McpServerConfig;\n+use codex_config::McpServerToolConfig;\n use codex_config::types::AuthKeyringBackendKind;\n use codex_exec_server::EnvironmentManager;\n use codex_protocol::ToolName;\n@@ -1128,6 +1132,8 @@ async fn list_all_tools_adds_server_metadata_to_cached_tools() {\n ...", + "path": "codex-rs/codex-mcp/src/connection_manager_tests.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -12,6 +12,7 @@ pub use runtime::SandboxState;\n pub use tools::ToolInfo;\n \n pub use catalog::McpCatalogBuilder;\n+pub use catalog::McpPluginAttribution;\n pub use catalog::McpServerConflict;\n pub use catalog::McpServerConflictAction;\n pub use catalog::McpServerRegistration;", + "path": "codex-rs/codex-mcp/src/lib.rs", + "status": "modified" + }, + { + "additions": 30, + "deletions": 13, + "patch_excerpt": "@@ -12,6 +12,7 @@ pub use auth::should_retry_without_scopes;\n pub(crate) mod auth;\n \n use std::collections::HashMap;\n+use std::collections::HashSet;\n use std::env;\n use std::path::PathBuf;\n use std::time::Duration;\n@@ -141,7 +142,8 @@ pub struct McpConfig {\n pub client_elicitation_capability: ElicitationCapability,\n /// Resolved MCP registrations keyed by logical server name.\n pub mcp_server_catalog: ResolvedMcpCatalog,\n- /// Plugin metadata used to attribute MCP tools/connectors to plugin display names.\n+ /// Plugin metadata used to attribute connector tools to plugin display names.\n+ /// MCP registrations retain their own package attribution in the catalog.\n pub plugin_capability_summaries: Vec,\n }\n \n@@ -150,6 +152,7 @@ pub struct ToolPluginProvenance {\n plugin_display_names_by_connector_id: HashMap>,\n plugin...", + "path": "codex-rs/codex-mcp/src/mcp/mod.rs", + "status": "modified" + }, + { + "additions": 45, + "deletions": 1, + "patch_excerpt": "@@ -1,4 +1,5 @@\n use super::*;\n+use crate::McpPluginAttribution;\n use crate::McpServerRegistration;\n use codex_config::Constrained;\n use codex_config::types::AppToolApproval;\n@@ -13,6 +14,7 @@ use codex_protocol::protocol::AskForApproval;\n use codex_protocol::protocol::GranularApprovalConfig;\n use pretty_assertions::assert_eq;\n use std::collections::HashMap;\n+use std::collections::HashSet;\n use std::path::PathBuf;\n \n fn test_mcp_config(codex_home: PathBuf) -> McpConfig {\n@@ -127,7 +129,7 @@ fn tool_plugin_provenance_collects_app_and_mcp_sources() {\n let mut catalog = ResolvedMcpCatalog::builder();\n catalog.register(McpServerRegistration::from_plugin(\n \"alpha\".to_string(),\n- \"alpha@test\".to_string(),\n+ McpPluginAttribution::new(\"alpha@test\".to_string(), \"alpha-plugin\".to_string()),\n /*plugin_order*/ 0,\n codex_apps_mcp_server_config(\"https://al...", + "path": "codex-rs/codex-mcp/src/mcp/mod_tests.rs", + "status": "modified" + }, + { + "additions": 25, + "deletions": 0, + "patch_excerpt": "@@ -1,3 +1,6 @@\n+use std::collections::HashMap;\n+\n+use codex_config::AppToolApproval;\n use codex_config::McpServerConfig;\n use codex_config::McpServerTransportConfig;\n \n@@ -75,6 +78,18 @@ pub(crate) struct McpServerMetadata {\n pub pollutes_memory: bool,\n pub origin: Option,\n pub supports_parallel_tool_calls: bool,\n+ pub default_tools_approval_mode: Option,\n+ pub tool_approval_modes: HashMap,\n+}\n+\n+impl McpServerMetadata {\n+ pub fn tool_approval_mode(&self, tool_name: &str) -> AppToolApproval {\n+ self.tool_approval_modes\n+ .get(tool_name)\n+ .copied()\n+ .or(self.default_tools_approval_mode)\n+ .unwrap_or_default()\n+ }\n }\n \n impl From<&EffectiveMcpServer> for McpServerMetadata {\n@@ -84,6 +99,16 @@ impl From<&EffectiveMcpServer> for McpServerMetadata {\n ...", + "path": "codex-rs/codex-mcp/src/server.rs", + "status": "modified" + }, + { + "additions": 39, + "deletions": 4, + "patch_excerpt": "@@ -4357,8 +4357,13 @@ async fn rebuild_preserving_session_layers_refreshes_plugin_derived_mcp_config()\n Some(&http_mcp(\"https://sample.example/mcp\"))\n );\n assert_eq!(\n- mcp_config.mcp_server_catalog.plugin_ids_by_server_name(),\n- HashMap::from([(\"sample\".to_string(), \"sample@test\".to_string())])\n+ mcp_config\n+ .mcp_server_catalog\n+ .plugin_attributions_by_server_name(),\n+ HashMap::from([(\n+ \"sample\".to_string(),\n+ McpPluginAttribution::new(\"sample@test\".to_string(), \"sample\".to_string()),\n+ )])\n );\n \n Ok(())\n@@ -4416,15 +4421,15 @@ enabled = true\n assert!(\n mcp_config\n .mcp_server_catalog\n- .plugin_ids_by_server_name()\n+ .plugin_attributions_by_server_name()\n .is_empty()\n );\n \n Ok(())\n }\n \n #[tokio::test]\n-async fn to_mc...", + "path": "codex-rs/core/src/config/config_tests.rs", + "status": "modified" + }, + { + "additions": 41, + "deletions": 12, + "patch_excerpt": "@@ -70,6 +70,7 @@ use codex_git_utils::resolve_root_git_project_for_trust;\n use codex_install_context::InstallContext;\n use codex_login::AuthManagerConfig;\n use codex_mcp::McpConfig;\n+use codex_mcp::McpPluginAttribution;\n use codex_mcp::McpServerRegistration;\n use codex_mcp::ResolvedMcpCatalog;\n use codex_memories_read::memory_root;\n@@ -1396,41 +1397,69 @@ impl Config {\n )\n }\n \n- pub async fn to_mcp_config(\n+ /// Applies managed MCP requirements to servers supplied by one plugin.\n+ pub fn apply_plugin_mcp_server_requirements(\n &self,\n- plugins_manager: &codex_core_plugins::PluginsManager,\n- ) -> McpConfig {\n- let plugins_input = self.plugins_config_input();\n- let loaded_plugins = plugins_manager.plugins_for_config(&plugins_input).await;\n- let mut catalog = ResolvedMcpCatalog::builder();\n+ plugin_id: &str,\n+ mcp_ser...", + "path": "codex-rs/core/src/config/mod.rs", + "status": "modified" + }, + { + "additions": 85, + "deletions": 23, + "patch_excerpt": "@@ -12,13 +12,28 @@ use codex_login::CodexAuth;\n use codex_mcp::CODEX_APPS_MCP_SERVER_NAME;\n use codex_mcp::EffectiveMcpServer;\n use codex_mcp::McpConfig;\n+use codex_mcp::McpPluginAttribution;\n use codex_mcp::McpServerRegistration;\n use codex_mcp::codex_apps_mcp_server_config;\n use codex_mcp::configured_mcp_servers;\n use codex_mcp::effective_mcp_servers;\n \n const LEGACY_CODEX_APPS_REGISTRATION_ID: &str = \"legacy_codex_apps\";\n \n+enum OrderedMcpOverlay {\n+ Set {\n+ contributor_id: &'static str,\n+ contribution_order: usize,\n+ name: String,\n+ config: Box,\n+ },\n+ Remove {\n+ contributor_id: &'static str,\n+ contribution_order: usize,\n+ name: String,\n+ },\n+}\n+\n #[derive(Clone)]\n pub struct McpManager {\n plugins_manager: Arc,\n@@ -65,7 +80,58 @@ impl McpManager {\n config: &Config,\n thre...", + "path": "codex-rs/core/src/mcp.rs", + "status": "modified" + }, + { + "additions": 19, + "deletions": 2, + "patch_excerpt": "@@ -166,6 +166,15 @@ pub(crate) async fn handle_mcp_tool_call(\n };\n let approval_mode = if server == CODEX_APPS_MCP_SERVER_NAME {\n app_tool_policy.approval\n+ } else if let Some(approval_mode) = {\n+ // Selected-plugin registrations are absent from config.toml and the legacy plugin manager,\n+ // so their resolved catalog entry is the authoritative source for tool approval policy.\n+ let manager = sess.services.mcp_connection_manager.load();\n+ manager\n+ .is_selected_plugin_mcp_server(&server)\n+ .then(|| manager.tool_approval_mode(&server, &tool_name))\n+ } {\n+ approval_mode\n } else {\n custom_mcp_tool_approval_mode(sess.as_ref(), turn_context.as_ref(), &server, &tool_name)\n .await\n@@ -1168,8 +1177,16 @@ async fn maybe_request_mcp_tool_approval(\n }\n \n let session_approval_key = session...", + "path": "codex-rs/core/src/mcp_tool_call.rs", + "status": "modified" + }, + { + "additions": 6, + "deletions": 2, + "patch_excerpt": "@@ -448,8 +448,12 @@ async fn start_thread_seeds_extension_data_for_mcp_and_lifecycle_contributors()\n &selected_root.location;\n server.environment_id = environment_id.clone();\n server.enabled = false;\n- vec![codex_extension_api::McpServerContribution::Set {\n- name: selected_root.id,\n+ let plugin_id = selected_root.id;\n+ vec![codex_extension_api::McpServerContribution::SelectedPlugin {\n+ name: plugin_id.clone(),\n+ plugin_display_name: plugin_id.clone(),\n+ plugin_id,\n+ selection_order: 0,\n config: Box::new(server),\n }]\n })", + "path": "codex-rs/core/src/thread_manager_tests.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -49,8 +49,8 @@ pub type ExtensionFuture<'a, T> = Pin + Send + 'a>>;\n /// Thread-scoped resolution exposes the host-seeded thread inputs; global\n /// resolution exposes none and must not imply a local fallback. Thread inputs\n /// are frozen for the runtime and do not include lifecycle-contributor state.\n-/// Plugin-owned servers and their provenance continue to be resolved by the\n-/// plugin manager until that ownership moves into an extension explicitly.\n+/// Auto-discovered plugin servers are resolved by the plugin manager. A\n+/// thread-selected plugin contribution must carry its own package provenance.\n pub trait McpServerContributor: Send + Sync {\n /// Stable identity used for registration provenance and conflict diagnostics.\n fn id(&self) -> &'static str;", + "path": "codex-rs/ext/extension-api/src/contributors.rs", + "status": "modified" + }, + { + "additions": 8, + "deletions": 0, + "patch_excerpt": "@@ -58,6 +58,14 @@ pub enum McpServerContribution {\n name: String,\n config: Box,\n },\n+ /// Registers a server declared by a plugin selected for this thread.\n+ SelectedPlugin {\n+ name: String,\n+ plugin_id: String,\n+ plugin_display_name: String,\n+ selection_order: usize,\n+ config: Box,\n+ },\n /// Removes a named MCP server.\n Remove { name: String },\n }", + "path": "codex-rs/ext/extension-api/src/contributors/mcp.rs", + "status": "modified" + }, + { + "additions": 5, + "deletions": 4, + "patch_excerpt": "@@ -34,6 +34,10 @@ impl LoadedPlugin {\n pub fn is_active(&self) -> bool {\n self.enabled && self.error.is_none()\n }\n+\n+ pub fn display_name(&self) -> &str {\n+ self.manifest_name.as_deref().unwrap_or(&self.config_name)\n+ }\n }\n \n fn plugin_capability_summary_from_loaded(\n@@ -48,10 +52,7 @@ fn plugin_capability_summary_from_loaded(\n \n let summary = PluginCapabilitySummary {\n config_name: plugin.config_name.clone(),\n- display_name: plugin\n- .manifest_name\n- .clone()\n- .unwrap_or_else(|| plugin.config_name.clone()),\n+ display_name: plugin.display_name().to_string(),\n description: prompt_safe_plugin_description(plugin.manifest_description.as_deref()),\n has_skills: plugin.has_enabled_skills,\n mcp_server_names,", + "path": "codex-rs/plugin/src/load_outcome.rs", + "status": "modified" + } + ], + "linked_issues": [ + "#27870" + ], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Why\n\n**In short:** this PR resolves already-discovered MCP registrations. It does not read selected plugins or discover their MCP servers.\n\nThe resolved MCP catalog currently builds config and auto-discovered plugin registrations before runtime contributors are applied. A thread-selected plugin needs a distinct precedence tier in that same initial resolution pass: otherwise a disabled lower-precedence winner can leave stale name-level state behind, and the winning MCP tools cannot be attributed to the selected package reliably.\n\nThis PR adds that catalog boundary before executor discovery is connected.\n\n## What changed\n\n- Added an explicit selected-plugin registration tier between auto-discovered plugins and explicit config.\n- Collected selected-plugin contributions before the initial catalog build, while leaving compatibility and generic extension overlays in their existing runtime phase.\n- Retained the winning plugin ID and display name directly on plugin-owned catalog registrations.\n- Derived MCP tool provenance from the winning catalog entry instead of joining against local-only plugin summaries.\n- Retained the winning selected server's tool approval policy in the running connection manager, so a selected registration cannot inherit approval behavior from a losing local plugin.\n- Kept remembered approval session-scoped for selected plugins until there is an authority-aware persistence contract; Codex will not write approval back to an unrelated local plugin.\n- Preserved existing name-level disabled vetoes for discovered plugins and config, while keeping a selected package's own disabled registration scoped to that registration.\n- Preserved deterministic selection order and existing config, compatibility, and extension precedence.\n\nThe resulting order is:\n\n```text\nauto-discovered plugin\n < selected plugin\n < explicit config\n < compatibility registration\n < extension overlay\n```\n\n## Behavior and scope\n\nThis is a catalog and provenance change only. No production host contributes selected-plugin MCP registrations yet, so existing local MCP behavior remains unchanged.\n\nThe stacked follow-up, #27870, installs the executor plugin provider that produces these registrations. App-server activation remains a separate final step.\n\n## Verification\n\nFocused tests cover precedence, deterministic selected-plugin conflicts, disabled-veto behavior across catalog phases, managed requirements before selected-plugin resolution, winning-server approval policy, and attribution when local and selected packages share an ID or server name. CI owns execution of the test suite.\n", + "labels": [], + "merged_at": "2026-06-15T09:10:52Z", + "number": 27884, + "state": "merged", + "title": "Add selected-plugin precedence and attribution to the MCP catalog", + "url": "https://github.com/openai/codex/pull/27884" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-28001.json b/artifacts/github/bundles/openai-codex-pr-28001.json new file mode 100644 index 000000000..139f0d9eb --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-28001.json @@ -0,0 +1,42 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "tamird", + "committed_at": "2026-06-13T01:29:06Z", + "message": "[codex] package Windows ARM64 on x64", + "sha": "4b3fd5e359221031616a66cad36299c4becd810b", + "url": "https://github.com/openai/codex/commit/4b3fd5e359221031616a66cad36299c4becd810b" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "ARM64" + ], + "files": [ + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -210,11 +210,11 @@ jobs:\n runs_on:\n group: ${{ github.event.repository.name }}-runners\n labels: ${{ github.event.repository.name }}-windows-x64\n- - runner: windows-arm64\n+ - runner: windows-x64\n target: aarch64-pc-windows-msvc\n runs_on:\n group: ${{ github.event.repository.name }}-runners\n- labels: ${{ github.event.repository.name }}-windows-arm64\n+ labels: ${{ github.event.repository.name }}-windows-x64\n \n steps:\n - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2", + "path": ".github/workflows/rust-release-windows.yml", + "status": "modified" + } + ], + "linked_issues": [], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "The first release after parallelizing Windows packaging moved the\ncritical path to the ARM64 packaging job:\n\nhttps://github.com/openai/codex/actions/runs/27451157324\n\nThe x64 job started immediately and finished in 5m29s. The ARM64\njob waited 76s for its runner and then took 5m56s, holding the\nrelease for 1m43s after x64 had finished.\n\nPackaging only downloads, signs, archives, and compresses already\nbuilt binaries. It does not execute target code. Run both packaging\njobs on x64 runners, keeping ARM64 hardware for compilation.", + "labels": [], + "merged_at": "2026-06-13T19:55:53Z", + "number": 28001, + "state": "merged", + "title": "[codex] package Windows ARM64 on x64", + "url": "https://github.com/openai/codex/pull/28001" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/impact/openai-codex-pr-27607.json b/artifacts/github/impact/openai-codex-pr-27607.json new file mode 100644 index 000000000..248bab9bd --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-27607.json @@ -0,0 +1,47 @@ +{ + "candidate_followups": [ + "Audit Decodex plugin routing and render logic for assumptions that plugin App metadata is only connector IDs.", + "Check Control Plane plugin/MCP conflict handling for dual App/MCP plugins under ChatGPT/SIWC auth.", + "Preserve App declaration names in any Decodex analysis that explains why a plugin MCP server was hidden or retained." + ], + "caveats": [ + "The PR preserves existing effective App connector ID output for callers that only need connector IDs.", + "The meaningful behavior change is name-level conflict filtering for ChatGPT/SIWC plugin MCP servers.", + "Exact Decodex code changes require a separate implementation issue if local plugin routing code collapses App declaration names into connector IDs." + ], + "confidence": "confirmed", + "control_plane_impact": "compat_risk", + "evidence": [ + "PR #27607 defines the product behavior: matching App/MCP route names prefer the App route, while non-conflicting MCP names stay available.", + "`LoadedPlugin.apps` now stores App declarations rather than raw connector IDs.", + "App-server plugin install code derives connector IDs from declarations while retaining declaration names.", + "Core plugin tests cover keeping a non-conflicting MCP server available under ChatGPT auth.", + "The source-backed review is recorded at `artifacts/github/reviews/openai-codex-pr-27607.review.json`." + ], + "observed_change": "Codex now preserves plugin App declaration names and suppresses only matching-name plugin MCP servers for ChatGPT/SIWC auth instead of hiding all MCP servers from a dual App/MCP plugin.", + "public_signal_decision": "publish", + "publisher_angle": "operator_impact", + "repo": "openai/codex", + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-27607", + "social_notes": [ + "Frame this as a dual App/MCP plugin routing change, not a general MCP removal.", + "Mention that non-conflicting MCP servers remain available for ChatGPT/SIWC sessions." + ], + "source_refs": { + "items": [ + { + "kind": "pull_request", + "meta": "Merged 2026-06-14T00:53:09Z", + "title": "[codex] Dedupe plugin MCPs by app declaration name", + "url": "https://github.com/openai/codex/pull/27607" + }, + { + "kind": "pull_request", + "meta": "artifacts/github/reviews/openai-codex-pr-27607.review.json", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/27607" + } + ] + } +} diff --git a/artifacts/github/impact/openai-codex-pr-27870.json b/artifacts/github/impact/openai-codex-pr-27870.json new file mode 100644 index 000000000..1ab7da2a1 --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-27870.json @@ -0,0 +1,47 @@ +{ + "candidate_followups": [ + "Track selected capability root MCP servers as executor-owned, not host-local, in Decodex Control Plane design.", + "Reject assumptions that selected plugin MCP env vars can be forwarded from local config when the upstream owner is a non-local executor.", + "Wait for app-server installation evidence before creating a Decodex adoption issue for live selected executor plugin MCPs." + ], + "caveats": [ + "PR #27870 says app-server does not install this provider in this change.", + "Only stdio selected executor plugin MCP declarations are accepted in this first vertical.", + "Executor-owned HTTP MCP placement, resume, fork, environment removal, dynamic reload, and ID-change behavior remain separate lifecycle decisions." + ], + "confidence": "confirmed", + "control_plane_impact": "candidate", + "evidence": [ + "PR #27870 defines selected plugin MCP discovery through the owning executor filesystem.", + "The provider reads manifest-declared MCP config or default `.mcp.json` and skips missing default config as no MCP servers.", + "The provider accepts stdio servers and skips executor-owned HTTP declarations with a warning.", + "Selected declarations are frozen once per active thread runtime and normalized with the owning environment ID.", + "The source-backed review is recorded at `artifacts/github/reviews/openai-codex-pr-27870.review.json`." + ], + "observed_change": "Codex adds an executor plugin MCP provider that reads selected plugin MCP declarations through the owning executor filesystem and contributes stdio registrations bound to that environment.", + "public_signal_decision": "defer", + "publisher_angle": "watch_note", + "repo": "openai/codex", + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-27870", + "social_notes": [ + "A public note should emphasize this is provider groundwork, not live app-server behavior yet.", + "The durable public angle is executor-owned selected plugin MCP authority, especially no host-filesystem fallback." + ], + "source_refs": { + "items": [ + { + "kind": "pull_request", + "meta": "Merged 2026-06-15T09:52:06Z", + "title": "Discover stdio MCP servers from selected executor plugins", + "url": "https://github.com/openai/codex/pull/27870" + }, + { + "kind": "pull_request", + "meta": "artifacts/github/reviews/openai-codex-pr-27870.review.json", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/27870" + } + ] + } +} diff --git a/artifacts/github/impact/openai-codex-pr-27884.json b/artifacts/github/impact/openai-codex-pr-27884.json new file mode 100644 index 000000000..d37091a6a --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-27884.json @@ -0,0 +1,47 @@ +{ + "candidate_followups": [ + "Model selected-plugin MCP provenance in Decodex separately from local plugin summaries.", + "Track selected-plugin tool approval as catalog/runtime state and avoid writing approvals back to unrelated local plugin config.", + "Pair this with #27870 and the later app-server activation PR before claiming production selected-plugin MCP behavior." + ], + "caveats": [ + "PR #27884 is a catalog and provenance change only.", + "The PR body says no production host contributes selected-plugin MCP registrations yet.", + "Publisher copy should avoid saying selected executor plugin MCPs are live until activation evidence is present." + ], + "confidence": "confirmed", + "control_plane_impact": "candidate", + "evidence": [ + "PR #27884 defines selected-plugin registration precedence between auto-discovered plugins and explicit config.", + "The extension API adds a selected-plugin contribution carrying plugin identity, display name, selection order, and config.", + "MCP tool approval lookup reads selected-plugin approval policy from the running connection manager.", + "MCP server metadata now carries default and per-tool approval modes.", + "The source-backed review is recorded at `artifacts/github/reviews/openai-codex-pr-27884.review.json`." + ], + "observed_change": "Codex adds a selected-plugin MCP catalog precedence tier with retained plugin attribution and catalog-owned tool approval policy for selected registrations.", + "public_signal_decision": "defer", + "publisher_angle": "watch_note", + "repo": "openai/codex", + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-27884", + "social_notes": [ + "A public note should be caveated as groundwork, not shipped production selected-plugin MCP behavior.", + "Best paired with the executor-provider and app-server activation chain." + ], + "source_refs": { + "items": [ + { + "kind": "pull_request", + "meta": "Merged 2026-06-15T09:10:52Z", + "title": "Add selected-plugin precedence and attribution to the MCP catalog", + "url": "https://github.com/openai/codex/pull/27884" + }, + { + "kind": "pull_request", + "meta": "artifacts/github/reviews/openai-codex-pr-27884.review.json", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/27884" + } + ] + } +} diff --git a/artifacts/github/impact/openai-codex-pr-28001.json b/artifacts/github/impact/openai-codex-pr-28001.json new file mode 100644 index 000000000..d59384836 --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-28001.json @@ -0,0 +1,38 @@ +{ + "candidate_followups": [ + "Use as release-rollup context only if a future checkpoint needs Windows packaging throughput notes." + ], + "caveats": [ + "This is packaging infrastructure, not a runtime feature.", + "No source evidence indicates a Windows ARM64 binary behavior change." + ], + "confidence": "confirmed", + "control_plane_impact": "none", + "evidence": [ + "PR #28001 states packaging does not execute target code and only downloads, signs, archives, and compresses already built binaries.", + "The workflow patch changes the ARM64 package target to run on the Windows x64 runner label.", + "The source-backed review is recorded at `artifacts/github/reviews/openai-codex-pr-28001.review.json`." + ], + "observed_change": "Codex now packages Windows ARM64 release artifacts on Windows x64 runners instead of Windows ARM64 runners.", + "public_signal_decision": "skip", + "publisher_angle": "none", + "repo": "openai/codex", + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-28001", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "meta": "Merged 2026-06-13T19:55:53Z", + "title": "[codex] package Windows ARM64 on x64", + "url": "https://github.com/openai/codex/pull/28001" + }, + { + "kind": "pull_request", + "meta": "artifacts/github/reviews/openai-codex-pr-28001.review.json", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/28001" + } + ] + } +} diff --git a/artifacts/github/review-queue/openai-codex-latest.json b/artifacts/github/review-queue/openai-codex-latest.json index 6edcf9b32..477581d8e 100644 --- a/artifacts/github/review-queue/openai-codex-latest.json +++ b/artifacts/github/review-queue/openai-codex-latest.json @@ -1,14 +1,14 @@ { "counts": { - "critical": 19, + "critical": 20, "high": 5, "low": 0, - "normal": 16, + "normal": 15, "published_subjects_seen": 0, "recent_commits_scanned": 40, "subjects_queued": 40 }, - "generated_at": "2026-06-14T18:06:03.813491Z", + "generated_at": "2026-06-15T12:06:27.560195Z", "repo": "openai/codex", "schema": "upstream_review_queue/v1", "source": { @@ -17,53 +17,6 @@ "signals_dir": "site/src/content/signals" }, "subjects": [ - { - "attention_flags": [ - "auth_account", - "breaking_change", - "deprecated_removed", - "new_feature", - "protocol_change", - "release_packaging", - "security_policy" - ], - "changed_file_count": 11, - "commit_shas": [ - "07d66eccd904cd15cd754c43d3f74a72d63a1980", - "3e3379b56006b9e877f5db4ba65009b4ad41c49c", - "e026ff2133ba7c2e740eee1ecf0cc0ec5a7479a8", - "30f6722a49e562b8d8fccbaf221aa278d39ab30c" - ], - "committed_at": "2026-06-12T19:52:49Z", - "next_step": "ai_review_required", - "pr_number": 27535, - "pr_url": "https://github.com/openai/codex/pull/27535", - "review_priority": "critical", - "review_reason": "Needs AI review for auth_account, breaking_change, deprecated_removed, new_feature, protocol_change, release_packaging, security_policy.", - "sample_paths": [ - "codex-rs/config/src/types.rs", - "codex-rs/core/config.schema.json", - "codex-rs/core/src/config/auth_keyring.rs", - "codex-rs/core/src/config/auth_keyring_tests.rs", - "codex-rs/core/src/config/config_tests.rs", - "codex-rs/core/src/config/mod.rs", - "codex-rs/features/src/lib.rs", - "codex-rs/features/src/tests.rs", - "codex-rs/keyring-store/src/lib.rs", - "codex-rs/secrets/src/lib.rs", - "codex-rs/secrets/src/local.rs" - ], - "source_state": "merged", - "subject_id": "27535", - "subject_kind": "pr", - "surface_hints": [ - "auth_accounts", - "config_hooks", - "tests_ci" - ], - "title": "feat: add auth-specific encrypted secret namespaces", - "url": "https://github.com/openai/codex/pull/27535" - }, { "attention_flags": [ "deprecated_removed", @@ -891,35 +844,97 @@ { "attention_flags": [ "auth_account", + "deprecated_removed", "new_feature", "protocol_change", "security_policy" ], - "changed_file_count": 4, + "changed_file_count": 16, "commit_shas": [ - "d8961dbcec304ece82efc081a64c5a165a376bb7" + "8489afd8e3f3a547b47d569cfc91a389af125b34", + "a7578adf4ccdae18f4cc9d3085d5aac15a431a3d", + "fa026aaac9fd3a6ed649544c7c22df2fb600f9c4", + "947796d1630c443c43a45727dd8b2150b5dc00d6", + "e6b462681a96488861b6e2197327a5d0b01cc6f2" ], - "committed_at": "2026-06-12T20:23:22Z", + "committed_at": "2026-06-15T09:10:51Z", "next_step": "ai_review_required", - "pr_number": 27926, - "pr_url": "https://github.com/openai/codex/pull/27926", - "review_priority": "high", - "review_reason": "Needs AI review for auth_account, new_feature, protocol_change, security_policy.", + "pr_number": 27884, + "pr_url": "https://github.com/openai/codex/pull/27884", + "review_priority": "critical", + "review_reason": "Needs AI review for auth_account, deprecated_removed, new_feature, protocol_change, security_policy.", + "sample_paths": [ + "codex-rs/codex-mcp/src/catalog.rs", + "codex-rs/codex-mcp/src/catalog_tests.rs", + "codex-rs/codex-mcp/src/connection_manager.rs", + "codex-rs/codex-mcp/src/connection_manager_tests.rs", + "codex-rs/codex-mcp/src/lib.rs", + "codex-rs/codex-mcp/src/mcp/mod.rs", + "codex-rs/codex-mcp/src/mcp/mod_tests.rs", + "codex-rs/codex-mcp/src/server.rs", + "codex-rs/core/src/config/config_tests.rs", + "codex-rs/core/src/config/mod.rs", + "codex-rs/core/src/mcp.rs", + "codex-rs/core/src/mcp_tool_call.rs" + ], + "source_state": "merged", + "subject_id": "27884", + "subject_kind": "pr", + "surface_hints": [ + "config_hooks", + "mcp_plugins", + "tests_ci" + ], + "title": "Add selected-plugin precedence and attribution to the MCP catalog", + "url": "https://github.com/openai/codex/pull/27884" + }, + { + "attention_flags": [ + "auth_account", + "deprecated_removed", + "new_feature", + "protocol_change", + "release_packaging", + "security_policy" + ], + "changed_file_count": 11, + "commit_shas": [ + "8489afd8e3f3a547b47d569cfc91a389af125b34", + "a7578adf4ccdae18f4cc9d3085d5aac15a431a3d", + "8b78cf44bc9452427916b53c2a7c1b99a2b89434", + "b7180839381890e258ba75bfbf2b825b5064a41f", + "a5453ca8f4c3c24b473c031811c1e36c3185efb8" + ], + "committed_at": "2026-06-15T09:52:05Z", + "next_step": "ai_review_required", + "pr_number": 27870, + "pr_url": "https://github.com/openai/codex/pull/27870", + "review_priority": "critical", + "review_reason": "Needs AI review for auth_account, deprecated_removed, new_feature, protocol_change, release_packaging, security_policy.", "sample_paths": [ "codex-rs/Cargo.lock", - "codex-rs/core-skills/Cargo.toml", - "codex-rs/core-skills/src/invocation_utils.rs", - "codex-rs/core-skills/src/invocation_utils_tests.rs" + "codex-rs/core-plugins/src/lib.rs", + "codex-rs/core-plugins/src/loader.rs", + "codex-rs/core-plugins/src/provider.rs", + "codex-rs/ext/mcp/Cargo.toml", + "codex-rs/ext/mcp/src/executor_plugin.rs", + "codex-rs/ext/mcp/src/executor_plugin/provider.rs", + "codex-rs/ext/mcp/src/executor_plugin/provider_tests.rs", + "codex-rs/ext/mcp/src/lib.rs", + "codex-rs/ext/mcp/tests/executor_plugin_mcp.rs", + "codex-rs/plugin/src/manifest.rs" ], "source_state": "merged", - "subject_id": "27926", + "subject_id": "27870", "subject_kind": "pr", "surface_hints": [ "config_hooks", + "mcp_plugins", + "model_provider", "tests_ci" ], - "title": "[codex] Align implicit skill reads with parser", - "url": "https://github.com/openai/codex/pull/27926" + "title": "Discover stdio MCP servers from selected executor plugins", + "url": "https://github.com/openai/codex/pull/27870" }, { "attention_flags": [ @@ -1089,30 +1104,34 @@ }, { "attention_flags": [ - "new_feature", - "protocol_change" + "new_feature" ], - "changed_file_count": 1, + "changed_file_count": 4, "commit_shas": [ - "1b73843628fdfbb3968f89427fc272b8add483b0" + "24984b53cfed8bea45870dc5fe57a5ea6e796c61", + "888be7d90b2129e93bf28eb0070660601fa0ce77" ], - "committed_at": "2026-06-12T20:00:21Z", + "committed_at": "2026-06-15T10:21:02Z", "next_step": "ai_review_required", - "pr_number": 27939, - "pr_url": "https://github.com/openai/codex/pull/27939", - "review_priority": "normal", - "review_reason": "Needs AI review for new_feature, protocol_change.", + "pr_number": 28283, + "pr_url": "https://github.com/openai/codex/pull/28283", + "review_priority": "high", + "review_reason": "Needs AI review for new_feature.", "sample_paths": [ - "AGENTS.md" + "codex-rs/core/src/config/config_tests.rs", + "codex-rs/core/src/config/mod.rs", + "codex-rs/core/src/tools/handlers/multi_agents_spec.rs", + "codex-rs/core/src/tools/spec_plan_tests.rs" ], "source_state": "merged", - "subject_id": "27939", + "subject_id": "28283", "subject_kind": "pr", "surface_hints": [ - "internal_churn" + "config_hooks", + "tests_ci" ], - "title": "[codex] Add crate API surface review rule", - "url": "https://github.com/openai/codex/pull/27939" + "title": "[codex] update multi-agent v2 prompts", + "url": "https://github.com/openai/codex/pull/28283" }, { "attention_flags": [ diff --git a/artifacts/github/reviews/openai-codex-pr-27607.review.json b/artifacts/github/reviews/openai-codex-pr-27607.review.json new file mode 100644 index 000000000..187a5aae7 --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27607.review.json @@ -0,0 +1,71 @@ +{ + "adoption_opportunity": "Audit Decodex plugin and MCP routing assumptions so App declarations, connector IDs, and MCP server names stay distinct when deciding which surfaces to expose under ChatGPT/SIWC auth.", + "caveats": "The PR intentionally preserves existing public effective App outputs as deduped connector IDs; the compatibility-sensitive detail is the retained App declaration name used for MCP conflict checks.", + "changed_surfaces": [ + "plugin App metadata model", + "core plugin manager auth projection", + "app-server plugin install filtering", + "explicit plugin mention behavior under ChatGPT/SIWC auth", + "plugin capability summary generation", + "plugin App/MCP regression tests" + ], + "community_value": "High for plugin authors and operator tooling because dual App/MCP plugins can keep non-conflicting MCP servers available while ChatGPT/SIWC prefers the App route for matching names.", + "compatibility_risk": "Moderate for clients or Control Plane code that assumed plugin Apps were only connector IDs or that ChatGPT/SIWC auth hides every MCP server from a plugin that also declares an App.", + "confidence": "confirmed", + "control_plane_relevance": "High. Decodex tracks plugin, MCP, and app-route behavior, and this changes the source identity needed to reason about App-vs-MCP conflicts.", + "deprecated_or_breaking_notes": "Upstream changes `LoadedPlugin.apps` from connector IDs to `AppDeclaration` records internally; callers that need connector IDs must derive them explicitly.", + "evidence": [ + "PR #27607 says ChatGPT/SIWC sessions should use an App route only when a plugin exposes an App and MCP route for the same name, while a separate MCP server such as `foo2` remains available.", + "`codex-rs/plugin/src/lib.rs` adds `AppDeclaration` with `name`, `connector_id`, and `category`, plus `app_connector_ids_from_declarations` for existing connector-id projections.", + "`codex-rs/plugin/src/load_outcome.rs` changes `LoadedPlugin.apps` from `Vec` to `Vec`.", + "`codex-rs/app-server/src/request_processors/plugins.rs` loads App declarations, derives connector IDs for existing app auth checks, and keeps declaration names available for install filtering.", + "`codex-rs/core-plugins/src/manager.rs` switches auth projection from `load_plugin_app_metadata` to declaration-aware `load_plugin_apps`.", + "`codex-rs/core/tests/suite/plugins.rs` adds `explicit_plugin_mentions_keep_non_conflicting_mcp_for_chatgpt_auth`, covering the non-conflicting MCP path.", + "The normalized bundle `artifacts/github/bundles/openai-codex-pr-27607.json` records 7 changed files for the merged PR." + ], + "next_actions": [ + { + "reason": "The App declaration versus MCP server-name split affects Decodex plugin and Control Plane compatibility assumptions.", + "type": "upstream_impact" + }, + { + "reason": "The change has a concrete public operator angle for dual App/MCP plugin behavior under ChatGPT/SIWC auth.", + "type": "social_candidate" + } + ], + "observed_change": "Codex now preserves plugin App declaration names and suppresses only matching-name plugin MCP servers for ChatGPT/SIWC auth instead of hiding all MCP servers from a dual App/MCP plugin.", + "repo": "openai/codex", + "reviewed_at": "2026-06-15T12:08:04Z", + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27607", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "meta": "Merged 2026-06-14T00:53:09Z", + "title": "[codex] Dedupe plugin MCPs by app declaration name", + "url": "https://github.com/openai/codex/pull/27607" + }, + { + "kind": "commit", + "title": "Dedupe plugin MCPs by app declaration name", + "url": "https://github.com/openai/codex/commit/6090e9d2498277d9f8720d860e6e89142d9e45a9" + }, + { + "kind": "commit", + "title": "Use app declarations for plugin app parsing", + "url": "https://github.com/openai/codex/commit/97a3f22d9eaa499ee384674a4f47788a2c0e219e" + } + ] + }, + "subject": { + "commit_shas": [ + "6090e9d2498277d9f8720d860e6e89142d9e45a9", + "97a3f22d9eaa499ee384674a4f47788a2c0e219e", + "8235bab021b5b77dd1999487ab3082fafca3069a" + ], + "subject_id": "27607", + "subject_kind": "pr" + }, + "user_visible_path": "For a dual-surface plugin under ChatGPT/SIWC auth, matching App and MCP route names prefer the App route, while separate MCP server names remain available." +} diff --git a/artifacts/github/reviews/openai-codex-pr-27870.review.json b/artifacts/github/reviews/openai-codex-pr-27870.review.json new file mode 100644 index 000000000..fa78822e0 --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27870.review.json @@ -0,0 +1,75 @@ +{ + "adoption_opportunity": "Prepare Decodex Control Plane MCP modeling for selected capability roots whose plugin package, MCP config, working directory, environment ID, and environment-variable resolution belong to an executor rather than the host filesystem.", + "caveats": "The PR body says there is intentionally no production behavior change yet because app-server does not install this provider in this change.", + "changed_surfaces": [ + "executor plugin provider", + "MCP extension provider", + "selected capability root MCP discovery", + "executor filesystem authority", + "stdio MCP registration normalization", + "environment-variable resolution for executor-owned MCPs", + "executor plugin MCP tests" + ], + "community_value": "Moderate now and potentially high after activation: it documents how selected executor plugins can contribute stdio MCP servers without host-filesystem fallback.", + "compatibility_risk": "Low immediately because the provider is not installed in app-server by this PR; medium for future activation because selected plugin MCPs use executor filesystem and environment authority instead of local config assumptions.", + "confidence": "confirmed", + "control_plane_relevance": "High. Decodex needs to distinguish host-local MCP config from executor-owned selected plugin MCP registrations, especially around filesystem, environment ID, and env-var forwarding boundaries.", + "deprecated_or_breaking_notes": "Executor-owned HTTP MCP declarations are skipped with a warning in this first vertical; stdio declarations are the only accepted selected executor plugin MCP servers.", + "evidence": [ + "PR #27870 says it discovers MCP registrations by reading a selected plugin's `.mcp.json` on its executor and contributes stdio registrations bound to the owning environment ID.", + "The PR body says existing MCP launcher and connection manager are unchanged and app-server does not install the provider in this change.", + "`codex-rs/core-plugins/src/provider.rs` adds `ResolvedExecutorPlugin`, pairing a resolved plugin descriptor with the concrete executor filesystem used to read it.", + "`codex-rs/ext/mcp/src/executor_plugin/provider.rs` loads MCP declarations through the executor filesystem and defines `.mcp.json` as the default MCP config file.", + "`codex-rs/ext/mcp/src/executor_plugin.rs` freezes selected plugin MCP declarations for the active thread runtime and carries stable environment authority.", + "`codex-rs/plugin/src/manifest.rs` adds `display_name()` so selected plugin contributions can preserve presentation metadata.", + "`codex-rs/ext/mcp/tests/executor_plugin_mcp.rs` adds end-to-end contribution tests for selected plugin MCP registrations.", + "The normalized bundle `artifacts/github/bundles/openai-codex-pr-27870.json` records 11 changed files for the merged PR." + ], + "next_actions": [ + { + "reason": "Executor-owned selected plugin MCP discovery is directly relevant to Decodex Control Plane authority boundaries.", + "type": "upstream_impact" + }, + { + "reason": "There is a public watch-note opportunity, but it should remain deferred until app-server activation is source-backed.", + "type": "social_candidate" + } + ], + "observed_change": "Codex adds an executor plugin MCP provider that reads selected plugin MCP declarations through the owning executor filesystem and contributes stdio registrations bound to that environment.", + "repo": "openai/codex", + "reviewed_at": "2026-06-15T12:08:04Z", + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27870", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "meta": "Merged 2026-06-15T09:52:06Z", + "title": "Discover stdio MCP servers from selected executor plugins", + "url": "https://github.com/openai/codex/pull/27870" + }, + { + "kind": "commit", + "title": "Discover MCP servers in selected executor plugins", + "url": "https://github.com/openai/codex/commit/8b78cf44bc9452427916b53c2a7c1b99a2b89434" + }, + { + "kind": "commit", + "title": "Narrow executor plugin access to its filesystem", + "url": "https://github.com/openai/codex/commit/b7180839381890e258ba75bfbf2b825b5064a41f" + } + ] + }, + "subject": { + "commit_shas": [ + "8489afd8e3f3a547b47d569cfc91a389af125b34", + "a7578adf4ccdae18f4cc9d3085d5aac15a431a3d", + "8b78cf44bc9452427916b53c2a7c1b99a2b89434", + "b7180839381890e258ba75bfbf2b825b5064a41f", + "a5453ca8f4c3c24b473c031811c1e36c3185efb8" + ], + "subject_id": "27870", + "subject_kind": "pr" + }, + "user_visible_path": "No direct production user path in this PR alone; it supplies the provider that later app-server activation can use to expose stdio MCP servers from selected executor plugins." +} diff --git a/artifacts/github/reviews/openai-codex-pr-27884.review.json b/artifacts/github/reviews/openai-codex-pr-27884.review.json new file mode 100644 index 000000000..f693d2b4f --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27884.review.json @@ -0,0 +1,74 @@ +{ + "adoption_opportunity": "Track selected-plugin MCP provenance and approval policy as catalog-owned state rather than deriving it from local plugin summaries or config-only MCP entries.", + "caveats": "The PR body says no production host contributes selected-plugin MCP registrations yet; the visible behavior depends on later activation by the stacked executor-provider work.", + "changed_surfaces": [ + "MCP catalog registration precedence", + "selected-plugin MCP provenance", + "MCP tool approval policy lookup", + "extension MCP contribution API", + "MCP server metadata", + "Config-to-MCP catalog construction", + "selected plugin regression tests" + ], + "community_value": "Moderate for MCP/plugin operators because it explains the precedence and attribution boundary needed before selected executor plugin MCPs can become user-visible.", + "compatibility_risk": "Low immediately because the PR says no production host contributes selected-plugin MCP registrations yet; medium once activated for clients that assume MCP provenance or approval policy only comes from config.toml or local plugin summaries.", + "confidence": "confirmed", + "control_plane_relevance": "High. Decodex Control Plane reasoning about selected capability roots, MCP provenance, and tool approval needs this catalog-owned selected-plugin tier.", + "deprecated_or_breaking_notes": "Selected-plugin registrations are a new precedence tier between auto-discovered plugins and explicit config. Existing config, compatibility, and extension overlay ordering is preserved.", + "evidence": [ + "PR #27884 states it adds a selected-plugin registration tier between auto-discovered plugins and explicit config before executor discovery is connected.", + "`codex-rs/codex-mcp/src/catalog.rs` adds `McpPluginAttribution` and changes plugin MCP sources to retain plugin ID plus display name.", + "`codex-rs/ext/extension-api/src/contributors/mcp.rs` adds a `SelectedPlugin` MCP contribution carrying name, plugin ID, display name, selection order, and config.", + "`codex-rs/core/src/mcp_tool_call.rs` reads selected-plugin tool approval from the running connection manager because selected registrations are absent from config.toml and the legacy plugin manager.", + "`codex-rs/core/src/mcp.rs` introduces ordered MCP overlays so selected-plugin contributions can be applied before later runtime overlays.", + "`codex-rs/codex-mcp/src/server.rs` stores default and per-tool approval modes in MCP server metadata.", + "The normalized bundle `artifacts/github/bundles/openai-codex-pr-27884.json` records 16 changed files for the merged PR." + ], + "next_actions": [ + { + "reason": "Selected-plugin MCP provenance and approval policy are Control Plane relevant even before production activation.", + "type": "upstream_impact" + }, + { + "reason": "The change is useful public context only as a caveated watch note because the PR says production contribution is not active yet.", + "type": "social_candidate" + } + ], + "observed_change": "Codex adds a selected-plugin MCP catalog precedence tier with retained plugin attribution and catalog-owned tool approval policy for selected registrations.", + "repo": "openai/codex", + "reviewed_at": "2026-06-15T12:08:04Z", + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27884", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "meta": "Merged 2026-06-15T09:10:52Z", + "title": "Add selected-plugin precedence and attribution to the MCP catalog", + "url": "https://github.com/openai/codex/pull/27884" + }, + { + "kind": "commit", + "title": "Resolve selected plugin MCP registrations", + "url": "https://github.com/openai/codex/commit/8489afd8e3f3a547b47d569cfc91a389af125b34" + }, + { + "kind": "commit", + "title": "Use selected MCP winner approval policy", + "url": "https://github.com/openai/codex/commit/a7578adf4ccdae18f4cc9d3085d5aac15a431a3d" + } + ] + }, + "subject": { + "commit_shas": [ + "8489afd8e3f3a547b47d569cfc91a389af125b34", + "a7578adf4ccdae18f4cc9d3085d5aac15a431a3d", + "fa026aaac9fd3a6ed649544c7c22df2fb600f9c4", + "947796d1630c443c43a45727dd8b2150b5dc00d6", + "e6b462681a96488861b6e2197327a5d0b01cc6f2" + ], + "subject_id": "27884", + "subject_kind": "pr" + }, + "user_visible_path": "No direct user-visible production path in this PR alone; it defines the selected-plugin MCP catalog behavior that later activation can expose." +} diff --git a/artifacts/github/reviews/openai-codex-pr-28001.review.json b/artifacts/github/reviews/openai-codex-pr-28001.review.json new file mode 100644 index 000000000..aca7e473d --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-28001.review.json @@ -0,0 +1,52 @@ +{ + "adoption_opportunity": "No Decodex runtime adoption needed; track only as release-window context for Windows packaging throughput.", + "changed_surfaces": [ + "Windows Rust release workflow", + "Windows ARM64 packaging runner selection" + ], + "community_value": "Low as a standalone public item; it may be useful only as release-rollup context for faster Windows artifact packaging.", + "compatibility_risk": "None identified for Decodex Control Plane or Codex runtime clients because the change only moves Windows ARM64 packaging onto x64 runners after binaries are already built.", + "confidence": "confirmed", + "control_plane_relevance": "None. This is release infrastructure rather than app-server, plugin, MCP, sandbox, CLI, or protocol behavior.", + "deprecated_or_breaking_notes": "No deprecated or removed user behavior identified.", + "evidence": [ + "PR #28001 says packaging only downloads, signs, archives, and compresses already built binaries and does not execute target code.", + "PR #28001 says the first release after parallelizing Windows packaging was held by the ARM64 packaging job and proposes running both packaging jobs on x64 runners.", + "`.github/workflows/rust-release-windows.yml` changes the `aarch64-pc-windows-msvc` packaging matrix runner from `windows-arm64` to `windows-x64`.", + "The normalized bundle `artifacts/github/bundles/openai-codex-pr-28001.json` records 1 changed workflow file for the merged PR." + ], + "next_actions": [ + { + "reason": "Record an explicit impact decision so downstream release automation does not need to redo source analysis for this release-infrastructure PR.", + "type": "upstream_impact" + } + ], + "observed_change": "Codex now packages Windows ARM64 release artifacts on Windows x64 runners instead of Windows ARM64 runners.", + "repo": "openai/codex", + "reviewed_at": "2026-06-15T12:08:04Z", + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-28001", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "meta": "Merged 2026-06-13T19:55:53Z", + "title": "[codex] package Windows ARM64 on x64", + "url": "https://github.com/openai/codex/pull/28001" + }, + { + "kind": "commit", + "title": "[codex] package Windows ARM64 on x64", + "url": "https://github.com/openai/codex/commit/4b3fd5e359221031616a66cad36299c4becd810b" + } + ] + }, + "subject": { + "commit_shas": [ + "4b3fd5e359221031616a66cad36299c4becd810b" + ], + "subject_id": "28001", + "subject_kind": "pr" + }, + "user_visible_path": "No direct runtime path; users may only experience faster or less bottlenecked Windows ARM64 release packaging." +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-27607.json b/artifacts/github/social-candidates/openai-codex-pr-27607.json new file mode 100644 index 000000000..6234c5093 --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-27607.json @@ -0,0 +1,59 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-27607", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "operator_impact", + "priority": "high", + "audience": "Codex plugin authors and Control Plane operators", + "candidate_text": [ + "Codex PR #27607 narrows ChatGPT/SIWC plugin routing: a plugin MCP server is hidden only when its server name matches an App declaration; non-conflicting MCP servers stay available. https://github.com/openai/codex/pull/27607", + "Control-plane takeaway: preserve App declaration names separately from connector IDs. App-vs-MCP routing now depends on the declared App name, not only the connector ID list." + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-27607.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-27607.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/27607" + ] + }, + "evidence_notes": [ + "PR #27607 says ChatGPT/SIWC should prefer the App route only for matching App/MCP names and keep separate MCP names available.", + "`LoadedPlugin.apps` now stores App declarations, and app-server derives connector IDs from those declarations for existing callers.", + "The checked upstream review and impact artifacts classify this as a Decodex plugin/MCP compatibility-risk item." + ], + "claims": [ + { + "text": "Codex now preserves plugin App declaration names separately from connector IDs.", + "evidence": "artifacts/github/reviews/openai-codex-pr-27607.review.json", + "confidence": "confirmed" + }, + { + "text": "For ChatGPT/SIWC auth, only plugin MCP servers whose names match declared App names are suppressed.", + "evidence": "https://github.com/openai/codex/pull/27607", + "confidence": "confirmed" + }, + { + "text": "Non-conflicting MCP servers from the same dual-surface plugin remain available.", + "evidence": "artifacts/github/bundles/openai-codex-pr-27607.json", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "publish", + "reason": "Source-backed operator-impact change for dual App/MCP plugin routing under ChatGPT/SIWC auth.", + "idempotency_key": "x:decodexspace:openai-codex-pr-27607:operator-impact" + }, + "caveats": [ + "Do not present this as removal of plugin MCP support.", + "The PR preserves effective App connector ID output for callers that only need connector IDs." + ], + "next_steps": [ + "Let Publisher decide whether to post; this automation must not write social_post/v1 or publish to X." + ] +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-27870.json b/artifacts/github/social-candidates/openai-codex-pr-27870.json new file mode 100644 index 000000000..bc058eafe --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-27870.json @@ -0,0 +1,59 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-27870", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "watch_note", + "priority": "normal", + "audience": "Codex MCP operators tracking selected executor plugin support", + "candidate_text": [ + "Defer public post for Codex PR #27870: it adds executor-owned discovery for stdio MCP servers in selected plugins, but the PR says app-server does not install the provider yet. https://github.com/openai/codex/pull/27870" + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-27870.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-27870.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/27870" + ] + }, + "evidence_notes": [ + "PR #27870 says selected plugin MCP config is read through the owning executor filesystem and normalized with the stable environment ID.", + "The PR body says app-server does not install the provider in this change and existing local plugin MCP loading is unchanged.", + "The checked impact artifact classifies this as a Control Plane candidate with deferred public publishing." + ], + "claims": [ + { + "text": "Codex adds provider code for stdio MCP declarations inside selected executor plugins.", + "evidence": "artifacts/github/reviews/openai-codex-pr-27870.review.json", + "confidence": "confirmed" + }, + { + "text": "Selected plugin MCP discovery uses the owning executor filesystem rather than host filesystem fallback.", + "evidence": "artifacts/github/bundles/openai-codex-pr-27870.json", + "confidence": "confirmed" + }, + { + "text": "This PR alone does not install the provider in app-server.", + "evidence": "https://github.com/openai/codex/pull/27870", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "defer", + "reason": "Provider groundwork is source-backed, but public capability claims should wait for app-server activation.", + "idempotency_key": "x:decodexspace:openai-codex-pr-27870:watch-note:defer" + }, + "caveats": [ + "Do not imply selected executor plugin MCPs are live in app-server yet.", + "The current source-backed vertical accepts stdio servers only." + ], + "next_steps": [ + "Do not hand this defer candidate to X Publisher as publish-ready.", + "Re-evaluate after app-server installation is reviewed." + ] +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-27884.json b/artifacts/github/social-candidates/openai-codex-pr-27884.json new file mode 100644 index 000000000..4202017df --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-27884.json @@ -0,0 +1,59 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-27884", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "watch_note", + "priority": "normal", + "audience": "Codex MCP and plugin operators tracking selected-plugin support", + "candidate_text": [ + "Defer public post for Codex PR #27884: it adds selected-plugin MCP catalog precedence, attribution, and approval-policy plumbing, but the PR says no production host contributes selected-plugin MCP registrations yet. https://github.com/openai/codex/pull/27884" + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-27884.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-27884.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/27884" + ] + }, + "evidence_notes": [ + "PR #27884 explicitly says this is a catalog and provenance change only and no production host contributes selected-plugin MCP registrations yet.", + "The extension API adds a selected-plugin MCP contribution shape and the MCP catalog retains plugin attribution.", + "The checked impact artifact classifies this as a Control Plane candidate with deferred public publishing." + ], + "claims": [ + { + "text": "Codex adds a selected-plugin MCP registration tier in the catalog.", + "evidence": "artifacts/github/reviews/openai-codex-pr-27884.review.json", + "confidence": "confirmed" + }, + { + "text": "Selected-plugin MCP approval policy is read from the running catalog/connection manager rather than local config.", + "evidence": "artifacts/github/bundles/openai-codex-pr-27884.json", + "confidence": "confirmed" + }, + { + "text": "This PR alone does not activate production selected-plugin MCP registrations.", + "evidence": "https://github.com/openai/codex/pull/27884", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "defer", + "reason": "Useful selected-plugin MCP groundwork, but public copy should wait for activation evidence.", + "idempotency_key": "x:decodexspace:openai-codex-pr-27884:watch-note:defer" + }, + "caveats": [ + "Do not imply selected executor plugin MCPs are live from this PR alone.", + "Pair with #27870 and app-server activation before making user-facing capability claims." + ], + "next_steps": [ + "Do not hand this defer candidate to X Publisher as publish-ready.", + "Re-evaluate after the activation PR is reviewed." + ] +}