From eed032348e8b2aaab6b1adb33e33624653f081f8 Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Tue, 12 May 2026 09:47:58 +0800 Subject: [PATCH 1/2] {"schema":"decodex/commit/1","summary":"Make Decodex probe threads ephemeral","authority":"manual"} --- apps/decodex/src/agent/app_server.rs | 3 +++ apps/decodex/src/agent/app_server/tests.rs | 15 +++++++++++++++ apps/decodex/src/orchestrator/execution.rs | 1 + docs/spec/app-server.md | 5 ++++- 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/apps/decodex/src/agent/app_server.rs b/apps/decodex/src/agent/app_server.rs index 757016e..a9d5e49 100644 --- a/apps/decodex/src/agent/app_server.rs +++ b/apps/decodex/src/agent/app_server.rs @@ -308,6 +308,7 @@ pub(crate) struct AppServerRunRequest<'a> { pub(crate) continuation_user_input: Option, pub(crate) activity_marker_path: Option, pub(crate) resume_thread_id: Option, + pub(crate) ephemeral_thread: bool, pub(crate) command_exec_health_check: Option, pub(crate) dynamic_tool_handler: Option<&'a dyn DynamicToolHandler>, pub(crate) continuation_guard: Option<&'a dyn TurnContinuationGuard>, @@ -924,6 +925,7 @@ pub(crate) fn probe_app_server(listen: &str) -> crate::prelude::Result() -> super::AppServerRunRequest<'a> { continuation_user_input: None, activity_marker_path: None, resume_thread_id: None, + ephemeral_thread: false, command_exec_health_check: None, dynamic_tool_handler: None, continuation_guard: None, @@ -366,6 +368,17 @@ fn minimal_run_request<'a>() -> super::AppServerRunRequest<'a> { } } +#[test] +fn synthetic_probe_thread_start_is_ephemeral_when_requested() { + let mut request = minimal_run_request(); + request.ephemeral_thread = true; + + let start = super::build_thread_start_request(&request).expect("request should build"); + let value = serde_json::to_value(&start).expect("thread start request should serialize"); + + assert_eq!(value["ephemeral"], true); +} + #[test] fn command_exec_health_check_uses_bounded_standalone_request() { let health_check = CommandExecHealthCheck { @@ -1258,6 +1271,7 @@ fn live_app_server_resume_round_trip_updates_marker_and_state() { )), activity_marker_path: Some(marker_path.clone()), resume_thread_id: None, + ephemeral_thread: false, command_exec_health_check: None, dynamic_tool_handler: Some(&handler), continuation_guard: Some(&guard), @@ -1301,6 +1315,7 @@ fn live_app_server_resume_round_trip_updates_marker_and_state() { continuation_user_input: None, activity_marker_path: Some(marker_path.clone()), resume_thread_id: Some(first_result.thread_id.clone()), + ephemeral_thread: false, command_exec_health_check: None, dynamic_tool_handler: Some(&handler), continuation_guard: None, diff --git a/apps/decodex/src/orchestrator/execution.rs b/apps/decodex/src/orchestrator/execution.rs index 8c66062..a318944 100644 --- a/apps/decodex/src/orchestrator/execution.rs +++ b/apps/decodex/src/orchestrator/execution.rs @@ -452,6 +452,7 @@ where )), activity_marker_path: Some(issue_run.worktree.path.clone()), resume_thread_id: resolve_resume_thread_id(state_store, issue_run)?, + ephemeral_thread: false, command_exec_health_check: None, dynamic_tool_handler: Some(&decodex_tool_bridge), continuation_guard: Some(&continuation_guard), diff --git a/docs/spec/app-server.md b/docs/spec/app-server.md index bd87269..2b1bbfd 100644 --- a/docs/spec/app-server.md +++ b/docs/spec/app-server.md @@ -167,6 +167,7 @@ The MVP thread start request owns these fields: - `cwd` - `dynamicTools` when the run exposes issue-scoped tracker tools - `developerInstructions` +- `ephemeral` only for synthetic Decodex probe threads Decodex must not inject project-owned config, model, personality, service-tier, sandbox, or approval-policy overrides into `thread/start`. Child runs inherit runtime defaults from the active Codex runtime. @@ -302,6 +303,8 @@ The `decodex probe` command must verify at least: 5. The local client can complete the bounded app-server capability preflight after `initialize` and before `thread/start`. 6. The local client can complete one bounded standalone `command/exec` health check after `initialize`. -7. The local client can complete one `dynamicTools -> item/tool/call -> response` round trip and still finish with the expected final output. +7. The local client can complete one ephemeral + `dynamicTools -> item/tool/call -> response` round trip and still finish with the + expected final output without materializing a probe thread on disk. The probe command is the first gate before deeper orchestrator logic depends on the protocol. From a26bdadbeb820c8412a32c8d6482a6b25c4b2f75 Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Tue, 12 May 2026 09:51:48 +0800 Subject: [PATCH 2/2] {"schema":"decodex/commit/1","summary":"Satisfy Decodex probe style gate","authority":"manual"} --- apps/decodex/src/agent/app_server/tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/decodex/src/agent/app_server/tests.rs b/apps/decodex/src/agent/app_server/tests.rs index 93fd095..9f33fa7 100644 --- a/apps/decodex/src/agent/app_server/tests.rs +++ b/apps/decodex/src/agent/app_server/tests.rs @@ -371,6 +371,7 @@ fn minimal_run_request<'a>() -> super::AppServerRunRequest<'a> { #[test] fn synthetic_probe_thread_start_is_ephemeral_when_requested() { let mut request = minimal_run_request(); + request.ephemeral_thread = true; let start = super::build_thread_start_request(&request).expect("request should build");