summaryrefslogtreecommitdiff
path: root/makima/src/server
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-19 17:40:25 +0000
committersoryu <soryu@soryu.co>2026-01-19 17:40:25 +0000
commit164941cbd591b46f69a034bb9b86521fd7700ddb (patch)
treea11f4dc7196f6e00c7d52da1cfc6aa982cce60aa /makima/src/server
parent0833fb1f30c0c3b920157deb882e0e902c3af02a (diff)
downloadsoryu-164941cbd591b46f69a034bb9b86521fd7700ddb.tar.gz
soryu-164941cbd591b46f69a034bb9b86521fd7700ddb.zip
Remove 'task' type contract
Diffstat (limited to 'makima/src/server')
-rw-r--r--makima/src/server/handlers/contract_chat.rs25
-rw-r--r--makima/src/server/handlers/contracts.rs132
-rw-r--r--makima/src/server/handlers/mesh.rs172
-rw-r--r--makima/src/server/mod.rs2
-rw-r--r--makima/src/server/openapi.rs7
5 files changed, 85 insertions, 253 deletions
diff --git a/makima/src/server/handlers/contract_chat.rs b/makima/src/server/handlers/contract_chat.rs
index 7e7b476..0f794c1 100644
--- a/makima/src/server/handlers/contract_chat.rs
+++ b/makima/src/server/handlers/contract_chat.rs
@@ -2795,10 +2795,20 @@ fn analyze_phase_readiness(contract: &crate::db::models::ContractWithRelations)
}
let ready = total_tasks > 0 && done_tasks == total_tasks;
+
+ // For simple contracts, execute is the terminal phase - suggest completion
+ if ready && contract.contract.contract_type == "simple" {
+ suggestions.push("Mark the contract as completed".to_string());
+ }
+
PhaseReadinessAnalysis {
ready,
summary: if ready {
- "All tasks completed. Ready for Review phase.".to_string()
+ if contract.contract.contract_type == "simple" {
+ "All tasks completed. Contract can be marked as completed.".to_string()
+ } else {
+ "All tasks completed. Ready for Review phase.".to_string()
+ }
} else if total_tasks == 0 {
"No tasks created yet. Create and complete tasks before reviewing.".to_string()
} else {
@@ -2815,12 +2825,19 @@ fn analyze_phase_readiness(contract: &crate::db::models::ContractWithRelations)
if review_files == 0 {
suggestions.push("Create review checklist or release notes".to_string());
+ } else {
+ // Review documentation exists - suggest completion
+ suggestions.push("Mark the contract as completed".to_string());
}
PhaseReadinessAnalysis {
- ready: false,
- summary: "Review is the final phase. Contract can be marked as complete when review is done.".to_string(),
- reasons: vec!["Review phase is the final phase".to_string()],
+ ready: review_files > 0,
+ summary: if review_files > 0 {
+ "Review documentation complete. Contract can be marked as completed.".to_string()
+ } else {
+ "Review phase needs documentation before completion.".to_string()
+ },
+ reasons: vec!["Review is the final phase".to_string()],
suggestions,
}
}
diff --git a/makima/src/server/handlers/contracts.rs b/makima/src/server/handlers/contracts.rs
index 4524860..11337f2 100644
--- a/makima/src/server/handlers/contracts.rs
+++ b/makima/src/server/handlers/contracts.rs
@@ -259,85 +259,75 @@ pub async fn create_contract(
match repository::create_contract_for_owner(pool, auth.owner_id, req.clone()).await {
Ok(contract) => {
- // Only create supervisor task for non-task type contracts
- // Task type contracts are lightweight adhoc tasks that don't need supervisors
- if contract.contract_type != crate::db::models::CONTRACT_TYPE_TASK {
- // Create supervisor task for this contract
- let supervisor_name = format!("{} Supervisor", contract.name);
- let supervisor_plan = format!(
- "You are the supervisor for contract '{}'. Your goal is to drive this contract to completion.\n\n{}",
- contract.name,
- contract.description.as_deref().unwrap_or("No description provided.")
- );
-
- // Get repository info from contract if available
- let repo_url = {
- // Try to get the first repository associated with this contract
- match repository::list_contract_repositories(pool, contract.id).await {
- Ok(repos) if !repos.is_empty() => {
- let repo = &repos[0];
- repo.repository_url.clone()
- }
- _ => None,
- }
- };
-
- let supervisor_req = crate::db::models::CreateTaskRequest {
- name: supervisor_name,
- description: None,
- plan: supervisor_plan,
- repository_url: repo_url,
- base_branch: None,
- target_branch: None,
- parent_task_id: None,
- contract_id: contract.id,
- target_repo_path: None,
- completion_action: None,
- continue_from_task_id: None,
- copy_files: None,
- is_supervisor: true,
- checkpoint_sha: None,
- priority: 0,
- merge_mode: None,
- };
-
- match repository::create_task_for_owner(pool, auth.owner_id, supervisor_req).await {
- Ok(supervisor_task) => {
- tracing::info!(
- contract_id = %contract.id,
- supervisor_task_id = %supervisor_task.id,
- is_supervisor = supervisor_task.is_supervisor,
- "Created supervisor task for contract"
- );
+ // Create supervisor task for this contract
+ let supervisor_name = format!("{} Supervisor", contract.name);
+ let supervisor_plan = format!(
+ "You are the supervisor for contract '{}'. Your goal is to drive this contract to completion.\n\n{}",
+ contract.name,
+ contract.description.as_deref().unwrap_or("No description provided.")
+ );
- // Update contract with supervisor_task_id
- let update_req = crate::db::models::UpdateContractRequest {
- supervisor_task_id: Some(supervisor_task.id),
- version: Some(contract.version),
- ..Default::default()
- };
- if let Err(e) = repository::update_contract_for_owner(pool, contract.id, auth.owner_id, update_req).await {
- tracing::warn!(
- contract_id = %contract.id,
- error = %e,
- "Failed to link supervisor task to contract"
- );
- }
+ // Get repository info from contract if available
+ let repo_url = {
+ // Try to get the first repository associated with this contract
+ match repository::list_contract_repositories(pool, contract.id).await {
+ Ok(repos) if !repos.is_empty() => {
+ let repo = &repos[0];
+ repo.repository_url.clone()
}
- Err(e) => {
+ _ => None,
+ }
+ };
+
+ let supervisor_req = crate::db::models::CreateTaskRequest {
+ name: supervisor_name,
+ description: None,
+ plan: supervisor_plan,
+ repository_url: repo_url,
+ base_branch: None,
+ target_branch: None,
+ parent_task_id: None,
+ contract_id: contract.id,
+ target_repo_path: None,
+ completion_action: None,
+ continue_from_task_id: None,
+ copy_files: None,
+ is_supervisor: true,
+ checkpoint_sha: None,
+ priority: 0,
+ merge_mode: None,
+ };
+
+ match repository::create_task_for_owner(pool, auth.owner_id, supervisor_req).await {
+ Ok(supervisor_task) => {
+ tracing::info!(
+ contract_id = %contract.id,
+ supervisor_task_id = %supervisor_task.id,
+ is_supervisor = supervisor_task.is_supervisor,
+ "Created supervisor task for contract"
+ );
+
+ // Update contract with supervisor_task_id
+ let update_req = crate::db::models::UpdateContractRequest {
+ supervisor_task_id: Some(supervisor_task.id),
+ version: Some(contract.version),
+ ..Default::default()
+ };
+ if let Err(e) = repository::update_contract_for_owner(pool, contract.id, auth.owner_id, update_req).await {
tracing::warn!(
contract_id = %contract.id,
error = %e,
- "Failed to create supervisor task for contract"
+ "Failed to link supervisor task to contract"
);
}
}
- } else {
- tracing::info!(
- contract_id = %contract.id,
- contract_type = %contract.contract_type,
- "Skipping supervisor creation for task-type contract"
- );
+ Err(e) => {
+ tracing::warn!(
+ contract_id = %contract.id,
+ error = %e,
+ "Failed to create supervisor task for contract"
+ );
+ }
}
// Record history event for contract creation
diff --git a/makima/src/server/handlers/mesh.rs b/makima/src/server/handlers/mesh.rs
index ad3ec79..275dc3c 100644
--- a/makima/src/server/handlers/mesh.rs
+++ b/makima/src/server/handlers/mesh.rs
@@ -9,10 +9,9 @@ use axum::{
use uuid::Uuid;
use crate::db::models::{
- AdhocTaskRequest, AdhocTaskResponse, ContractSummary, CreateContractRequest,
CreateTaskRequest, DaemonDirectory, DaemonDirectoriesResponse, DaemonListResponse,
SendMessageRequest, Task, TaskEventListResponse, TaskListResponse, TaskOutputEntry,
- TaskOutputResponse, TaskWithSubtasks, UpdateTaskRequest, CONTRACT_TYPE_TASK,
+ TaskOutputResponse, TaskWithSubtasks, UpdateTaskRequest,
};
use crate::db::repository::{self, RepositoryError};
use crate::server::auth::Authenticated;
@@ -375,40 +374,6 @@ pub async fn update_task(
}
});
}
-
- // Auto-archive task-type contracts when task reaches terminal status
- let terminal_statuses = ["done", "failed"];
- if terminal_statuses.contains(&task.status.as_str()) {
- let pool = pool.clone();
- let owner_id = auth.owner_id;
- let task_id = task.id;
- tokio::spawn(async move {
- if let Ok(Some(contract)) = repository::get_contract_for_owner(&pool, contract_id, owner_id).await {
- if contract.contract_type == CONTRACT_TYPE_TASK {
- // Archive the contract
- let update_req = crate::db::models::UpdateContractRequest {
- status: Some("archived".to_string()),
- version: Some(contract.version),
- ..Default::default()
- };
- if let Err(e) = repository::update_contract_for_owner(&pool, contract_id, owner_id, update_req).await {
- tracing::warn!(
- contract_id = %contract_id,
- task_id = %task_id,
- error = %e,
- "Failed to auto-archive task-type contract"
- );
- } else {
- tracing::info!(
- contract_id = %contract_id,
- task_id = %task_id,
- "Auto-archived task-type contract on task completion"
- );
- }
- }
- }
- });
- }
}
Json(task).into_response()
@@ -3339,138 +3304,3 @@ pub async fn branch_from_checkpoint(
)
.into_response()
}
-
-// =============================================================================
-// Adhoc Task Endpoint
-// =============================================================================
-
-/// Create an adhoc (one-off) task without supervisor overhead.
-///
-/// This creates a minimal "task" type contract with a single task.
-/// The contract auto-archives when the task completes.
-#[utoipa::path(
- post,
- path = "/api/v1/tasks/adhoc",
- request_body = AdhocTaskRequest,
- responses(
- (status = 201, description = "Adhoc task created", body = AdhocTaskResponse),
- (status = 400, description = "Invalid request", body = ApiError),
- (status = 401, description = "Unauthorized", body = ApiError),
- (status = 503, description = "Database not configured", body = ApiError),
- (status = 500, description = "Internal server error", body = ApiError),
- ),
- security(
- ("bearer_auth" = []),
- ("api_key" = [])
- ),
- tag = "Mesh"
-)]
-pub async fn create_adhoc_task(
- State(state): State<SharedState>,
- Authenticated(auth): Authenticated,
- Json(req): Json<AdhocTaskRequest>,
-) -> impl IntoResponse {
- let Some(ref pool) = state.db_pool else {
- return (
- StatusCode::SERVICE_UNAVAILABLE,
- Json(ApiError::new("DB_UNAVAILABLE", "Database not configured")),
- )
- .into_response();
- };
-
- // Generate a short unique name for the contract
- let contract_name = format!("task-{}", &Uuid::new_v4().to_string()[..8]);
-
- // 1. Create a minimal "task" type contract
- let contract_req = CreateContractRequest {
- name: contract_name,
- description: Some(req.name.clone()),
- contract_type: Some(CONTRACT_TYPE_TASK.to_string()),
- initial_phase: Some("execute".to_string()), // Skip planning
- autonomous_loop: Some(false),
- phase_guard: None,
- };
-
- let contract = match repository::create_contract_for_owner(pool, auth.owner_id, contract_req).await {
- Ok(c) => c,
- Err(e) => {
- tracing::error!("Failed to create adhoc task contract: {}", e);
- return (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(ApiError::new("DB_ERROR", e.to_string())),
- )
- .into_response();
- }
- };
-
- tracing::info!(
- contract_id = %contract.id,
- contract_type = %contract.contract_type,
- "Created task-type contract for adhoc task"
- );
-
- // 2. Create the actual task (no supervisor)
- let task_req = CreateTaskRequest {
- contract_id: contract.id,
- name: req.name,
- description: None,
- plan: req.plan,
- is_supervisor: false,
- parent_task_id: None,
- priority: 0,
- repository_url: req.repository_url,
- base_branch: req.base_branch,
- target_branch: None,
- merge_mode: None,
- target_repo_path: None,
- completion_action: None,
- continue_from_task_id: None,
- copy_files: None,
- checkpoint_sha: None,
- };
-
- let task = match repository::create_task_for_owner(pool, auth.owner_id, task_req).await {
- Ok(t) => t,
- Err(e) => {
- tracing::error!("Failed to create adhoc task: {}", e);
- // Clean up the contract we just created
- let _ = repository::delete_contract_for_owner(pool, contract.id, auth.owner_id).await;
- return (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(ApiError::new("DB_ERROR", e.to_string())),
- )
- .into_response();
- }
- };
-
- tracing::info!(
- task_id = %task.id,
- contract_id = %contract.id,
- "Created adhoc task"
- );
-
- // Build the contract summary for the response
- let contract_summary = ContractSummary {
- id: contract.id,
- name: contract.name,
- description: contract.description,
- contract_type: contract.contract_type,
- phase: contract.phase,
- status: contract.status,
- supervisor_task_id: contract.supervisor_task_id,
- file_count: 0,
- task_count: 1,
- repository_count: 0,
- version: contract.version,
- created_at: contract.created_at,
- };
-
- (
- StatusCode::CREATED,
- Json(AdhocTaskResponse {
- contract: contract_summary,
- task,
- }),
- )
- .into_response()
-}
diff --git a/makima/src/server/mod.rs b/makima/src/server/mod.rs
index a4cb3d1..7e31285 100644
--- a/makima/src/server/mod.rs
+++ b/makima/src/server/mod.rs
@@ -63,8 +63,6 @@ pub fn make_router(state: SharedState) -> Router {
.route("/files/{id}/versions/{version}", get(versions::get_version))
.route("/files/{id}/versions/restore", post(versions::restore_version))
// Mesh/task orchestration endpoints
- // Adhoc task endpoint (creates task-type contract + task in one call)
- .route("/tasks/adhoc", post(mesh::create_adhoc_task))
.route(
"/mesh/tasks",
get(mesh::list_tasks).post(mesh::create_task),
diff --git a/makima/src/server/openapi.rs b/makima/src/server/openapi.rs
index 47e3456..4daae3b 100644
--- a/makima/src/server/openapi.rs
+++ b/makima/src/server/openapi.rs
@@ -3,8 +3,8 @@
use utoipa::OpenApi;
use crate::db::models::{
- AddLocalRepositoryRequest, AddRemoteRepositoryRequest, AdhocTaskRequest, AdhocTaskResponse,
- BranchInfo, BranchListResponse, ChangePhaseRequest, Contract, ContractChatHistoryResponse,
+ AddLocalRepositoryRequest, AddRemoteRepositoryRequest, BranchInfo, BranchListResponse,
+ ChangePhaseRequest, Contract, ContractChatHistoryResponse,
ContractChatMessageRecord, ContractEvent, ContractListResponse, ContractRepository,
ContractSummary, ContractWithRelations, CreateContractRequest, CreateFileRequest,
CreateManagedRepositoryRequest, CreateTaskRequest, Daemon, DaemonDirectoriesResponse,
@@ -43,7 +43,6 @@ use crate::server::messages::{ApiError, AudioEncoding, StartMessage, StopMessage
mesh::list_tasks,
mesh::get_task,
mesh::create_task,
- mesh::create_adhoc_task,
mesh::update_task,
mesh::delete_task,
mesh::list_subtasks,
@@ -124,8 +123,6 @@ use crate::server::messages::{ApiError, AudioEncoding, StartMessage, StopMessage
CreateTaskRequest,
UpdateTaskRequest,
SendMessageRequest,
- AdhocTaskRequest,
- AdhocTaskResponse,
TaskEventListResponse,
Daemon,
DaemonListResponse,