summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-02-06 21:43:21 +0000
committersoryu <soryu@soryu.co>2026-02-06 21:43:21 +0000
commitcececbf326e258211ceae7afce716a5d1e46014f (patch)
treea10db0b7e26908b043fa9b9b065be5d5c94bbd67
parent1b692b8cde4a888c8a35af69231f181b57bf5619 (diff)
downloadsoryu-cececbf326e258211ceae7afce716a5d1e46014f.tar.gz
soryu-cececbf326e258211ceae7afce716a5d1e46014f.zip
Fix: Link directives and contracts
-rw-r--r--makima/src/orchestration/engine.rs51
-rw-r--r--makima/src/orchestration/mod.rs2
-rw-r--r--makima/src/server/handlers/directives.rs68
3 files changed, 114 insertions, 7 deletions
diff --git a/makima/src/orchestration/engine.rs b/makima/src/orchestration/engine.rs
index 470db40..9f7c3b1 100644
--- a/makima/src/orchestration/engine.rs
+++ b/makima/src/orchestration/engine.rs
@@ -99,6 +99,20 @@ pub enum EngineEvent {
},
}
+/// Result from starting a directive, containing info needed for auto-start.
+pub struct PlanningStartResult {
+ /// The planning task ID that needs to be started on a daemon
+ pub task_id: Uuid,
+ /// The owner ID for finding available daemons
+ pub owner_id: Uuid,
+ /// The planning task details needed for the SpawnTask command
+ pub task_name: String,
+ pub plan: String,
+ pub contract_id: Uuid,
+ pub repository_url: Option<String>,
+ pub base_branch: Option<String>,
+}
+
/// Main orchestration engine for directives.
pub struct DirectiveEngine {
pool: PgPool,
@@ -134,7 +148,8 @@ impl DirectiveEngine {
// ========================================================================
/// Start a directive: spawn a planning contract+task to generate the chain.
- pub async fn start_directive(&self, directive_id: Uuid) -> Result<(), EngineError> {
+ /// Returns a `PlanningStartResult` so the caller can auto-start the task on a daemon.
+ pub async fn start_directive(&self, directive_id: Uuid) -> Result<PlanningStartResult, EngineError> {
let directive = repository::get_directive(&self.pool, directive_id)
.await?
.ok_or(EngineError::DirectiveNotFound(directive_id))?;
@@ -201,17 +216,18 @@ impl DirectiveEngine {
let plan = self.build_planning_task_instructions(&directive);
// Create the planning task
- let _task = repository::create_task_for_owner(
+ let task_name = format!("{} - Planning", directive.title);
+ let task = repository::create_task_for_owner(
&self.pool,
directive.owner_id,
CreateTaskRequest {
contract_id: Some(contract.id),
- name: format!("{} - Planning", directive.title),
+ name: task_name.clone(),
description: Some(format!(
"Plan the execution chain for directive: {}",
directive.goal
)),
- plan,
+ plan: plan.clone(),
parent_task_id: None,
is_supervisor: true,
priority: 5,
@@ -234,6 +250,22 @@ impl DirectiveEngine {
EngineError::ContractCreation(format!("Failed to create planning task: {}", e))
})?;
+ // Link the supervisor task to the contract
+ if let Err(e) = repository::update_contract_supervisor(
+ &self.pool,
+ contract.id,
+ task.id,
+ )
+ .await
+ {
+ tracing::warn!(
+ contract_id = %contract.id,
+ task_id = %task.id,
+ error = %e,
+ "Failed to link supervisor task to planning contract"
+ );
+ }
+
// Link the planning contract to the directive
repository::set_directive_orchestrator_contract(
&self.pool,
@@ -248,13 +280,22 @@ impl DirectiveEngine {
"info",
serde_json::json!({
"contract_id": contract.id,
+ "task_id": task.id,
"message": "Planning task spawned, waiting for chain generation",
}),
"system",
)
.await?;
- Ok(())
+ Ok(PlanningStartResult {
+ task_id: task.id,
+ owner_id: directive.owner_id,
+ task_name,
+ plan,
+ contract_id: contract.id,
+ repository_url: directive.repository_url.clone(),
+ base_branch: directive.base_branch.clone(),
+ })
}
/// Pause a directive.
diff --git a/makima/src/orchestration/mod.rs b/makima/src/orchestration/mod.rs
index 8c21089..8fca5ba 100644
--- a/makima/src/orchestration/mod.rs
+++ b/makima/src/orchestration/mod.rs
@@ -18,7 +18,7 @@ mod engine;
mod planner;
mod verifier;
-pub use engine::{DirectiveEngine, EngineError};
+pub use engine::{DirectiveEngine, EngineError, PlanningStartResult};
pub use planner::{ChainPlanner, GeneratedSpec, PlannerError};
pub use verifier::{
auto_detect_verifiers, CompositeEvaluator, ConfidenceLevel, EvaluationResult, Verifier,
diff --git a/makima/src/server/handlers/directives.rs b/makima/src/server/handlers/directives.rs
index 52422cd..9c65c5e 100644
--- a/makima/src/server/handlers/directives.rs
+++ b/makima/src/server/handlers/directives.rs
@@ -292,7 +292,73 @@ pub async fn start_directive(
// Start directive via orchestration engine
let engine = crate::orchestration::DirectiveEngine::new(pool.clone());
match engine.start_directive(id).await {
- Ok(()) => {
+ Ok(planning) => {
+ // Auto-start the planning task on an available daemon
+ if let Some(daemon_id) = state.find_alternative_daemon(auth.owner_id, &[]) {
+ // Update task status to "starting" and assign daemon
+ let update_req = crate::db::models::UpdateTaskRequest {
+ status: Some("starting".to_string()),
+ daemon_id: Some(daemon_id),
+ ..Default::default()
+ };
+ if let Err(e) = repository::update_task_for_owner(
+ pool,
+ planning.task_id,
+ auth.owner_id,
+ update_req,
+ )
+ .await
+ {
+ tracing::warn!("Failed to update planning task status: {}", e);
+ }
+
+ let command = crate::server::state::DaemonCommand::SpawnTask {
+ task_id: planning.task_id,
+ task_name: planning.task_name,
+ plan: planning.plan,
+ repo_url: planning.repository_url,
+ base_branch: planning.base_branch,
+ target_branch: None,
+ parent_task_id: None,
+ depth: 0,
+ is_orchestrator: false,
+ target_repo_path: None,
+ completion_action: Some("none".to_string()),
+ continue_from_task_id: None,
+ copy_files: None,
+ contract_id: Some(planning.contract_id),
+ is_supervisor: true,
+ autonomous_loop: false,
+ resume_session: false,
+ conversation_history: None,
+ patch_data: None,
+ patch_base_sha: None,
+ local_only: false,
+ auto_merge_local: false,
+ supervisor_worktree_task_id: None,
+ };
+
+ if let Err(e) = state.send_daemon_command(daemon_id, command).await {
+ tracing::warn!(
+ "Failed to auto-start planning task on daemon {}: {}",
+ daemon_id,
+ e
+ );
+ } else {
+ tracing::info!(
+ "Auto-started planning task {} on daemon {} for directive {}",
+ planning.task_id,
+ daemon_id,
+ id
+ );
+ }
+ } else {
+ tracing::warn!(
+ "No daemon available to auto-start planning task for directive {}",
+ id
+ );
+ }
+
// Return the updated directive with progress
match repository::get_directive_with_progress(pool, id, auth.owner_id).await {
Ok(Some(directive)) => Json(directive).into_response(),