From 9f5497647dc7eef77461cc0df40ddef77107e480 Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Wed, 10 Jun 2026 22:19:17 +0800 Subject: [PATCH] {"schema":"decodex/commit/1","summary":"Persist upstream Radar review artifacts","authority":"manual"} --- .../github/bundles/openai-codex-pr-27174.json | 63 +++++ .../github/bundles/openai-codex-pr-27198.json | 259 ++++++++++++++++++ .../github/bundles/openai-codex-pr-27375.json | 49 ++++ .../github/bundles/openai-codex-pr-27383.json | 193 +++++++++++++ .../github/impact/openai-codex-pr-27174.json | 44 +++ .../github/impact/openai-codex-pr-27198.json | 47 ++++ .../github/impact/openai-codex-pr-27383.json | 44 +++ .../review-queue/openai-codex-latest.json | 236 ++++++++-------- .../reviews/openai-codex-pr-27174.review.json | 66 +++++ .../reviews/openai-codex-pr-27198.review.json | 76 +++++ .../reviews/openai-codex-pr-27375.review.json | 53 ++++ .../reviews/openai-codex-pr-27383.review.json | 62 +++++ .../openai-codex-pr-27174.json | 54 ++++ .../openai-codex-pr-27198.json | 56 ++++ 14 files changed, 1189 insertions(+), 113 deletions(-) create mode 100644 artifacts/github/bundles/openai-codex-pr-27174.json create mode 100644 artifacts/github/bundles/openai-codex-pr-27198.json create mode 100644 artifacts/github/bundles/openai-codex-pr-27375.json create mode 100644 artifacts/github/bundles/openai-codex-pr-27383.json create mode 100644 artifacts/github/impact/openai-codex-pr-27174.json create mode 100644 artifacts/github/impact/openai-codex-pr-27198.json create mode 100644 artifacts/github/impact/openai-codex-pr-27383.json create mode 100644 artifacts/github/reviews/openai-codex-pr-27174.review.json create mode 100644 artifacts/github/reviews/openai-codex-pr-27198.review.json create mode 100644 artifacts/github/reviews/openai-codex-pr-27375.review.json create mode 100644 artifacts/github/reviews/openai-codex-pr-27383.review.json create mode 100644 artifacts/github/social-candidates/openai-codex-pr-27174.json create mode 100644 artifacts/github/social-candidates/openai-codex-pr-27198.json diff --git a/artifacts/github/bundles/openai-codex-pr-27174.json b/artifacts/github/bundles/openai-codex-pr-27174.json new file mode 100644 index 000000000..18496490d --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27174.json @@ -0,0 +1,63 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "jif-oai", + "committed_at": "2026-06-09T11:12:39Z", + "message": "fix(tui): keep child MCP warnings out of parent", + "sha": "ec27d8d71b40bd86900682e989e3ea64bcd72f79", + "url": "https://github.com/openai/codex/commit/ec27d8d71b40bd86900682e989e3ea64bcd72f79" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-09T11:47:58Z", + "message": "fix(tui): reject child MCP updates before status restore", + "sha": "1bfa6cfe558427f95780c9a1ea2c6a00257b8235", + "url": "https://github.com/openai/codex/commit/1bfa6cfe558427f95780c9a1ea2c6a00257b8235" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-09T11:56:16Z", + "message": "test(tui): annotate stream error details argument", + "sha": "ffedf368162b5ea8b085b5619505a18980a734d8", + "url": "https://github.com/openai/codex/commit/ffedf368162b5ea8b085b5619505a18980a734d8" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "MCP" + ], + "files": [ + { + "additions": 9, + "deletions": 0, + "patch_excerpt": "@@ -6,6 +6,15 @@ impl ChatWidget {\n notification: ServerNotification,\n replay_kind: Option,\n ) {\n+ // Reject misrouted child updates before shared notification handling mutates parent state.\n+ if let ServerNotification::McpServerStatusUpdated(notification) = ¬ification\n+ && let (Some(notification_thread_id), Some(thread_id)) =\n+ (notification.thread_id.as_deref(), self.thread_id())\n+ && notification_thread_id != thread_id.to_string()\n+ {\n+ return;\n+ }\n+\n let from_replay = replay_kind.is_some();\n let is_resume_initial_replay =\n matches!(replay_kind, Some(ReplayKind::ResumeInitialMessages));", + "path": "codex-rs/tui/src/chatwidget/protocol.rs", + "status": "modified" + }, + { + "additions": 41, + "deletions": 0, + "patch_excerpt": "@@ -25,6 +25,47 @@ fn notify_mcp_status_error(chat: &mut ChatWidget, name: &str, error: &str) {\n );\n }\n \n+#[tokio::test]\n+async fn mcp_startup_ignores_status_for_other_thread() {\n+ let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;\n+ chat.show_welcome_banner = false;\n+ chat.set_mcp_startup_expected_servers([\"sentry\".to_string()]);\n+ let parent_thread_id = ThreadId::new();\n+ let child_thread_id = ThreadId::new();\n+ chat.thread_id = Some(parent_thread_id);\n+ chat.on_stream_error(\n+ \"Connection interrupted, retrying\".to_string(),\n+ /*additional_details*/ None,\n+ );\n+ let status_before = chat.status_state.current_status.clone();\n+ let retry_status_header_before = chat.status_state.retry_status_header.clone();\n+\n+ for status in [\n+ McpServerStartupState::Starting,\n+ McpServerStartupState::Fai...", + "path": "codex-rs/tui/src/chatwidget/tests/mcp_startup.rs", + "status": "modified" + } + ], + "linked_issues": [], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Why\n\nMCP startup status notifications are thread-owned, but `ChatWidget` trusted upstream routing. If routing state delivered a tagged child notification to the active parent widget, the child MCP failure could still mutate the parent's startup state and transcript. Rejecting it only inside the MCP handler was also too late because shared notification handling could already restore and consume the parent's retry status.\n\n## What changed\n\n- Validate a tagged MCP status notification against the visible `ChatWidget` thread before shared notification handling mutates any parent state.\n- Cover child `Starting` and `Failed` notifications delivered to a retrying parent widget, asserting that they preserve its visible retry error and saved status header while producing no history or MCP status mutation.\n\n## User impact\n\nSubagent MCP startup failures remain scoped to the child transcript instead of appearing as duplicate warnings in the parent transcript.\n\n## Testing\n\n- `just test -p codex-tui mcp_startup_ignores_status_for_other_thread`\n- `just test -p codex-tui primary_thread_ignores_child_mcp_startup_notifications`\n- `just fmt`\n", + "labels": [], + "merged_at": "2026-06-10T09:45:50Z", + "number": 27174, + "state": "merged", + "title": "feat: keep child MCP warnings out of parent transcript", + "url": "https://github.com/openai/codex/pull/27174" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-27198.json b/artifacts/github/bundles/openai-codex-pr-27198.json new file mode 100644 index 000000000..47f4585fd --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27198.json @@ -0,0 +1,259 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "jif-oai", + "committed_at": "2026-06-09T15:04:42Z", + "message": "Use plugin-service MCP as hosted plugin runtime", + "sha": "c86af903969cdfa1fbeba67fbd454710f0d53de2", + "url": "https://github.com/openai/codex/commit/c86af903969cdfa1fbeba67fbd454710f0d53de2" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-09T16:03:04Z", + "message": "nit fix", + "sha": "fa313ba390f5372cd7dfa2e2a05b890f93b9b3a1", + "url": "https://github.com/openai/codex/commit/fa313ba390f5372cd7dfa2e2a05b890f93b9b3a1" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-09T21:13:57Z", + "message": "Regenerate config schema for removed Apps flag", + "sha": "19a14a08106a029b63c08e0a930c2c3bd769183b", + "url": "https://github.com/openai/codex/commit/19a14a08106a029b63c08e0a930c2c3bd769183b" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-10T10:38:13Z", + "message": "nits", + "sha": "e7ea9109a9e343fd1cc6a62d4c50d2d7b9cf1660", + "url": "https://github.com/openai/codex/commit/e7ea9109a9e343fd1cc6a62d4c50d2d7b9cf1660" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "MCP", + "HTTP", + "URL", + "CLI", + "CCA", + "SKU", + "CODEX_APPS_MCP_SERVER_NAME", + "TOML", + "LOCAL_FS", + "API", + "SKILL", + "FEATURES" + ], + "files": [ + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -1516,7 +1516,7 @@ async fn start_apps_server_with_delays_and_control_inner(\n get(workspace_settings_response),\n )\n .with_state(state)\n- .nest_service(\"/api/codex/apps\", mcp_service);\n+ .nest_service(\"/api/codex/ps/mcp\", mcp_service);\n \n let handle = tokio::spawn(async move {\n let _ = axum::serve(listener, router).await;", + "path": "codex-rs/app-server/tests/suite/v2/app_list.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -256,7 +256,7 @@ async fn start_resource_apps_mcp_server() -> Result<(String, JoinHandle<()>)> {\n Arc::new(LocalSessionManager::default()),\n StreamableHttpServerConfig::default(),\n );\n- let router = Router::new().nest_service(\"/api/codex/apps\", mcp_service);\n+ let router = Router::new().nest_service(\"/api/codex/ps/mcp\", mcp_service);\n let apps_server_handle = tokio::spawn(async move {\n let _ = axum::serve(listener, router).await;\n });", + "path": "codex-rs/app-server/tests/suite/v2/mcp_resource.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -406,7 +406,7 @@ async fn start_apps_server() -> Result<(String, JoinHandle<()>)> {\n get(list_directory_connectors),\n )\n .with_state(state)\n- .nest_service(\"/api/codex/apps\", mcp_service);\n+ .nest_service(\"/api/codex/ps/mcp\", mcp_service);\n \n let handle = tokio::spawn(async move {\n let _ = axum::serve(listener, router).await;", + "path": "codex-rs/app-server/tests/suite/v2/mcp_server_elicitation.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -1289,7 +1289,7 @@ async fn start_apps_server(\n get(list_directory_connectors),\n )\n .with_state(state)\n- .nest_service(\"/api/codex/apps\", mcp_service);\n+ .nest_service(\"/api/codex/ps/mcp\", mcp_service);\n \n let handle = tokio::spawn(async move {\n let _ = axum::serve(listener, router).await;", + "path": "codex-rs/app-server/tests/suite/v2/plugin_install.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -1914,7 +1914,7 @@ async fn start_apps_server(\n get(list_directory_connectors),\n )\n .with_state(state)\n- .nest_service(\"/api/codex/apps\", mcp_service);\n+ .nest_service(\"/api/codex/ps/mcp\", mcp_service);\n \n let handle = tokio::spawn(async move {\n let _ = axum::serve(listener, router).await;", + "path": "codex-rs/app-server/tests/suite/v2/plugin_read.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -29,8 +29,8 @@ pub use mcp::configured_mcp_servers;\n pub use mcp::effective_mcp_servers;\n pub use mcp::effective_mcp_servers_from_configured;\n pub use mcp::host_owned_codex_apps_enabled;\n+pub use mcp::hosted_plugin_runtime_mcp_server_config;\n pub use mcp::tool_plugin_provenance;\n-pub use mcp::with_codex_apps_mcp;\n \n pub use mcp::McpServerStatusSnapshot;\n pub use mcp::McpSnapshotDetail;", + "path": "codex-rs/codex-mcp/src/lib.rs", + "status": "modified" + }, + { + "additions": 37, + "deletions": 46, + "patch_excerpt": "@@ -106,8 +106,6 @@ pub struct McpPermissionPromptAutoApproveContext {\n pub struct McpConfig {\n /// Base URL for ChatGPT-hosted app MCP servers, copied from the root config.\n pub chatgpt_base_url: String,\n- /// Optional path override for the host-owned apps MCP server.\n- pub apps_mcp_path_override: Option,\n /// Optional product SKU forwarded to the host-owned apps MCP server.\n pub apps_mcp_product_sku: Option,\n /// Codex home directory used for MCP OAuth state and app-tool cache files.\n@@ -128,22 +126,18 @@ pub struct McpConfig {\n pub use_legacy_landlock: bool,\n /// Whether the app MCP integration is enabled by config.\n ///\n- /// ChatGPT auth is checked separately at runtime before the host-owned apps\n- /// MCP server is added.\n+ /// ChatGPT auth is checked separately before a materialized host-owned Apps\n+ /// server can ...", + "path": "codex-rs/codex-mcp/src/mcp/mod.rs", + "status": "modified" + }, + { + "additions": 15, + "deletions": 68, + "patch_excerpt": "@@ -16,7 +16,6 @@ use std::path::PathBuf;\n fn test_mcp_config(codex_home: PathBuf) -> McpConfig {\n McpConfig {\n chatgpt_base_url: \"https://chatgpt.com\".to_string(),\n- apps_mcp_path_override: None,\n apps_mcp_product_sku: None,\n codex_home,\n mcp_oauth_credentials_store_mode: OAuthCredentialsStoreMode::default(),\n@@ -27,7 +26,6 @@ fn test_mcp_config(codex_home: PathBuf) -> McpConfig {\n codex_linux_sandbox_exe: None,\n use_legacy_landlock: false,\n apps_enabled: false,\n- legacy_apps_mcp_loader_enabled: true,\n prefix_mcp_tool_names: true,\n client_elicitation_capability: ElicitationCapability::default(),\n configured_mcp_servers: HashMap::new(),\n@@ -178,52 +176,27 @@ fn tool_plugin_provenance_collects_app_and_mcp_sources() {\n #[test]\n fn codex_apps_mcp_url_for_base_url_keeps_existing_paths() {\n a...", + "path": "codex-rs/codex-mcp/src/mcp/mod_tests.rs", + "status": "modified" + }, + { + "additions": 26, + "deletions": 3, + "patch_excerpt": "@@ -9,6 +9,7 @@ use schemars::schema::ObjectValidation;\n use schemars::schema::RootSchema;\n use schemars::schema::Schema;\n use schemars::schema::SchemaObject;\n+use schemars::schema::SubschemaValidation;\n use serde_json::Map;\n use serde_json::Value;\n use std::path::Path;\n@@ -46,9 +47,7 @@ pub fn features_schema(schema_gen: &mut SchemaGenerator) -> Schema {\n if feature.id == codex_features::Feature::AppsMcpPathOverride {\n validation.properties.insert(\n feature.key.to_string(),\n- schema_gen.subschema_for::>(),\n+ removed_apps_mcp_path_override_schema(schema_gen),\n );\n continue;\n }\n@@ -76,6 +75,30 @@ pub fn features_schema(schema_gen: &mut SchemaGenerator) -> Schema {\n Schema::O...", + "path": "codex-rs/config/src/schema.rs", + "status": "modified" + }, + { + "additions": 35, + "deletions": 25, + "patch_excerpt": "@@ -226,18 +226,6 @@\n },\n \"type\": \"object\"\n },\n- \"AppsMcpPathOverrideConfigToml\": {\n- \"additionalProperties\": false,\n- \"properties\": {\n- \"enabled\": {\n- \"type\": \"boolean\"\n- },\n- \"path\": {\n- \"type\": \"string\"\n- }\n- },\n- \"type\": \"object\"\n- },\n \"AskForApproval\": {\n \"description\": \"Determines the conditions under which the user is consulted to approve running the command proposed by Codex.\",\n \"oneOf\": [\n@@ -408,7 +396,23 @@\n \"type\": \"boolean\"\n },\n \"apps_mcp_path_override\": {\n- \"$ref\": \"#/definitions/FeatureToml_for_AppsMcpPathOverrideConfigToml\"\n+ \"anyOf\": [\n+ {\n+ \"type\": \"boolean\"\n+ },\n+ {\n+ \"additionalProperties\": false,\n+ \"properti...", + "path": "codex-rs/core/config.schema.json", + "status": "modified" + }, + { + "additions": 0, + "deletions": 83, + "patch_excerpt": "@@ -5500,14 +5500,9 @@ async fn to_mcp_config_preserves_apps_feature_from_config() -> std::io::Result<(\n .await?;\n let plugins_manager = PluginsManager::new(codex_home.path().to_path_buf());\n \n- config.apps_mcp_path_override = Some(\"/custom/mcp\".to_string());\n config.apps_mcp_product_sku = Some(\"tpp\".to_string());\n let mcp_config = config.to_mcp_config(&plugins_manager).await;\n assert!(mcp_config.apps_enabled);\n- assert_eq!(\n- mcp_config.apps_mcp_path_override.as_deref(),\n- Some(\"/custom/mcp\")\n- );\n assert_eq!(mcp_config.apps_mcp_product_sku.as_deref(), Some(\"tpp\"));\n \n let _ = config.features.disable(Feature::Apps);\n@@ -8862,84 +8857,6 @@ allow_login_shell = false\n Ok(())\n }\n \n-#[tokio::test]\n-async fn config_loads_apps_mcp_path_override_from_feature_config() -> std::io::Result<()> {\n- let codex_home = TempDir::new()?;\n- let t...", + "path": "codex-rs/core/src/config/config_tests.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 24, + "patch_excerpt": "@@ -58,7 +58,6 @@ use codex_config::types::WindowsSandboxModeToml;\n use codex_core_plugins::PluginsConfigInput;\n use codex_exec_server::ExecutorFileSystem;\n use codex_exec_server::LOCAL_FS;\n-use codex_features::AppsMcpPathOverrideConfigToml;\n use codex_features::CodeModeConfigToml;\n use codex_features::Feature;\n use codex_features::FeatureConfigSource;\n@@ -931,9 +930,6 @@ pub struct Config {\n /// Base URL for requests to ChatGPT (as opposed to the OpenAI API).\n pub chatgpt_base_url: String,\n \n- /// Optional path override for the host-owned apps MCP server.\n- pub apps_mcp_path_override: Option,\n-\n /// Optional product SKU forwarded to the host-owned apps MCP server.\n pub apps_mcp_product_sku: Option,\n \n@@ -1415,7 +1411,6 @@ impl Config {\n \n McpConfig {\n chatgpt_base_url: self.chatgpt_base_url.clone(),\n- apps_mcp_path_ove...", + "path": "codex-rs/core/src/config/mod.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 0, + "patch_excerpt": "@@ -125,6 +125,9 @@ fn config_lock_for_comparison(\n ) -> ConfigLockfileToml {\n let mut lockfile = lockfile.clone();\n clear_config_lock_debug_controls(&mut lockfile.config);\n+ if let Some(features) = lockfile.config.features.as_mut() {\n+ features.clear_removed_compatibility_entries();\n+ }\n if options.allow_codex_version_mismatch {\n lockfile.codex_version.clear();\n }", + "path": "codex-rs/core/src/config_lock.rs", + "status": "modified" + }, + { + "additions": 16, + "deletions": 7, + "patch_excerpt": "@@ -11,6 +11,7 @@ use codex_mcp::CODEX_APPS_MCP_SERVER_NAME;\n use codex_mcp::EffectiveMcpServer;\n use codex_mcp::McpConfig;\n use codex_mcp::ToolPluginProvenance;\n+use codex_mcp::codex_apps_mcp_server_config;\n use codex_mcp::configured_mcp_servers;\n use codex_mcp::effective_mcp_servers;\n use codex_mcp::tool_plugin_provenance as collect_tool_plugin_provenance;\n@@ -40,16 +41,24 @@ impl McpManager {\n }\n }\n \n- /// Returns the MCP config after applying runtime-only extension overlays.\n+ /// Returns the MCP config after applying compatibility built-ins and\n+ /// runtime-only extension overlays.\n pub async fn runtime_config(&self, config: &Config) -> McpConfig {\n let mut mcp_config = config.to_mcp_config(self.plugins_manager.as_ref()).await;\n- let contributions = self.contributions(config).await;\n- if contributions\n- .iter()\n- ...", + "path": "codex-rs/core/src/mcp.rs", + "status": "modified" + }, + { + "additions": 26, + "deletions": 5, + "patch_excerpt": "@@ -2,7 +2,6 @@ use anyhow::Context;\n use codex_config::config_toml::ConfigLockfileToml;\n use codex_config::config_toml::ConfigToml;\n use codex_config::types::MemoriesToml;\n-use codex_features::AppsMcpPathOverrideConfigToml;\n use codex_features::Feature;\n use codex_features::FeatureToml;\n use codex_features::FeaturesToml;\n@@ -149,10 +148,6 @@ fn save_config_resolved_fields(\n resolved_config_to_toml(&config.multi_agent_v2, \"features.multi_agent_v2\")?;\n multi_agent_v2.enabled = Some(config.features.enabled(Feature::MultiAgentV2));\n features.multi_agent_v2 = Some(FeatureToml::Config(multi_agent_v2));\n- features.apps_mcp_path_override = Some(FeatureToml::Config(AppsMcpPathOverrideConfigToml {\n- enabled: Some(config.features.enabled(Feature::AppsMcpPathOverride)),\n- path: config.apps_mcp_path_override.clone(),\n- }));\n lock_config.memories = Some(resolve...", + "path": "codex-rs/core/src/session/config_lock.rs", + "status": "modified" + }, + { + "additions": 9, + "deletions": 1, + "patch_excerpt": "@@ -7,6 +7,8 @@ use codex_protocol::items::McpToolCallItem;\n use codex_protocol::items::McpToolCallStatus;\n use codex_protocol::items::TurnItem;\n use codex_protocol::mcp::CallToolResult;\n+use codex_protocol::protocol::TruncationPolicy;\n+use codex_utils_output_truncation::truncate_text;\n use rmcp::model::ListResourceTemplatesResult;\n use rmcp::model::ListResourcesResult;\n use rmcp::model::ReadResourceResult;\n@@ -270,7 +272,10 @@ fn normalize_required_string(field: &str, value: String) -> Result(payload: T) -> Result\n+fn serialize_function_output(\n+ payload: T,\n+ truncation_policy: TruncationPolicy,\n+) -> Result\n where\n T: Serialize,\n {\n@@ -279,6 +284,9 @@ where\n \"failed to serialize MCP resource response: {err}\"\n ))\n ...", + "path": "codex-rs/core/src/tools/handlers/mcp_resource.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -117,7 +117,7 @@ impl ToolExecutor for ListMcpResourceTemplatesHandler {\n .await;\n \n match payload_result {\n- Ok(payload) => match serialize_function_output(payload) {\n+ Ok(payload) => match serialize_function_output(payload, turn.truncation_policy) {\n Ok(output) => {\n let content = function_call_output_content_items_to_text(&output.body)\n .unwrap_or_default();", + "path": "codex-rs/core/src/tools/handlers/mcp_resource/list_mcp_resource_templates.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -115,7 +115,7 @@ impl ToolExecutor for ListMcpResourcesHandler {\n .await;\n \n match payload_result {\n- Ok(payload) => match serialize_function_output(payload) {\n+ Ok(payload) => match serialize_function_output(payload, turn.truncation_policy) {\n Ok(output) => {\n let content = function_call_output_content_items_to_text(&output.body)\n .unwrap_or_default();", + "path": "codex-rs/core/src/tools/handlers/mcp_resource/list_mcp_resources.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -93,7 +93,7 @@ impl ToolExecutor for ReadMcpResourceHandler {\n .await;\n \n match payload_result {\n- Ok(payload) => match serialize_function_output(payload) {\n+ Ok(payload) => match serialize_function_output(payload, turn.truncation_policy) {\n Ok(output) => {\n let content = function_call_output_content_items_to_text(&output.body)\n .unwrap_or_default();", + "path": "codex-rs/core/src/tools/handlers/mcp_resource/read_mcp_resource.rs", + "status": "modified" + }, + { + "additions": 37, + "deletions": 0, + "patch_excerpt": "@@ -1,6 +1,7 @@\n use super::*;\n use pretty_assertions::assert_eq;\n use rmcp::model::AnnotateAble;\n+use rmcp::model::ResourceContents;\n use serde_json::json;\n \n fn resource(uri: &str, name: &str) -> Resource {\n@@ -123,3 +124,39 @@ fn template_with_server_serializes_server_field() {\n })\n );\n }\n+\n+#[test]\n+fn serialize_function_output_preserves_small_payload() {\n+ let payload = json!({\"server\": \"hosted\", \"resources\": []});\n+ let expected = serde_json::to_string(&payload).expect(\"serialize payload\");\n+\n+ let output = serialize_function_output(payload, TruncationPolicy::Bytes(1_024))\n+ .expect(\"serialize function output\")\n+ .into_text();\n+\n+ assert_eq!(output, expected);\n+}\n+\n+#[test]\n+fn serialize_function_output_caps_read_resource_payload() {\n+ let truncation_policy = TruncationPolicy::Bytes(8_000);\n+ let payload = ReadResourcePayload {\n+ s...", + "path": "codex-rs/core/src/tools/handlers/mcp_resource_tests.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 9, + "patch_excerpt": "@@ -11,12 +11,3 @@ pub enum McpServerContribution {\n /// Removes a named MCP server.\n Remove { name: String },\n }\n-\n-impl McpServerContribution {\n- /// Returns the stable server name owned by this contribution.\n- pub fn name(&self) -> &str {\n- match self {\n- Self::Set { name, .. } | Self::Remove { name } => name,\n- }\n- }\n-}", + "path": "codex-rs/ext/extension-api/src/contributors/mcp.rs", + "status": "modified" + }, + { + "additions": 5, + "deletions": 6, + "patch_excerpt": "@@ -3,12 +3,12 @@ use codex_extension_api::ExtensionRegistryBuilder;\n use codex_extension_api::McpServerContribution;\n use codex_extension_api::McpServerContributor;\n use codex_mcp::CODEX_APPS_MCP_SERVER_NAME;\n-use codex_mcp::codex_apps_mcp_server_config;\n+use codex_mcp::hosted_plugin_runtime_mcp_server_config;\n \n-struct HostedAppsMcpExtension;\n+struct HostedPluginRuntimeExtension;\n \n #[async_trait::async_trait]\n-impl McpServerContributor for HostedAppsMcpExtension {\n+impl McpServerContributor for HostedPluginRuntimeExtension {\n async fn contribute(&self, config: &Config) -> Vec {\n let name = CODEX_APPS_MCP_SERVER_NAME.to_string();\n if !config.features.enabled(codex_features::Feature::Apps) {\n@@ -17,15 +17,14 @@ impl McpServerContributor for HostedAppsMcpExtension {\n \n vec![McpServerContribution::Set {\n ...", + "path": "codex-rs/ext/mcp/src/lib.rs", + "status": "modified" + }, + { + "additions": 76, + "deletions": 9, + "patch_excerpt": "@@ -6,44 +6,100 @@ use codex_core::config::Config;\n use codex_core::config::ConfigBuilder;\n use codex_core_plugins::PluginsManager;\n use codex_extension_api::ExtensionRegistryBuilder;\n+use codex_extension_api::McpServerContribution;\n+use codex_extension_api::McpServerContributor;\n use codex_login::CodexAuth;\n use codex_mcp::CODEX_APPS_MCP_SERVER_NAME;\n use pretty_assertions::assert_eq;\n \n+type TestResult = Result<(), Box>;\n+\n #[tokio::test]\n-async fn contributes_hosted_apps_mcp_without_an_executor() -> Result<(), Box>\n-{\n+async fn contributes_hosted_plugin_runtime_without_an_executor() -> TestResult {\n let codex_home = tempfile::tempdir()?;\n let config = ConfigBuilder::default()\n .codex_home(codex_home.path().to_path_buf())\n .fallback_cwd(Some(codex_home.path().to_path_buf()))\n .cli_overrides(vec![\n ...", + "path": "codex-rs/ext/mcp/tests/hosted_apps_mcp.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 13, + "patch_excerpt": "@@ -70,21 +70,11 @@ impl FeatureConfig for MultiAgentV2ConfigToml {\n \n #[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, JsonSchema)]\n #[serde(deny_unknown_fields)]\n-pub struct AppsMcpPathOverrideConfigToml {\n+pub(crate) struct RemovedAppsMcpPathOverrideConfigToml {\n #[serde(skip_serializing_if = \"Option::is_none\")]\n- pub enabled: Option,\n+ enabled: Option,\n #[serde(skip_serializing_if = \"Option::is_none\")]\n- pub path: Option,\n-}\n-\n-impl FeatureConfig for AppsMcpPathOverrideConfigToml {\n- fn enabled(&self) -> Option {\n- self.enabled.or(self.path.as_ref().map(|_| true))\n- }\n-\n- fn set_enabled(&mut self, enabled: bool) {\n- self.enabled = Some(enabled);\n- }\n+ path: Option,\n }\n \n #[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, JsonSchema)]", + "path": "codex-rs/features/src/feature_configs.rs", + "status": "modified" + }, + { + "additions": 16, + "deletions": 16, + "patch_excerpt": "@@ -16,13 +16,13 @@ use toml::Table;\n \n mod feature_configs;\n mod legacy;\n-pub use feature_configs::AppsMcpPathOverrideConfigToml;\n pub use feature_configs::CodeModeConfigToml;\n pub use feature_configs::MultiAgentV2ConfigToml;\n pub use feature_configs::NetworkProxyConfigToml;\n pub use feature_configs::NetworkProxyDomainPermissionToml;\n pub use feature_configs::NetworkProxyModeToml;\n pub use feature_configs::NetworkProxyUnixSocketPermissionToml;\n+use feature_configs::RemovedAppsMcpPathOverrideConfigToml;\n use legacy::LegacyFeatureToggles;\n pub use legacy::legacy_feature_keys;\n \n@@ -143,7 +143,7 @@ pub enum Feature {\n Apps,\n /// Enable MCP apps.\n EnableMcpApps,\n- /// Use the new path for the host-owned apps MCP server.\n+ /// Removed compatibility flag for the legacy Apps MCP path override.\n AppsMcpPathOverride,\n /// Removed compatibility flag retained as a no-op n...", + "path": "codex-rs/features/src/lib.rs", + "status": "modified" + }, + { + "additions": 21, + "deletions": 0, + "patch_excerpt": "@@ -83,6 +83,27 @@ fn plugin_hooks_is_removed_and_disabled_by_default() {\n assert_eq!(feature_for_key(\"plugin_hooks\"), Some(Feature::PluginHooks));\n }\n \n+#[test]\n+fn removed_apps_mcp_path_override_shapes_are_ignored() {\n+ let features = [\n+ toml::from_str::(\"apps_mcp_path_override = true\")\n+ .expect(\"boolean compatibility form should deserialize\"),\n+ toml::from_str::(\n+ r#\"\n+[apps_mcp_path_override]\n+enabled = true\n+path = \"/custom/mcp\"\n+\"#,\n+ )\n+ .expect(\"structured compatibility form should deserialize\"),\n+ ];\n+\n+ assert_eq!(\n+ features.map(|features| features.entries()),\n+ [BTreeMap::new(), BTreeMap::new()]\n+ );\n+}\n+\n #[test]\n fn code_mode_only_requires_code_mode() {\n let mut features = Features::with_defaults();", + "path": "codex-rs/features/src/tests.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 1, + "patch_excerpt": "@@ -251,7 +251,6 @@ fn new_config(model: Option, arg0_paths: Arg0DispatchPaths) -> anyhow::R\n model_catalog: None,\n model_verbosity: None,\n chatgpt_base_url: \"https://chatgpt.com/backend-api/\".to_string(),\n- apps_mcp_path_override: None,\n apps_mcp_product_sku: None,\n realtime_audio: RealtimeAudioConfig::default(),\n experimental_realtime_ws_base_url: None,", + "path": "codex-rs/thread-manager-sample/src/main.rs", + "status": "modified" + } + ], + "linked_issues": [ + "#27191", + "#27184" + ], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Stack\n\n- Base: #27191\n- This PR is the third vertical and should be reviewed against `jif/external-plugins-2`, not `main`.\n\n## Why\n\n#27191 moves the host-owned Apps MCP registration behind an extension contributor, but deliberately preserves the existing endpoint-selection feature while that contribution contract lands. App-server can therefore resolve the server through extensions, yet the hosted plugin endpoint is still selected through temporary `apps_mcp_path_override` plumbing.\n\nThat is not the long-term plugin model. A plugin can bundle skills, connectors, MCP servers, and hooks, and those components do not all need the same source or execution environment. In particular, an authenticated HTTP MCP server can expose plugin capabilities directly from a backend without an executor or an orchestrator filesystem.\n\nThis PR completes that hosted vertical. App-server's MCP extension now owns the aggregate hosted plugin runtime at `/ps/mcp`. Connector actions continue to arrive as MCP tools, while backend-provided skills arrive as MCP resources and use Codex's existing resource list/read paths. No second backend client, skill filesystem, or generic plugin activation framework is introduced.\n\nThe backend route remains the hosted implementation. This change replaces Codex's temporary endpoint-selection mechanism, not the service behind the endpoint.\n\n## What changed\n\n### Hosted plugin runtime\n\nThe MCP extension now contributes `codex_apps` as the hosted plugin runtime rather than as a configurable Apps endpoint:\n\n- `https://chatgpt.com` resolves to `https://chatgpt.com/backend-api/ps/mcp`;\n- a bare custom ChatGPT base resolves to `/api/codex/ps/mcp`;\n- the existing product-SKU header and ChatGPT authentication behavior are preserved;\n- executor availability is never consulted for this streamable HTTP transport.\n\nThe same MCP connection carries both component shapes supported by the hosted endpoint:\n\n- connector actions are discovered and invoked as MCP tools;\n- hosted skills are enumerated and read as MCP resources through the existing `list_mcp_resources` and `read_mcp_resource` paths.\n\nThis keeps component access in the subsystem that already owns the protocol instead of downloading backend skills into an orchestrator filesystem or inventing a parallel hosted-skill client.\n\n### Explicit runtime ordering\n\n`McpManager` now resolves the reserved `codex_apps` entry in three ordered phases:\n\n1. install the legacy Apps fallback for compatibility;\n2. apply ordered extension `Set` or `Remove` overlays;\n3. apply the final ChatGPT-auth gate without synthesizing the server again.\n\nThis ordering is important:\n\n- an ordinary configured or plugin MCP server cannot claim the auth-bearing `codex_apps` name;\n- an extension-contributed hosted runtime wins over the fallback;\n- an extension `Remove` remains authoritative;\n- a host without the MCP extension retains the legacy Apps endpoint and current local-only behavior.\n\nThe temporary `legacy_apps_mcp_loader_enabled` coordination flag is no longer needed.\n\n### Remove the path override\n\nThe `apps_mcp_path_override` feature and its runtime plumbing are removed, including:\n\n- the feature registry entry and structured feature config;\n- `Config` and `McpConfig` fields;\n- config schema output;\n- config-lock materialization;\n- URL override handling in `codex-mcp`.\n\nExisting boolean and structured forms still deserialize as ignored compatibility input. They are omitted from new serialized config, and config-lock comparison normalizes the removed input so older locks remain replayable.\n\n### App-server coverage\n\nApp-server MCP fixtures now serve the hosted route at `/api/codex/ps/mcp`. Existing resource-read and tool/elicitation flows therefore exercise the extension-owned endpoint rather than succeeding through the legacy fallback.\n\nThe stack also adds the missing `codex_chatgpt::connectors` re-export for the manager-backed connector helper introduced in #27191.\n\n## Compatibility\n\n- App-server installs the extension and uses `/ps/mcp` for the hosted runtime.\n- CLI and other hosts that do not install the extension retain the legacy Apps endpoint.\n- Apps disabled or non-ChatGPT authentication removes `codex_apps` from the effective runtime view.\n- Existing local plugins, local skills, executor-selected skills, configured MCP servers, and MCP OAuth behavior are otherwise unchanged.\n- Backend plugin enablement remains account/workspace state owned by the hosted endpoint; this PR does not add thread-local backend plugin selection.\n\n## Architectural fit\n\nThe stack now proves two independent runtime shapes:\n\n1. #27184 resolves filesystem-backed skills through the executor that owns a selected root.\n2. #27191 and this PR resolve a backend-hosted HTTP MCP through an extension with no executor.\n\nTogether they preserve the intended separation:\n\n- selection identifies a plugin/root when explicit selection is needed;\n- each component's owning extension resolves its concrete access mechanism;\n- execution stays with the runtime required by that component;\n- existing skills, MCP, connector, and hook subsystems remain the downstream consumers.\n\n## Planned follow-ups\n\n1. **Executor stdio MCP:** selecting an executor plugin registers a manifest-declared stdio MCP server and executes it in the environment that owns the plugin.\n2. **Optional backend selection:** only if CCA needs thread-local selection distinct from backend account/workspace enablement, add a concrete backend-owned capability location and surface those selected skills through the skills catalog.\n3. **Connector metadata and hooks:** activate those plugin components through their existing owning subsystems, with executor hooks remaining environment-bound.\n4. **Propagation and persistence:** define explicit resume, fork, subagent, refresh, and environment-removal semantics once selected roots have multiple real consumers.\n5. **Local convergence:** migrate legacy local skill, MCP, connector, and hook paths behind their owning extensions one vertical at a time, then remove duplicate core managers and compatibility plumbing after parity.\n\n## Verification\n\nCoverage in this change exercises:\n\n- extension-owned `/backend-api/ps/mcp` registration without an executor;\n- preservation of the legacy endpoint in hosts without the extension;\n- extension `Set` and `Remove` precedence over the legacy fallback;\n- ChatGPT-auth gating for the reserved server;\n- hosted MCP resource reads with and without an active thread;\n- connector tool invocation and MCP elicitation through the hosted route;\n- ignored boolean and structured forms of the removed path override;\n- config-lock replay compatibility for the removed feature.\n\n`cargo check -p codex-features -p codex-mcp-extension -p codex-app-server` passes. Tests and Clippy were not run locally under the current development instruction; CI provides the full validation pass.\n", + "labels": [], + "merged_at": "2026-06-10T10:54:21Z", + "number": 27198, + "state": "merged", + "title": "Use plugin-service MCP as the hosted plugin runtime", + "url": "https://github.com/openai/codex/pull/27198" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-27375.json b/artifacts/github/bundles/openai-codex-pr-27375.json new file mode 100644 index 000000000..7e5eeee1c --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27375.json @@ -0,0 +1,49 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "jif-oai", + "committed_at": "2026-06-10T10:41:45Z", + "message": "multi-agent: tag spawn metric with version", + "sha": "90fc87dd1a1c7449323fcff804c77abfea377a23", + "url": "https://github.com/openai/codex/commit/90fc87dd1a1c7449323fcff804c77abfea377a23" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "--check" + ], + "files": [ + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -204,7 +204,7 @@ async fn handle_spawn_agent(\n turn.session_telemetry.counter(\n \"codex.multi_agent.spawn\",\n /*inc*/ 1,\n- &[(\"role\", role_tag)],\n+ &[(\"role\", role_tag), (\"version\", \"v1\")],\n );\n \n Ok(SpawnAgentResult {", + "path": "codex-rs/core/src/tools/handlers/multi_agents/spawn.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -165,7 +165,7 @@ async fn handle_spawn_agent(\n turn.session_telemetry.counter(\n \"codex.multi_agent.spawn\",\n /*inc*/ 1,\n- &[(\"role\", role_tag)],\n+ &[(\"role\", role_tag), (\"version\", \"v2\")],\n );\n let task_name = String::from(new_agent_path);", + "path": "codex-rs/core/src/tools/handlers/multi_agents_v2/spawn.rs", + "status": "modified" + } + ], + "linked_issues": [], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Summary\n- tag legacy multi-agent spawn metrics with `version=v1`\n- tag multi-agent v2 spawn metrics with `version=v2`\n\n## Why\n`codex.multi_agent.spawn` is emitted by both runtimes, so the existing metric cannot distinguish v2 adoption from aggregate multi-agent spawning. The bounded version tag makes that breakdown directly queryable without changing the counter's success-only semantics.\n\n## Validation\n- `just fmt`\n- `git diff --check`\n- Tests and Clippy were intentionally left to CI.", + "labels": [], + "merged_at": "2026-06-10T11:06:49Z", + "number": 27375, + "state": "merged", + "title": "[codex] Tag multi-agent spawn metrics with version", + "url": "https://github.com/openai/codex/pull/27375" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-27383.json b/artifacts/github/bundles/openai-codex-pr-27383.json new file mode 100644 index 000000000..e652d73f4 --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27383.json @@ -0,0 +1,193 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "jif-oai", + "committed_at": "2026-06-10T12:01:45Z", + "message": "Remove async-trait from extension contributors", + "sha": "9c223af9d01e5e895d668d29291c45b794eeda83", + "url": "https://github.com/openai/codex/commit/9c223af9d01e5e895d668d29291c45b794eeda83" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-10T12:04:59Z", + "message": "Merge branch 'main' into jif/remove-async-trait-extension-api", + "sha": "6b0115c1838acfe99839e07a64bcaffbf9bebeb4", + "url": "https://github.com/openai/codex/commit/6b0115c1838acfe99839e07a64bcaffbf9bebeb4" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "RPITIT", + "MCP", + "CODEX_APPS_MCP_SERVER_NAME", + "MAX_SKILL_NAME_BYTES", + "MAX_SKILL_PATH_BYTES" + ], + "files": [ + { + "additions": 0, + "deletions": 3, + "patch_excerpt": "@@ -2921,7 +2921,6 @@ dependencies = [\n name = \"codex-extension-api\"\n version = \"0.0.0\"\n dependencies = [\n- \"async-trait\",\n \"codex-config\",\n \"codex-context-fragments\",\n \"codex-protocol\",\n@@ -3070,7 +3069,6 @@ dependencies = [\n name = \"codex-guardian\"\n version = \"0.0.0\"\n dependencies = [\n- \"async-trait\",\n \"codex-core\",\n \"codex-extension-api\",\n \"codex-protocol\",\n@@ -3255,7 +3253,6 @@ dependencies = [\n name = \"codex-mcp-extension\"\n version = \"0.0.0\"\n dependencies = [\n- \"async-trait\",\n \"codex-config\",\n \"codex-core\",\n \"codex-core-plugins\",", + "path": "codex-rs/Cargo.lock", + "status": "modified" + }, + { + "additions": 131, + "deletions": 96, + "patch_excerpt": "@@ -1922,26 +1922,27 @@ async fn record_token_usage_info_notifies_extension_contributors() {\n records: Arc>>,\n }\n \n- #[async_trait::async_trait]\n impl codex_extension_api::TokenUsageContributor for TokenUsageRecorder {\n- async fn on_token_usage(\n- &self,\n- session_store: &codex_extension_api::ExtensionData,\n- thread_store: &codex_extension_api::ExtensionData,\n- turn_store: &codex_extension_api::ExtensionData,\n- token_usage: &TokenUsageInfo,\n- ) {\n- self.records\n- .lock()\n- .expect(\"token usage records lock\")\n- .push(RecordedTokenUsage {\n- session_level_id: session_store.level_id().to_string(),\n- thread_level_id: thread_store.level_id().to_string(),\n- ...", + "path": "codex-rs/core/src/session/tests.rs", + "status": "modified" + }, + { + "additions": 14, + "deletions": 13, + "patch_excerpt": "@@ -7,20 +7,21 @@ use std::sync::Arc;\n \n struct RewriteAgentMessageContributor;\n \n-#[async_trait::async_trait]\n impl TurnItemContributor for RewriteAgentMessageContributor {\n- async fn contribute(\n- &self,\n- _thread_store: &ExtensionData,\n- _turn_store: &ExtensionData,\n- item: &mut TurnItem,\n- ) -> Result<(), String> {\n- if let TurnItem::AgentMessage(agent_message) = item {\n- agent_message.content = vec![AgentMessageContent::Text {\n- text: \"plan contributed assistant text\".to_string(),\n- }];\n- }\n- Ok(())\n+ fn contribute<'a>(\n+ &'a self,\n+ _thread_store: &'a ExtensionData,\n+ _turn_store: &'a ExtensionData,\n+ item: &'a mut TurnItem,\n+ ) -> codex_extension_api::ExtensionFuture<'a, Result<(), String>> {\n+ Box::pin(async move {\n+ if let TurnItem::Agen...", + "path": "codex-rs/core/src/session/turn_tests.rs", + "status": "modified" + }, + { + "additions": 30, + "deletions": 28, + "patch_excerpt": "@@ -167,41 +167,43 @@ struct TestTurnItemContributor;\n #[derive(Debug)]\n struct TurnItemContributorRan;\n \n-#[async_trait::async_trait]\n impl TurnItemContributor for TestTurnItemContributor {\n- async fn contribute(\n- &self,\n- _thread_store: &ExtensionData,\n- turn_store: &ExtensionData,\n- item: &mut TurnItem,\n- ) -> Result<(), String> {\n- turn_store.insert(TurnItemContributorRan);\n- if let TurnItem::AgentMessage(agent_message) = item {\n- agent_message.memory_citation = Some(MemoryCitation {\n- entries: Vec::new(),\n- rollout_ids: Vec::new(),\n- });\n- }\n- Ok(())\n+ fn contribute<'a>(\n+ &'a self,\n+ _thread_store: &'a ExtensionData,\n+ turn_store: &'a ExtensionData,\n+ item: &'a mut TurnItem,\n+ ) -> codex_extension_api::ExtensionFuture<'a, Result<(), St...", + "path": "codex-rs/core/src/stream_events_utils_tests.rs", + "status": "modified" + }, + { + "additions": 17, + "deletions": 13, + "patch_excerpt": "@@ -394,20 +394,24 @@ async fn start_thread_seeds_extension_data_before_lifecycle_contributors_run() {\n observed: Arc>>,\n }\n \n- #[async_trait::async_trait]\n impl codex_extension_api::ThreadLifecycleContributor for InitialDataRecorder {\n- async fn on_thread_start(&self, input: codex_extension_api::ThreadStartInput<'_, Config>) {\n- let marker = input\n- .thread_store\n- .get::()\n- .expect(\"initial extension data should be available\");\n- *self\n- .observed\n- .lock()\n- .unwrap_or_else(std::sync::PoisonError::into_inner) = Some((\n- input.thread_store.level_id().to_string(),\n- marker.0.to_string(),\n- ));\n+ fn on_thread_start<'a>(\n+ &'a sel...", + "path": "codex-rs/core/src/thread_manager_tests.rs", + "status": "modified" + }, + { + "additions": 10, + "deletions": 9, + "patch_excerpt": "@@ -388,16 +388,17 @@ mod tests {\n \n struct RecordExtensionTurnItemContributor;\n \n- #[async_trait::async_trait]\n impl TurnItemContributor for RecordExtensionTurnItemContributor {\n- async fn contribute(\n- &self,\n- _thread_store: &ExtensionData,\n- turn_store: &ExtensionData,\n- _item: &mut TurnItem,\n- ) -> Result<(), String> {\n- turn_store.insert(ExtensionTurnItemContributorRan);\n- Ok(())\n+ fn contribute<'a>(\n+ &'a self,\n+ _thread_store: &'a ExtensionData,\n+ turn_store: &'a ExtensionData,\n+ _item: &'a mut TurnItem,\n+ ) -> codex_extension_api::ExtensionFuture<'a, Result<(), String>> {\n+ Box::pin(async move {\n+ turn_store.insert(ExtensionTurnItemContributorRan);\n+ Ok(())\n+ })\n }\n }", + "path": "codex-rs/core/src/tools/handlers/extension_tools.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 1, + "patch_excerpt": "@@ -14,7 +14,6 @@ doctest = false\n workspace = true\n \n [dependencies]\n-async-trait = { workspace = true }\n codex-config = { workspace = true }\n codex-context-fragments = { workspace = true }\n codex-protocol = { workspace = true }", + "path": "codex-rs/ext/extension-api/Cargo.toml", + "status": "modified" + }, + { + "additions": 83, + "deletions": 42, + "patch_excerpt": "@@ -1,4 +1,5 @@\n use std::future::Future;\n+use std::pin::Pin;\n use std::sync::Arc;\n \n use codex_context_fragments::ContextualUserFragment;\n@@ -36,16 +37,18 @@ pub use turn_lifecycle::TurnErrorInput;\n pub use turn_lifecycle::TurnStartInput;\n pub use turn_lifecycle::TurnStopInput;\n \n+/// Boxed, sendable future returned by asynchronous extension contributors.\n+pub type ExtensionFuture<'a, T> = Pin + Send + 'a>>;\n+\n /// Extension contribution that resolves runtime MCP servers from host config.\n ///\n /// Contributors run in registration order. Later contributions for the same\n /// name replace earlier ones. Implementations must contribute only names they\n /// own and must apply any source-specific policy before returning a server.\n /// Plugin-owned servers and their provenance continue to be resolved by the\n /// plugin manager until that ownership moves into an exte...", + "path": "codex-rs/ext/extension-api/src/contributors.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -31,6 +31,7 @@ pub use codex_tools::parse_tool_input_schema_without_compaction;\n pub use contributors::ApprovalReviewContributor;\n pub use contributors::ConfigContributor;\n pub use contributors::ContextContributor;\n+pub use contributors::ExtensionFuture;\n pub use contributors::McpServerContribution;\n pub use contributors::McpServerContributor;\n pub use contributors::PromptFragment;", + "path": "codex-rs/ext/extension-api/src/lib.rs", + "status": "modified" + }, + { + "additions": 66, + "deletions": 61, + "patch_excerpt": "@@ -1,5 +1,3 @@\n-use std::future::Future;\n-use std::pin::Pin;\n use std::sync::Arc;\n use std::sync::Mutex;\n \n@@ -9,6 +7,7 @@ use codex_extension_api::ContextContributor;\n use codex_extension_api::ContextualUserFragment;\n use codex_extension_api::ExtensionData;\n use codex_extension_api::ExtensionEventSink;\n+use codex_extension_api::ExtensionFuture;\n use codex_extension_api::ExtensionRegistryBuilder;\n use codex_extension_api::PromptFragment;\n use codex_extension_api::ThreadLifecycleContributor;\n@@ -37,32 +36,32 @@ impl ContextContributor for AllContributors {\n &'a self,\n _session_store: &'a ExtensionData,\n _thread_store: &'a ExtensionData,\n- ) -> Pin> + Send + 'a>> {\n+ ) -> ExtensionFuture<'a, Vec> {\n Box::pin(std::future::ready(Vec::new()))\n }\n }\n \n-#[async_trait::async_trait]\n impl Thread...", + "path": "codex-rs/ext/extension-api/tests/registry.rs", + "status": "modified" + }, + { + "additions": 199, + "deletions": 184, + "patch_excerpt": "@@ -1,12 +1,12 @@\n use std::sync::Arc;\n use std::sync::Weak;\n \n-use async_trait::async_trait;\n use codex_analytics::AnalyticsEventsClient;\n use codex_core::ThreadManager;\n use codex_extension_api::ConfigContributor;\n use codex_extension_api::ExtensionData;\n use codex_extension_api::ExtensionEventSink;\n+use codex_extension_api::ExtensionFuture;\n use codex_extension_api::ExtensionRegistryBuilder;\n use codex_extension_api::ThreadIdleInput;\n use codex_extension_api::ThreadLifecycleContributor;\n@@ -95,76 +95,83 @@ impl GoalExtension {\n }\n }\n \n-#[async_trait]\n impl ThreadLifecycleContributor for GoalExtension\n where\n C: Send + Sync + 'static,\n {\n- async fn on_thread_start(&self, input: ThreadStartInput<'_, C>) {\n- let enabled = (self.goals_enabled)(input.config);\n- let tools_available_for_thread = input.persistent_thread_state_available\n- && !...", + "path": "codex-rs/ext/goal/src/extension.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 1, + "patch_excerpt": "@@ -14,7 +14,6 @@ doctest = false\n workspace = true\n \n [dependencies]\n-async-trait = { workspace = true }\n codex-core = { workspace = true }\n codex-extension-api = { workspace = true }\n codex-protocol = { workspace = true }", + "path": "codex-rs/ext/guardian/Cargo.toml", + "status": "modified" + }, + { + "additions": 14, + "deletions": 8, + "patch_excerpt": "@@ -3,6 +3,7 @@ use std::sync::Arc;\n use codex_core::config::Config;\n use codex_extension_api::AgentSpawnFuture;\n use codex_extension_api::AgentSpawner;\n+use codex_extension_api::ExtensionFuture;\n use codex_extension_api::ExtensionRegistryBuilder;\n use codex_extension_api::ThreadLifecycleContributor;\n use codex_extension_api::ThreadStartInput;\n@@ -47,18 +48,23 @@ impl GuardianThreadContext {\n }\n }\n \n-#[async_trait::async_trait]\n impl ThreadLifecycleContributor for GuardianExtension\n where\n S: Send + Sync,\n {\n- async fn on_thread_start(&self, input: ThreadStartInput<'_, Config>) {\n- let Ok(forked_from_thread_id) = ThreadId::from_string(input.thread_store.level_id()) else {\n- return;\n- };\n- input.thread_store.insert(GuardianThreadContext {\n- forked_from_thread_id,\n- });\n+ fn on_thread_start<'a>(\n+ &'a self...", + "path": "codex-rs/ext/guardian/src/lib.rs", + "status": "modified" + }, + { + "additions": 10, + "deletions": 5, + "patch_excerpt": "@@ -3,6 +3,7 @@ use std::sync::Arc;\n use codex_core::config::Config;\n use codex_extension_api::ConfigContributor;\n use codex_extension_api::ExtensionData;\n+use codex_extension_api::ExtensionFuture;\n use codex_extension_api::ExtensionRegistryBuilder;\n use codex_extension_api::ThreadLifecycleContributor;\n use codex_extension_api::ThreadStartInput;\n@@ -41,13 +42,17 @@ impl From<&Config> for ImageGenerationExtensionConfig {\n }\n }\n \n-#[async_trait::async_trait]\n impl ThreadLifecycleContributor for ImageGenerationExtension {\n /// Seeds image-generation availability when a thread begins.\n- async fn on_thread_start(&self, input: ThreadStartInput<'_, Config>) {\n- input\n- .thread_store\n- .insert(ImageGenerationExtensionConfig::from(input.config));\n+ fn on_thread_start<'a>(\n+ &'a self,\n+ input: ThreadStartInput<'a, Config>,\n+ ) -> ...", + "path": "codex-rs/ext/image-generation/src/extension.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 1, + "patch_excerpt": "@@ -14,7 +14,6 @@ doctest = false\n workspace = true\n \n [dependencies]\n-async-trait = { workspace = true }\n codex-core = { workspace = true }\n codex-extension-api = { workspace = true }\n codex-features = { workspace = true }", + "path": "codex-rs/ext/mcp/Cargo.toml", + "status": "modified" + }, + { + "additions": 18, + "deletions": 13, + "patch_excerpt": "@@ -1,4 +1,5 @@\n use codex_core::config::Config;\n+use codex_extension_api::ExtensionFuture;\n use codex_extension_api::ExtensionRegistryBuilder;\n use codex_extension_api::McpServerContribution;\n use codex_extension_api::McpServerContributor;\n@@ -7,21 +8,25 @@ use codex_mcp::hosted_plugin_runtime_mcp_server_config;\n \n struct HostedPluginRuntimeExtension;\n \n-#[async_trait::async_trait]\n impl McpServerContributor for HostedPluginRuntimeExtension {\n- async fn contribute(&self, config: &Config) -> Vec {\n- let name = CODEX_APPS_MCP_SERVER_NAME.to_string();\n- if !config.features.enabled(codex_features::Feature::Apps) {\n- return vec![McpServerContribution::Remove { name }];\n- }\n+ fn contribute<'a>(\n+ &'a self,\n+ config: &'a Config,\n+ ) -> ExtensionFuture<'a, Vec> {\n+ Box::pin(async ...", + "path": "codex-rs/ext/mcp/src/lib.rs", + "status": "modified" + }, + { + "additions": 9, + "deletions": 5, + "patch_excerpt": "@@ -150,11 +150,15 @@ fn installed_manager(config: &Config) -> McpManager {\n \n struct RemoveCodexApps;\n \n-#[async_trait::async_trait]\n impl McpServerContributor for RemoveCodexApps {\n- async fn contribute(&self, _config: &Config) -> Vec {\n- vec![McpServerContribution::Remove {\n- name: CODEX_APPS_MCP_SERVER_NAME.to_string(),\n- }]\n+ fn contribute<'a>(\n+ &'a self,\n+ _config: &'a Config,\n+ ) -> codex_extension_api::ExtensionFuture<'a, Vec> {\n+ Box::pin(async move {\n+ vec![McpServerContribution::Remove {\n+ name: CODEX_APPS_MCP_SERVER_NAME.to_string(),\n+ }]\n+ })\n }\n }", + "path": "codex-rs/ext/mcp/tests/hosted_apps_mcp.rs", + "status": "modified" + }, + { + "additions": 10, + "deletions": 5, + "patch_excerpt": "@@ -4,6 +4,7 @@ use codex_core::config::Config;\n use codex_extension_api::ConfigContributor;\n use codex_extension_api::ContextContributor;\n use codex_extension_api::ExtensionData;\n+use codex_extension_api::ExtensionFuture;\n use codex_extension_api::ExtensionRegistryBuilder;\n use codex_extension_api::PromptFragment;\n use codex_extension_api::ThreadLifecycleContributor;\n@@ -69,12 +70,16 @@ impl ContextContributor for MemoriesExtension {\n }\n }\n \n-#[async_trait::async_trait]\n impl ThreadLifecycleContributor for MemoriesExtension {\n- async fn on_thread_start(&self, input: ThreadStartInput<'_, Config>) {\n- input\n- .thread_store\n- .insert(MemoriesExtensionConfig::from_config(input.config));\n+ fn on_thread_start<'a>(\n+ &'a self,\n+ input: ThreadStartInput<'a, Config>,\n+ ) -> ExtensionFuture<'a, ()> {\n+ Box::pin(async move {\n+ ...", + "path": "codex-rs/ext/memories/src/extension.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -14,7 +14,6 @@ doctest = false\n workspace = true\n \n [dependencies]\n-async-trait = { workspace = true }\n codex-core = { workspace = true }\n codex-core-skills = { workspace = true }\n codex-exec-server = { workspace = true }\n@@ -24,5 +23,6 @@ codex-utils-absolute-path = { workspace = true }\n codex-utils-string = { workspace = true }\n \n [dev-dependencies]\n+async-trait = { workspace = true }\n pretty_assertions = { workspace = true }\n tokio = { workspace = true, features = [\"macros\", \"rt-multi-thread\"] }", + "path": "codex-rs/ext/skills/Cargo.toml", + "status": "modified" + }, + { + "additions": 109, + "deletions": 99, + "patch_excerpt": "@@ -10,6 +10,7 @@ use codex_extension_api::ContextContributor;\n use codex_extension_api::ContextualUserFragment;\n use codex_extension_api::ExtensionData;\n use codex_extension_api::ExtensionEventSink;\n+use codex_extension_api::ExtensionFuture;\n use codex_extension_api::ExtensionRegistryBuilder;\n use codex_extension_api::PromptFragment;\n use codex_extension_api::ThreadLifecycleContributor;\n@@ -44,18 +45,22 @@ struct SkillsExtension {\n event_sink: Arc,\n }\n \n-#[async_trait::async_trait]\n impl ThreadLifecycleContributor for SkillsExtension {\n- async fn on_thread_start(&self, input: ThreadStartInput<'_, Config>) {\n- let selected_roots = input\n- .thread_store\n- .get::>()\n- .map(|selected_roots| selected_roots.as_ref().clone())\n- .unwrap_or_default();\n- input.thread_store....", + "path": "codex-rs/ext/skills/src/extension.rs", + "status": "modified" + }, + { + "additions": 10, + "deletions": 5, + "patch_excerpt": "@@ -9,6 +9,7 @@ use codex_api::SearchSettings;\n use codex_core::config::Config;\n use codex_extension_api::ConfigContributor;\n use codex_extension_api::ExtensionData;\n+use codex_extension_api::ExtensionFuture;\n use codex_extension_api::ExtensionRegistryBuilder;\n use codex_extension_api::ThreadLifecycleContributor;\n use codex_extension_api::ThreadStartInput;\n@@ -80,12 +81,16 @@ fn search_settings(config: &Config, web_search_mode: WebSearchMode) -> SearchSet\n }\n }\n \n-#[async_trait::async_trait]\n impl ThreadLifecycleContributor for WebSearchExtension {\n- async fn on_thread_start(&self, input: ThreadStartInput<'_, Config>) {\n- input\n- .thread_store\n- .insert(WebSearchExtensionConfig::from(input.config));\n+ fn on_thread_start<'a>(\n+ &'a self,\n+ input: ThreadStartInput<'a, Config>,\n+ ) -> ExtensionFuture<'a, ()> {\n+ Box::pin...", + "path": "codex-rs/ext/web-search/src/extension.rs", + "status": "modified" + } + ], + "linked_issues": [], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Why\n\nExtension contributors are registered behind `dyn Trait` objects, so native `async fn`/RPITIT methods would make these traits non-object-safe. Spell out the boxed, `Send` future contract directly so `extension-api` no longer needs `async-trait` while retaining the existing runtime model.\n\n## What changed\n\n- add a shared `ExtensionFuture` alias and use it for asynchronous contributor methods\n- migrate production and test implementations to return `Box::pin(async move { ... })`\n- remove `async-trait` dependencies where they are no longer used, keeping it dev-only where unrelated test executors still require it\n\n## Behavior\n\nNo behavior change is intended. Contributor futures remain boxed, `Send`, dynamically dispatched, and lazily executed; cancellation and callback ordering stay unchanged.\n\n## Testing\n\n- `just test -p codex-extension-api` (11 passed)\n- affected extension crates (64 passed)\n- targeted `codex-core` contributor tests (14 passed)\n- `just fmt`\n- `just bazel-lock-update`\n- `just bazel-lock-check`\n\nA broad local `codex-core` run compiled successfully but encountered unrelated sandbox and missing test-binary fixture failures; CI will run the full checks.", + "labels": [], + "merged_at": "2026-06-10T12:31:09Z", + "number": 27383, + "state": "merged", + "title": "Remove async-trait from extension contributors", + "url": "https://github.com/openai/codex/pull/27383" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/impact/openai-codex-pr-27174.json b/artifacts/github/impact/openai-codex-pr-27174.json new file mode 100644 index 000000000..a070f888f --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-27174.json @@ -0,0 +1,44 @@ +{ + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-27174", + "repo": "openai/codex", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "feat: keep child MCP warnings out of parent transcript", + "url": "https://github.com/openai/codex/pull/27174", + "meta": "Merged 2026-06-10T09:45:50Z" + }, + { + "kind": "pull_request", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/27174", + "meta": "artifacts/github/reviews/openai-codex-pr-27174.review.json" + } + ] + }, + "observed_change": "Codex TUI now rejects MCP startup status notifications whose `thread_id` does not match the visible `ChatWidget` before shared notification handling can mutate the parent chat state or transcript.", + "public_signal_decision": "publish", + "control_plane_impact": "watch", + "publisher_angle": "practical_explainer", + "confidence": "confirmed", + "evidence": [ + "The upstream review records the early thread-id guard in `ChatWidget::handle_server_notification`.", + "TUI tests cover child `Starting` and `Failed` MCP notifications delivered to a retrying parent widget.", + "The tests assert parent retry status is preserved and no parent history or MCP status mutation is produced.", + "No app-server schema or MCP payload shape changed in the source bundle." + ], + "candidate_followups": [ + "Keep this as watch evidence for any Decodex UI that displays nested-agent MCP status notifications.", + "Audit parent/child notification routing before showing MCP startup warnings in Control Plane surfaces." + ], + "social_notes": [ + "Public copy can describe cleaner parent transcripts for subagent MCP failures.", + "Do not imply a protocol-schema change; this is a TUI routing guard." + ], + "caveats": [ + "The fix depends on tagged notifications with thread ids.", + "The change narrows TUI handling and does not alter MCP startup semantics." + ] +} diff --git a/artifacts/github/impact/openai-codex-pr-27198.json b/artifacts/github/impact/openai-codex-pr-27198.json new file mode 100644 index 000000000..126e56752 --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-27198.json @@ -0,0 +1,47 @@ +{ + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-27198", + "repo": "openai/codex", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Use plugin-service MCP as the hosted plugin runtime", + "url": "https://github.com/openai/codex/pull/27198", + "meta": "Merged 2026-06-10T10:54:21Z" + }, + { + "kind": "pull_request", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/27198", + "meta": "artifacts/github/reviews/openai-codex-pr-27198.review.json" + } + ] + }, + "observed_change": "Codex now resolves the hosted `codex_apps` MCP runtime through an extension-owned `/ps/mcp` plugin-service route, removes the temporary `apps_mcp_path_override` plumbing, and exposes hosted skills as MCP resources on the same streamable HTTP connection as connector tools.", + "public_signal_decision": "publish", + "control_plane_impact": "compat_risk", + "publisher_angle": "operator_impact", + "confidence": "confirmed", + "evidence": [ + "The upstream review records the route change from `/api/codex/apps` to `/api/codex/ps/mcp` in app-server MCP fixtures.", + "`McpConfig` no longer carries `apps_mcp_path_override`, and removed compatibility forms are ignored during effective feature resolution.", + "The MCP extension contributes the reserved `codex_apps` server through `hosted_plugin_runtime_mcp_server_config`.", + "Hosted skills are exercised through MCP resource list/read paths while connector actions remain MCP tools." + ], + "candidate_followups": [ + "Audit Decodex hosted-plugin probes and fixtures for legacy `/apps` endpoint or path-override assumptions.", + "Prefer MCP resource list/read paths for backend-provided hosted skills when evaluating hosted plugin support.", + "Retain fallback expectations for hosts that do not install the MCP extension and for non-ChatGPT auth states that remove `codex_apps`." + ], + "social_notes": [ + "Public copy should frame this as a hosted plugin runtime routing and architecture change, not as a new backend service launch.", + "Mention that `apps_mcp_path_override` is removed from effective config, while old forms deserialize only for compatibility.", + "Avoid claiming thread-local backend plugin selection; the PR explicitly leaves backend plugin enablement account/workspace-owned." + ], + "caveats": [ + "Backend plugin enablement remains owned by hosted account or workspace state.", + "Hosts without the MCP extension retain the legacy Apps endpoint behavior.", + "The removed path override is accepted only as ignored compatibility input." + ] +} diff --git a/artifacts/github/impact/openai-codex-pr-27383.json b/artifacts/github/impact/openai-codex-pr-27383.json new file mode 100644 index 000000000..ff2a667ac --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-27383.json @@ -0,0 +1,44 @@ +{ + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-27383", + "repo": "openai/codex", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Remove async-trait from extension contributors", + "url": "https://github.com/openai/codex/pull/27383", + "meta": "Merged 2026-06-10T12:31:09Z" + }, + { + "kind": "pull_request", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/27383", + "meta": "artifacts/github/reviews/openai-codex-pr-27383.review.json" + } + ] + }, + "observed_change": "Codex extension contributors now return an explicit boxed `ExtensionFuture` from async contributor methods instead of relying on `async-trait`, while preserving boxed, `Send`, dynamically dispatched future behavior.", + "public_signal_decision": "defer", + "control_plane_impact": "compat_risk", + "publisher_angle": "watch_note", + "confidence": "confirmed", + "evidence": [ + "The upstream review records the new `ExtensionFuture<'a, T>` alias and contributor trait signature rewrites.", + "Production extension crates were updated to return `Box::pin(async move { ... })` instead of using `async-trait`.", + "`async-trait` was removed from direct extension API, guardian, and MCP extension dependencies.", + "The PR states no runtime behavior change is intended, so the primary risk is source compatibility for implementors." + ], + "candidate_followups": [ + "Audit Decodex extension API prototypes for `async-trait` contributor implementations.", + "Treat `ExtensionFuture` as the upstream object-safe async contributor contract in future plugin or extension experiments.", + "Do not turn this into public user-facing content unless paired with a broader extension API explainer." + ], + "social_notes": [ + "If used publicly later, keep it as a cautious extension-author watch note and avoid implying end-user behavior changed." + ], + "caveats": [ + "The source bundle supports a compile-time API shape change, not a runtime behavior change.", + "Out-of-tree contributor implementations need their own compile validation against the new trait signatures." + ] +} diff --git a/artifacts/github/review-queue/openai-codex-latest.json b/artifacts/github/review-queue/openai-codex-latest.json index 591c220d8..8e44dcd57 100644 --- a/artifacts/github/review-queue/openai-codex-latest.json +++ b/artifacts/github/review-queue/openai-codex-latest.json @@ -8,7 +8,7 @@ "recent_commits_scanned": 40, "subjects_queued": 40 }, - "generated_at": "2026-06-10T08:04:41.120407Z", + "generated_at": "2026-06-10T14:13:34.686999Z", "repo": "openai/codex", "schema": "upstream_review_queue/v1", "source": { @@ -17,84 +17,6 @@ "signals_dir": "site/src/content/signals" }, "subjects": [ - { - "attention_flags": [ - "deprecated_removed", - "new_feature", - "protocol_change", - "security_policy" - ], - "changed_file_count": 2, - "commit_shas": [ - "c84c6bbd105eeef517ee1411a75fc7f06f2a9515", - "aef829d185388be2dfa51b4d66be78836176ab92", - "65c5b125134e8afa0c7dd86014d95c490e791671" - ], - "committed_at": "2026-06-09T00:03:52Z", - "next_step": "ai_review_required", - "pr_number": 27068, - "pr_url": "https://github.com/openai/codex/pull/27068", - "review_priority": "critical", - "review_reason": "Needs AI review for deprecated_removed, new_feature, protocol_change, security_policy.", - "sample_paths": [ - "codex-rs/tui/src/debug_config.rs", - "codex-rs/tui/src/snapshots/codex_tui__debug_config__tests__debug_config_effective_sandbox_modes_with_deny_read.snap" - ], - "source_state": "merged", - "subject_id": "27068", - "subject_kind": "pr", - "surface_hints": [ - "cli_tui", - "config_hooks", - "sandbox_permissions", - "tests_ci" - ], - "title": "Show effective sandbox modes in /debug-config", - "url": "https://github.com/openai/codex/pull/27068" - }, - { - "attention_flags": [ - "breaking_change", - "deprecated_removed", - "new_feature", - "protocol_change", - "security_policy" - ], - "changed_file_count": 6, - "commit_shas": [ - "074c237d6b2f061e02273af5b8ba2d7869020e10", - "eb22256674028348ce7cb5ff6ed3b5365c11091e", - "dfcc2221c8b46a2390a982d48ef1426aadcc4a2b", - "c25d9987b95c0f55aeac3fc963a4d4087bf173d2", - "666fae92fc838030f95254738138e047298f718e", - "95e0f15865134edec8b6cc97f9983631b2607d0f", - "12556519e8796dc575eedbac190b42e779e608e2" - ], - "committed_at": "2026-06-09T01:08:48Z", - "next_step": "ai_review_required", - "pr_number": 25177, - "pr_url": "https://github.com/openai/codex/pull/25177", - "review_priority": "critical", - "review_reason": "Needs AI review for breaking_change, deprecated_removed, new_feature, protocol_change, security_policy.", - "sample_paths": [ - "codex-rs/tui/src/app.rs", - "codex-rs/tui/src/app/config_persistence.rs", - "codex-rs/tui/src/app/test_support.rs", - "codex-rs/tui/src/app/tests.rs", - "codex-rs/tui/src/external_agent_config_migration_startup.rs", - "codex-rs/tui/src/lib.rs" - ], - "source_state": "merged", - "subject_id": "25177", - "subject_kind": "pr", - "surface_hints": [ - "cli_tui", - "config_hooks", - "tests_ci" - ], - "title": "Preserve cloud requirements across TUI thread resets", - "url": "https://github.com/openai/codex/pull/25177" - }, { "attention_flags": [ "auth_account", @@ -835,45 +757,99 @@ }, { "attention_flags": [ + "auth_account", + "breaking_change", + "deprecated_removed", "new_feature", "protocol_change", "release_packaging", "security_policy" ], - "changed_file_count": 18, + "changed_file_count": 27, "commit_shas": [ - "211daa85d0efa0aa53a49533845a323177f81168" + "c86af903969cdfa1fbeba67fbd454710f0d53de2", + "fa313ba390f5372cd7dfa2e2a05b890f93b9b3a1", + "19a14a08106a029b63c08e0a930c2c3bd769183b", + "e7ea9109a9e343fd1cc6a62d4c50d2d7b9cf1660" ], - "committed_at": "2026-06-09T00:38:35Z", + "committed_at": "2026-06-10T10:54:21Z", "next_step": "ai_review_required", - "pr_number": 27092, - "pr_url": "https://github.com/openai/codex/pull/27092", - "review_priority": "high", - "review_reason": "Needs AI review for new_feature, protocol_change, release_packaging, security_policy.", + "pr_number": 27198, + "pr_url": "https://github.com/openai/codex/pull/27198", + "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/app-server/src/bespoke_event_handling.rs", - "codex-rs/app-server/src/request_processors/external_agent_session_import.rs", - "codex-rs/app-server/src/request_processors/thread_processor_tests.rs", - "codex-rs/app-server/tests/suite/conversation_summary.rs", - "codex-rs/app-server/tests/suite/v2/thread_read.rs", - "codex-rs/app-server/tests/suite/v2/thread_unarchive.rs", - "codex-rs/core-api/src/lib.rs", - "codex-rs/core/src/config/mod.rs", - "codex-rs/core/src/realtime_context_tests.rs", - "codex-rs/core/src/session/session.rs", - "codex-rs/core/src/session/tests.rs", - "codex-rs/thread-manager-sample/src/main.rs" + "codex-rs/app-server/tests/suite/v2/app_list.rs", + "codex-rs/app-server/tests/suite/v2/mcp_resource.rs", + "codex-rs/app-server/tests/suite/v2/mcp_server_elicitation.rs", + "codex-rs/app-server/tests/suite/v2/plugin_install.rs", + "codex-rs/app-server/tests/suite/v2/plugin_read.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/config/src/schema.rs", + "codex-rs/core/config.schema.json", + "codex-rs/core/src/config/config_tests.rs", + "codex-rs/core/src/config/mod.rs" ], "source_state": "merged", - "subject_id": "27092", + "subject_id": "27198", "subject_kind": "pr", "surface_hints": [ "app_server_protocol", "config_hooks", + "mcp_plugins", + "release_packaging", "tests_ci" ], - "title": "Add extra config to StoredThread, leave empty for now", - "url": "https://github.com/openai/codex/pull/27092" + "title": "Use plugin-service MCP as the hosted plugin runtime", + "url": "https://github.com/openai/codex/pull/27198" + }, + { + "attention_flags": [ + "auth_account", + "breaking_change", + "deprecated_removed", + "new_feature", + "protocol_change", + "release_packaging", + "security_policy" + ], + "changed_file_count": 21, + "commit_shas": [ + "9c223af9d01e5e895d668d29291c45b794eeda83", + "6b0115c1838acfe99839e07a64bcaffbf9bebeb4" + ], + "committed_at": "2026-06-10T12:31:09Z", + "next_step": "ai_review_required", + "pr_number": 27383, + "pr_url": "https://github.com/openai/codex/pull/27383", + "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/Cargo.lock", + "codex-rs/core/src/session/tests.rs", + "codex-rs/core/src/session/turn_tests.rs", + "codex-rs/core/src/stream_events_utils_tests.rs", + "codex-rs/core/src/thread_manager_tests.rs", + "codex-rs/core/src/tools/handlers/extension_tools.rs", + "codex-rs/ext/extension-api/Cargo.toml", + "codex-rs/ext/extension-api/src/contributors.rs", + "codex-rs/ext/extension-api/src/lib.rs", + "codex-rs/ext/extension-api/tests/registry.rs", + "codex-rs/ext/goal/src/extension.rs", + "codex-rs/ext/guardian/Cargo.toml" + ], + "source_state": "merged", + "subject_id": "27383", + "subject_kind": "pr", + "surface_hints": [ + "config_hooks", + "mcp_plugins", + "tests_ci" + ], + "title": "Remove async-trait from extension contributors", + "url": "https://github.com/openai/codex/pull/27383" }, { "attention_flags": [ @@ -1264,29 +1240,36 @@ }, { "attention_flags": [ + "new_feature", "protocol_change" ], - "changed_file_count": 1, + "changed_file_count": 2, "commit_shas": [ - "042d3fa9debbd5f93f485016e988eeb3d8b5f092" + "ec27d8d71b40bd86900682e989e3ea64bcd72f79", + "1bfa6cfe558427f95780c9a1ea2c6a00257b8235", + "ffedf368162b5ea8b085b5619505a18980a734d8" ], - "committed_at": "2026-06-09T00:55:00Z", + "committed_at": "2026-06-10T09:45:49Z", "next_step": "ai_review_required", - "pr_number": 27096, - "pr_url": "https://github.com/openai/codex/pull/27096", - "review_priority": "normal", - "review_reason": "Needs AI review for protocol_change.", + "pr_number": 27174, + "pr_url": "https://github.com/openai/codex/pull/27174", + "review_priority": "high", + "review_reason": "Needs AI review for new_feature, protocol_change.", "sample_paths": [ - "codex-rs/ext/web-search/web_run_description.md" + "codex-rs/tui/src/chatwidget/protocol.rs", + "codex-rs/tui/src/chatwidget/tests/mcp_startup.rs" ], "source_state": "merged", - "subject_id": "27096", + "subject_id": "27174", "subject_kind": "pr", "surface_hints": [ - "internal_churn" + "app_server_protocol", + "cli_tui", + "mcp_plugins", + "tests_ci" ], - "title": "Update web search citation prompt", - "url": "https://github.com/openai/codex/pull/27096" + "title": "feat: keep child MCP warnings out of parent transcript", + "url": "https://github.com/openai/codex/pull/27174" }, { "attention_flags": [ @@ -1566,6 +1549,33 @@ ], "title": "Add spans to build_tool_router", "url": "https://github.com/openai/codex/pull/27094" + }, + { + "attention_flags": [ + "release_packaging" + ], + "changed_file_count": 2, + "commit_shas": [ + "90fc87dd1a1c7449323fcff804c77abfea377a23" + ], + "committed_at": "2026-06-10T11:06:48Z", + "next_step": "ai_review_required", + "pr_number": 27375, + "pr_url": "https://github.com/openai/codex/pull/27375", + "review_priority": "normal", + "review_reason": "Needs AI review for release_packaging.", + "sample_paths": [ + "codex-rs/core/src/tools/handlers/multi_agents/spawn.rs", + "codex-rs/core/src/tools/handlers/multi_agents_v2/spawn.rs" + ], + "source_state": "merged", + "subject_id": "27375", + "subject_kind": "pr", + "surface_hints": [ + "internal_churn" + ], + "title": "[codex] Tag multi-agent spawn metrics with version", + "url": "https://github.com/openai/codex/pull/27375" } ] } diff --git a/artifacts/github/reviews/openai-codex-pr-27174.review.json b/artifacts/github/reviews/openai-codex-pr-27174.review.json new file mode 100644 index 000000000..40531c040 --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27174.review.json @@ -0,0 +1,66 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27174", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "27174", + "commit_shas": [ + "ec27d8d71b40bd86900682e989e3ea64bcd72f79", + "1bfa6cfe558427f95780c9a1ea2c6a00257b8235", + "ffedf368162b5ea8b085b5619505a18980a734d8" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "feat: keep child MCP warnings out of parent transcript", + "url": "https://github.com/openai/codex/pull/27174", + "meta": "Merged 2026-06-10T09:45:50Z" + }, + { + "kind": "commit", + "title": "fix(tui): keep child MCP warnings out of parent", + "url": "https://github.com/openai/codex/commit/ec27d8d71b40bd86900682e989e3ea64bcd72f79" + }, + { + "kind": "commit", + "title": "fix(tui): reject child MCP updates before status restore", + "url": "https://github.com/openai/codex/commit/1bfa6cfe558427f95780c9a1ea2c6a00257b8235" + } + ] + }, + "reviewed_at": "2026-06-10T14:15:22Z", + "observed_change": "Codex TUI now rejects MCP startup status notifications whose `thread_id` does not match the visible `ChatWidget` before shared notification handling can mutate the parent chat state or transcript.", + "changed_surfaces": [ + "TUI ChatWidget server-notification routing", + "MCP startup status handling for parent and child threads", + "TUI regression tests for retrying parent chats receiving child MCP status updates" + ], + "user_visible_path": "When a subagent has an MCP startup failure, the parent Codex TUI transcript should keep its own retry state and should not show duplicate or child-scoped MCP warning history.", + "control_plane_relevance": "Watch-level relevance for Decodex Control Plane: any UI or app-server client that multiplexes parent and child thread notifications should preserve the thread-id guard before mutating visible parent state.", + "compatibility_risk": "Low; this is a TUI-side filtering fix for tagged MCP status notifications and does not change the app-server schema or MCP startup payload shape.", + "adoption_opportunity": "Audit Decodex notification consumers for the same parent/child status-routing assumption if they display MCP startup warnings across nested agent threads.", + "community_value": "Medium for Codex multi-agent and MCP users because it removes confusing parent-transcript noise from child MCP failures.", + "deprecated_or_breaking_notes": "No deprecated, removed, migration, or breaking behavior was found; the change narrows misrouted status handling.", + "confidence": "confirmed", + "evidence": [ + "PR #27174 states that tagged child MCP status notifications could mutate a retrying parent widget and that the fix validates the notification against the visible `ChatWidget` thread before shared handling.", + "`codex-rs/tui/src/chatwidget/protocol.rs` returns early for `McpServerStatusUpdated` notifications when the notification `thread_id` differs from the visible widget thread id.", + "`codex-rs/tui/src/chatwidget/tests/mcp_startup.rs` adds coverage for child `Starting` and `Failed` notifications delivered to a retrying parent widget.", + "The tests assert parent retry status and status headers are preserved while no parent history or MCP status mutation is produced.", + "The normalized bundle `artifacts/github/bundles/openai-codex-pr-27174.json` records the TUI protocol and MCP startup test changes for the merged PR." + ], + "caveats": "The fix depends on tagged status notifications carrying thread ids; the source bundle does not show a protocol-schema change.", + "next_actions": [ + { + "type": "upstream_impact", + "reason": "The thread-scoped notification guard is relevant to Control Plane UI consumers that display nested-agent MCP status." + }, + { + "type": "social_candidate", + "reason": "The parent-transcript MCP warning fix has a clear user-facing multi-agent/MCP angle." + } + ] +} diff --git a/artifacts/github/reviews/openai-codex-pr-27198.review.json b/artifacts/github/reviews/openai-codex-pr-27198.review.json new file mode 100644 index 000000000..a66b8f942 --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27198.review.json @@ -0,0 +1,76 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27198", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "27198", + "commit_shas": [ + "c86af903969cdfa1fbeba67fbd454710f0d53de2", + "fa313ba390f5372cd7dfa2e2a05b890f93b9b3a1", + "19a14a08106a029b63c08e0a930c2c3bd769183b", + "e7ea9109a9e343fd1cc6a62d4c50d2d7b9cf1660" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Use plugin-service MCP as the hosted plugin runtime", + "url": "https://github.com/openai/codex/pull/27198", + "meta": "Merged 2026-06-10T10:54:21Z" + }, + { + "kind": "commit", + "title": "Use plugin-service MCP as hosted plugin runtime", + "url": "https://github.com/openai/codex/commit/c86af903969cdfa1fbeba67fbd454710f0d53de2" + }, + { + "kind": "commit", + "title": "Regenerate config schema for removed Apps flag", + "url": "https://github.com/openai/codex/commit/19a14a08106a029b63c08e0a930c2c3bd769183b" + } + ] + }, + "reviewed_at": "2026-06-10T14:15:22Z", + "observed_change": "Codex now resolves the hosted `codex_apps` MCP runtime through an extension-owned `/ps/mcp` plugin-service route, removes the temporary `apps_mcp_path_override` plumbing, and exposes hosted skills as MCP resources on the same streamable HTTP connection as connector tools.", + "changed_surfaces": [ + "hosted plugin MCP runtime route selection", + "MCP extension contribution ordering and `codex_apps` auth gating", + "app-server plugin install, read, resource, and elicitation tests", + "config feature schema and config-lock compatibility for removed `apps_mcp_path_override`", + "MCP resource list/read response truncation" + ], + "user_visible_path": "App-server clients with ChatGPT auth now reach the hosted plugin runtime through `/backend-api/ps/mcp` or the custom-base equivalent, can see hosted skills through MCP resource list/read tools, and no longer get a configurable `apps_mcp_path_override` path.", + "control_plane_relevance": "High for Decodex Control Plane because hosted plugin capabilities move behind the extension-owned MCP runtime and because app-server plugin and resource flows now exercise `/ps/mcp` instead of the legacy Apps endpoint.", + "compatibility_risk": "High for adapters or local configs that assumed `apps_mcp_path_override` selected the hosted Apps route; older boolean or structured config still deserializes as ignored compatibility input, but new effective runtime selection comes from extension contribution ordering and ChatGPT auth gating.", + "adoption_opportunity": "Align Decodex hosted-plugin probes with the plugin-service `/ps/mcp` runtime, consume hosted skills through MCP resources, and preserve fallback behavior for hosts that do not install the MCP extension.", + "community_value": "High for Codex plugin and app-server operators because it makes hosted connector tools and backend-provided skills share the MCP subsystem instead of requiring a second hosted-skill client or orchestrator filesystem download.", + "deprecated_or_breaking_notes": "`apps_mcp_path_override` is removed from effective config and schema output; compatibility deserialization and config-lock normalization keep older locks replayable, but the override no longer controls the hosted runtime path.", + "confidence": "confirmed", + "evidence": [ + "PR #27198 states the MCP extension now contributes `codex_apps` as the hosted plugin runtime at `/ps/mcp`, with connector actions as MCP tools and hosted skills as MCP resources.", + "The app-server test fixtures in `app_list.rs`, `mcp_resource.rs`, `mcp_server_elicitation.rs`, `plugin_install.rs`, and `plugin_read.rs change the hosted route from `/api/codex/apps` to `/api/codex/ps/mcp`.", + "`codex-rs/codex-mcp/src/mcp/mod.rs` removes `apps_mcp_path_override` from `McpConfig` while retaining product-SKU, ChatGPT auth, and app enablement inputs.", + "`codex-rs/ext/mcp/src/lib.rs` renames the hosted extension path around `hosted_plugin_runtime_mcp_server_config` and contributes the reserved `codex_apps` server through the MCP extension.", + "`codex-rs/core/config.schema.json` and `codex-rs/features/src/feature_configs.rs` replace the path override feature with removed compatibility shapes that deserialize but do not produce effective feature entries.", + "`codex-rs/core/src/config_lock.rs` and `codex-rs/core/src/session/config_lock.rs` normalize removed compatibility entries so older config locks remain comparable.", + "`codex-rs/core/src/tools/handlers/mcp_resource.rs` passes the turn truncation policy into MCP resource serialization and adds tests for capped resource-read payloads.", + "The normalized bundle `artifacts/github/bundles/openai-codex-pr-27198.json` records 27 changed files for the merged PR." + ], + "caveats": "The source review confirms Codex client and app-server behavior; backend plugin enablement remains account or workspace state owned by the hosted endpoint, and the PR explicitly does not add thread-local backend plugin selection.", + "next_actions": [ + { + "type": "upstream_impact", + "reason": "The hosted plugin runtime route and removed config override create direct Control Plane compatibility and adoption work." + }, + { + "type": "social_candidate", + "reason": "The hosted plugin-service MCP runtime has a clear public operator angle." + }, + { + "type": "linear_followup", + "reason": "Decodex should audit hosted-plugin probes, MCP resource reads, and legacy Apps endpoint assumptions against `/ps/mcp`." + } + ] +} diff --git a/artifacts/github/reviews/openai-codex-pr-27375.review.json b/artifacts/github/reviews/openai-codex-pr-27375.review.json new file mode 100644 index 000000000..304335e47 --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27375.review.json @@ -0,0 +1,53 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27375", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "27375", + "commit_shas": [ + "90fc87dd1a1c7449323fcff804c77abfea377a23" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "[codex] Tag multi-agent spawn metrics with version", + "url": "https://github.com/openai/codex/pull/27375", + "meta": "Merged 2026-06-10T11:06:49Z" + }, + { + "kind": "commit", + "title": "multi-agent: tag spawn metric with version", + "url": "https://github.com/openai/codex/commit/90fc87dd1a1c7449323fcff804c77abfea377a23" + } + ] + }, + "reviewed_at": "2026-06-10T14:15:22Z", + "observed_change": "Codex multi-agent spawn telemetry now tags the shared `codex.multi_agent.spawn` counter with `version=v1` for the legacy path and `version=v2` for the multi-agent v2 path.", + "changed_surfaces": [ + "legacy multi-agent spawn telemetry", + "multi-agent v2 spawn telemetry" + ], + "user_visible_path": "None; this PR changes analytics dimensions only and does not alter the user-facing spawn-agent behavior.", + "control_plane_relevance": "Watch-only relevance for Decodex analytics because the version tag can separate legacy and v2 multi-agent adoption if Decodex later consumes upstream telemetry semantics.", + "compatibility_risk": "None identified for app-server, plugin, MCP, sandbox, config, or user workflows; existing metric consumers should tolerate an added tag dimension.", + "adoption_opportunity": "Use the `version` dimension only if Decodex builds analytics or release-rollup reasoning around upstream multi-agent v1 versus v2 adoption.", + "community_value": "Low as a standalone public signal because the change improves internal telemetry segmentation rather than user-visible functionality.", + "deprecated_or_breaking_notes": "No deprecated, removed, migration, or breaking behavior was found.", + "confidence": "confirmed", + "evidence": [ + "PR #27375 says the shared `codex.multi_agent.spawn` metric is emitted by both runtimes and adds a bounded version tag to distinguish v2 adoption from aggregate spawning.", + "`codex-rs/core/src/tools/handlers/multi_agents/spawn.rs` adds `(version, v1)` to the legacy spawn counter labels.", + "`codex-rs/core/src/tools/handlers/multi_agents_v2/spawn.rs` adds `(version, v2)` to the v2 spawn counter labels.", + "The normalized bundle `artifacts/github/bundles/openai-codex-pr-27375.json` records only the two telemetry call-site changes." + ], + "caveats": "The PR body states tests and Clippy were left to CI; the source evidence is still sufficient for the analytics-only classification.", + "next_actions": [ + { + "type": "none", + "reason": "Review trace is enough; no impact, signal, social, or Linear follow-up is warranted from an analytics tag-only change." + } + ] +} diff --git a/artifacts/github/reviews/openai-codex-pr-27383.review.json b/artifacts/github/reviews/openai-codex-pr-27383.review.json new file mode 100644 index 000000000..4dc039695 --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27383.review.json @@ -0,0 +1,62 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27383", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "27383", + "commit_shas": [ + "9c223af9d01e5e895d668d29291c45b794eeda83", + "6b0115c1838acfe99839e07a64bcaffbf9bebeb4" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Remove async-trait from extension contributors", + "url": "https://github.com/openai/codex/pull/27383", + "meta": "Merged 2026-06-10T12:31:09Z" + }, + { + "kind": "commit", + "title": "Remove async-trait from extension contributors", + "url": "https://github.com/openai/codex/commit/9c223af9d01e5e895d668d29291c45b794eeda83" + } + ] + }, + "reviewed_at": "2026-06-10T14:15:22Z", + "observed_change": "Codex extension contributors now return an explicit boxed `ExtensionFuture` from async contributor methods instead of relying on `async-trait`, while preserving boxed, `Send`, dynamically dispatched future behavior.", + "changed_surfaces": [ + "extension API contributor trait signatures", + "goal, guardian, image-generation, MCP, memories, skills, and web-search extension implementations", + "extension API test implementations", + "Cargo dependency metadata for extension crates" + ], + "user_visible_path": "Normal Codex CLI, TUI, and app-server users should not see behavior change, but out-of-tree extension implementors must match the explicit `ExtensionFuture` return contract when implementing contributor traits.", + "control_plane_relevance": "Moderate for Decodex Control Plane because Decodex extension or plugin experiments that compile against upstream contributor traits need to track the new boxed-future API shape.", + "compatibility_risk": "Source compatibility risk for custom extension contributors written against the prior `async fn` or `async-trait` trait style; the PR states no runtime behavior change is intended.", + "adoption_opportunity": "Update any Decodex extension-contributor prototypes to use `ExtensionFuture` and `Box::pin(async move { ... })`, and treat this as the new object-safe contributor contract.", + "community_value": "Medium for extension authors because it documents the object-safe async boundary directly, but low for ordinary Codex users because behavior is intended to remain unchanged.", + "deprecated_or_breaking_notes": "`async-trait` is removed from `codex-extension-api`, `codex-guardian`, and `codex-mcp-extension` dependencies, and contributor trait method signatures now spell out the boxed future type.", + "confidence": "confirmed", + "evidence": [ + "PR #27383 says extension contributors are registered behind `dyn Trait` objects and replaces `async-trait` with an explicit boxed, `Send` future contract while retaining the existing runtime model.", + "`codex-rs/ext/extension-api/src/contributors.rs` adds `pub type ExtensionFuture<'a, T> = Pin + Send + 'a>>` and rewrites contributor traits to return it.", + "`codex-rs/ext/extension-api/src/lib.rs` re-exports `ExtensionFuture` for downstream implementors.", + "Production extensions under `codex-rs/ext/goal`, `ext/guardian`, `ext/image-generation`, `ext/mcp`, `ext/memories`, `ext/skills`, and `ext/web-search` now return `Box::pin(async move { ... })` from contributor methods.", + "`codex-rs/ext/extension-api/Cargo.toml`, `ext/guardian/Cargo.toml`, and `ext/mcp/Cargo.toml` remove direct `async-trait` dependencies.", + "The PR body reports targeted extension API, affected extension crate, and `codex-core` contributor tests, and the bundle records test rewrites that keep callback behavior covered." + ], + "caveats": "The reviewed source supports a compile-time API shape change, not a user-facing runtime behavior change; downstream extension authors still need to verify their own trait implementations.", + "next_actions": [ + { + "type": "upstream_impact", + "reason": "The contributor trait signature change is a source compatibility risk for Decodex extension or plugin experiments." + }, + { + "type": "linear_followup", + "reason": "Audit any Decodex-owned upstream extension API prototypes for `async-trait` contributor implementations." + } + ] +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-27174.json b/artifacts/github/social-candidates/openai-codex-pr-27174.json new file mode 100644 index 000000000..961e828da --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-27174.json @@ -0,0 +1,54 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-27174", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "practical_explainer", + "priority": "high", + "audience": "Codex multi-agent and MCP users", + "candidate_text": [ + "Codex TUI now drops child-thread MCP startup status updates before they can mutate a parent chat, so subagent MCP failures stay out of the parent transcript. PR: https://github.com/openai/codex/pull/27174" + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-27174.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-27174.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/27174" + ] + }, + "evidence_notes": [ + "The TUI now checks MCP status notification `thread_id` against the visible `ChatWidget` thread before shared handling.", + "Tests cover child `Starting` and `Failed` MCP status notifications delivered to a retrying parent widget.", + "The tests preserve the parent retry status and avoid parent history or MCP status mutation.", + "No app-server schema or MCP payload shape changed in this PR." + ], + "claims": [ + { + "text": "Child-thread MCP startup status updates are rejected before mutating the parent chat state.", + "evidence": "artifacts/github/reviews/openai-codex-pr-27174.review.json", + "confidence": "confirmed" + }, + { + "text": "Subagent MCP failures should no longer appear as duplicate parent-transcript warnings in the TUI.", + "evidence": "artifacts/github/impact/openai-codex-pr-27174.json", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "publish", + "reason": "The PR has a concrete user-visible multi-agent/MCP transcript cleanup path.", + "idempotency_key": "x:decodexspace:openai-codex-pr-27174:practical_explainer" + }, + "caveats": [ + "This is a TUI routing guard, not a protocol-schema change.", + "The behavior depends on tagged MCP status notifications carrying thread ids." + ], + "next_steps": [ + "Let Publisher automation decide whether to reserve or post this candidate." + ] +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-27198.json b/artifacts/github/social-candidates/openai-codex-pr-27198.json new file mode 100644 index 000000000..d171be6f6 --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-27198.json @@ -0,0 +1,56 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-27198", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "operator_impact", + "priority": "critical", + "audience": "Codex app-server, hosted plugin, and MCP operators", + "candidate_text": [ + "Codex now routes hosted plugin capabilities through the extension-owned `/ps/mcp` runtime: connector actions stay MCP tools, hosted skills become MCP resources, and `apps_mcp_path_override` is removed. PR: https://github.com/openai/codex/pull/27198" + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-27198.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-27198.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/27198" + ] + }, + "evidence_notes": [ + "The MCP extension now contributes `codex_apps` as the hosted plugin runtime at `/ps/mcp`.", + "App-server fixtures moved hosted MCP route coverage from `/api/codex/apps` to `/api/codex/ps/mcp`.", + "`apps_mcp_path_override` is removed from effective config and retained only as ignored compatibility input.", + "Hosted skills use MCP resource list/read paths while connector actions remain MCP tools." + ], + "claims": [ + { + "text": "Hosted plugin capabilities now route through the extension-owned `/ps/mcp` MCP runtime.", + "evidence": "artifacts/github/reviews/openai-codex-pr-27198.review.json", + "confidence": "confirmed" + }, + { + "text": "`apps_mcp_path_override` no longer controls effective hosted Apps MCP routing.", + "evidence": "artifacts/github/impact/openai-codex-pr-27198.json", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "publish", + "reason": "The PR changes hosted plugin runtime routing and config compatibility in a way Codex operators can act on.", + "idempotency_key": "x:decodexspace:openai-codex-pr-27198:operator_impact" + }, + "caveats": [ + "Backend plugin enablement remains account or workspace state.", + "Hosts without the MCP extension retain legacy behavior.", + "The PR does not add thread-local backend plugin selection." + ], + "next_steps": [ + "Let Publisher automation decide whether to reserve or post this candidate.", + "Create a Decodex follow-up if hosted plugin probes or config assumptions still expect the legacy Apps endpoint." + ] +}