From 20cb92d4da7cd447a21fede50ffbddd4ee973b4b Mon Sep 17 00:00:00 2001 From: "ilblackdragon@gmail.com" Date: Tue, 24 Mar 2026 11:51:39 -0700 Subject: [PATCH] feat(bridge): map routine_* calls to mission operations in v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the model calls routine_create, routine_list, routine_fire, routine_pause, routine_resume, or routine_delete, the bridge now routes them to the MissionManager instead of blocking with an error. Mapping: routine_create → mission_create (with cadence parsing) routine_list → mission_list routine_fire → mission_fire routine_pause → mission_pause routine_resume → mission_resume routine_update → mission_pause/resume (based on params) routine_delete → mission_complete (marks as done) Routine tools removed from v1-only blocklist and restored in available_actions(). The model can use either "routine" or "mission" vocabulary — both work. Still blocked: create_job, cancel_job, build_software (need v1 Scheduler/ContainerJobManager refs). Co-Authored-By: Claude Opus 4.6 (1M context) --- src/bridge/effect_adapter.rs | 55 +++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/src/bridge/effect_adapter.rs b/src/bridge/effect_adapter.rs index e7b318e9..7a95bf7c 100644 --- a/src/bridge/effect_adapter.rs +++ b/src/bridge/effect_adapter.rs @@ -84,7 +84,8 @@ impl EffectBridgeAdapter { let mgr = mgr.as_ref()?; let result = match action_name { - "mission_create" => { + // routine_create maps to mission_create in v2 + "mission_create" | "routine_create" => { let name = params .get("name") .or_else(|| params.get("_args").and_then(|a| a.get(0))) @@ -110,7 +111,7 @@ impl EffectBridgeAdapter { Err(e) => Err(e), } } - "mission_list" => match mgr.list_missions(context.project_id).await { + "mission_list" | "routine_list" => match mgr.list_missions(context.project_id).await { Ok(missions) => { let list: Vec = missions .iter() @@ -129,7 +130,7 @@ impl EffectBridgeAdapter { } Err(e) => Err(e), }, - "mission_fire" => { + "mission_fire" | "routine_fire" => { let id_str = params .get("id") .or_else(|| params.get("_args").and_then(|a| a.get(0))) @@ -153,7 +154,8 @@ impl EffectBridgeAdapter { Err(e) => Err(e), } } - "mission_pause" | "mission_resume" => { + "mission_pause" | "mission_resume" | "routine_pause" | "routine_resume" + | "routine_update" => { let id_str = params .get("id") .or_else(|| params.get("_args").and_then(|a| a.get(0))) @@ -166,11 +168,12 @@ impl EffectBridgeAdapter { }); match id { Ok(id) => { - let res = if action_name == "mission_pause" { - mgr.pause_mission(id).await - } else { - mgr.resume_mission(id).await - }; + let res = + if action_name == "mission_pause" || action_name == "routine_pause" { + mgr.pause_mission(id).await + } else { + mgr.resume_mission(id).await + }; match res { Ok(()) => Ok(serde_json::json!({"status": "ok"})), Err(e) => Err(e), @@ -179,7 +182,27 @@ impl EffectBridgeAdapter { Err(e) => Err(e), } } - _ => return None, // Not a mission call + "routine_delete" | "mission_delete" => { + let id_str = params + .get("id") + .or_else(|| params.get("name")) // routine_delete uses "name" param + .or_else(|| params.get("_args").and_then(|a| a.get(0))) + .and_then(|v| v.as_str()) + .unwrap_or(""); + let id = uuid::Uuid::parse_str(id_str) + .map(ironclaw_engine::MissionId) + .map_err(|e| EngineError::Effect { + reason: format!("invalid mission id: {e}"), + }); + match id { + Ok(id) => match mgr.complete_mission(id).await { + Ok(()) => Ok(serde_json::json!({"status": "deleted"})), + Err(e) => Err(e), + }, + Err(e) => Err(e), + } + } + _ => return None, // Not a mission/routine call }; Some(match result { @@ -479,18 +502,12 @@ fn parse_cadence(s: &str) -> ironclaw_engine::types::mission::MissionCadence { /// Tools that depend on v1 runtime components (RoutineEngine, Scheduler, /// ContainerJobManager) and cannot work in engine v2's minimal JobContext. +/// Tools that depend on v1 runtime components and can't work in engine v2. +/// Note: routine_* tools are NOT blocked — they map to mission operations. fn is_v1_only_tool(name: &str) -> bool { matches!( name, - "routine_create" - | "routine-create" - | "routine_update" - | "routine-update" - | "routine_delete" - | "routine-delete" - | "routine_fire" - | "routine-fire" - | "create_job" + "create_job" | "create-job" | "cancel_job" | "cancel-job"