summaryrefslogtreecommitdiff
path: root/makima/src/server
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-02-02 02:34:50 +0000
committersoryu <soryu@soryu.co>2026-02-02 02:34:50 +0000
commit151e9d87e117b7980e6aad522ac8f3633eeca87a (patch)
treee80fb4301361b3b12e5abf8e442603db2d0622dc /makima/src/server
parenta2c147ddd59f55a07b5be0c8970169726b55c876 (diff)
downloadsoryu-151e9d87e117b7980e6aad522ac8f3633eeca87a.tar.gz
soryu-151e9d87e117b7980e6aad522ac8f3633eeca87a.zip
Make makima more opinionated and structured
Diffstat (limited to 'makima/src/server')
-rw-r--r--makima/src/server/handlers/contract_chat.rs10
-rw-r--r--makima/src/server/handlers/contracts.rs5
-rw-r--r--makima/src/server/handlers/mesh.rs4
-rw-r--r--makima/src/server/handlers/mesh_chat.rs1
-rw-r--r--makima/src/server/handlers/mesh_daemon.rs62
-rw-r--r--makima/src/server/handlers/mesh_red_team.rs497
-rw-r--r--makima/src/server/handlers/mesh_supervisor.rs306
-rw-r--r--makima/src/server/handlers/mod.rs1
-rw-r--r--makima/src/server/handlers/templates.rs419
-rw-r--r--makima/src/server/handlers/transcript_analysis.rs4
-rw-r--r--makima/src/server/mod.rs18
11 files changed, 77 insertions, 1250 deletions
diff --git a/makima/src/server/handlers/contract_chat.rs b/makima/src/server/handlers/contract_chat.rs
index b025485..2d54894 100644
--- a/makima/src/server/handlers/contract_chat.rs
+++ b/makima/src/server/handlers/contract_chat.rs
@@ -1362,7 +1362,6 @@ async fn handle_contract_request(
continue_from_task_id: None,
copy_files: None,
is_supervisor: false,
- is_red_team: false,
checkpoint_sha: None,
branched_from_task_id: None,
conversation_history: None,
@@ -1460,7 +1459,6 @@ async fn handle_contract_request(
continue_from_task_id: None,
copy_files: None,
is_supervisor: false,
- is_red_team: false,
checkpoint_sha: None,
branched_from_task_id: None,
conversation_history: None,
@@ -2213,8 +2211,7 @@ async fn handle_contract_request(
continue_from_task_id: previous_task_id,
copy_files: None,
is_supervisor: false,
- is_red_team: false,
- checkpoint_sha: None,
+ checkpoint_sha: None,
branched_from_task_id: None,
conversation_history: None,
supervisor_worktree_task_id: None, // Not spawned by supervisor
@@ -2612,8 +2609,6 @@ async fn handle_contract_request(
phase_guard: None,
local_only: None,
auto_merge_local: None,
- red_team_enabled: None,
- red_team_prompt: None,
template_id: None,
};
@@ -2736,8 +2731,7 @@ async fn handle_contract_request(
continue_from_task_id: None,
copy_files: None,
is_supervisor: false,
- is_red_team: false,
- checkpoint_sha: None,
+ checkpoint_sha: None,
branched_from_task_id: None,
conversation_history: None,
supervisor_worktree_task_id: None, // Not spawned by supervisor
diff --git a/makima/src/server/handlers/contracts.rs b/makima/src/server/handlers/contracts.rs
index 01b4610..8c8cabf 100644
--- a/makima/src/server/handlers/contracts.rs
+++ b/makima/src/server/handlers/contracts.rs
@@ -363,7 +363,6 @@ pub async fn create_contract(
continue_from_task_id: None,
copy_files: None,
is_supervisor: true,
- is_red_team: false,
checkpoint_sha: None,
priority: 0,
merge_mode: None,
@@ -438,7 +437,6 @@ pub async fn create_contract(
supervisor_task_id: contract.supervisor_task_id,
local_only: contract.local_only,
auto_merge_local: contract.auto_merge_local,
- red_team_enabled: contract.red_team_enabled,
file_count: 0,
task_count: 0,
repository_count: 0,
@@ -462,7 +460,6 @@ pub async fn create_contract(
supervisor_task_id: contract.supervisor_task_id,
local_only: contract.local_only,
auto_merge_local: contract.auto_merge_local,
- red_team_enabled: contract.red_team_enabled,
file_count: 0,
task_count: 0,
repository_count: 0,
@@ -593,7 +590,6 @@ pub async fn update_contract(
supervisor_task_id: contract.supervisor_task_id,
local_only: contract.local_only,
auto_merge_local: contract.auto_merge_local,
- red_team_enabled: contract.red_team_enabled,
file_count: 0,
task_count: 0,
repository_count: 0,
@@ -1523,7 +1519,6 @@ pub async fn change_phase(
supervisor_task_id: updated_contract.supervisor_task_id,
local_only: updated_contract.local_only,
auto_merge_local: updated_contract.auto_merge_local,
- red_team_enabled: updated_contract.red_team_enabled,
file_count: 0,
task_count: 0,
repository_count: 0,
diff --git a/makima/src/server/handlers/mesh.rs b/makima/src/server/handlers/mesh.rs
index af77b56..fe9ffc0 100644
--- a/makima/src/server/handlers/mesh.rs
+++ b/makima/src/server/handlers/mesh.rs
@@ -2613,7 +2613,6 @@ pub async fn reassign_task(
plan: updated_plan.clone(),
parent_task_id: task.parent_task_id,
is_supervisor: task.is_supervisor,
- is_red_team: task.is_red_team,
priority: task.priority,
repository_url: task.repository_url.clone(),
base_branch: task.base_branch.clone(),
@@ -3390,7 +3389,6 @@ pub async fn fork_task(
plan: req.new_task_plan.clone(),
parent_task_id: None, // Forked tasks are independent
is_supervisor: false,
- is_red_team: false,
priority: task.priority,
repository_url: task.repository_url.clone(),
base_branch: task.base_branch.clone(),
@@ -3549,7 +3547,6 @@ pub async fn resume_from_checkpoint(
plan: req.plan,
parent_task_id: None,
is_supervisor: false,
- is_red_team: false,
priority: task.priority,
repository_url: task.repository_url.clone(),
base_branch: task.base_branch.clone(),
@@ -3886,7 +3883,6 @@ pub async fn branch_task(
plan: req.message,
parent_task_id: None,
is_supervisor: false,
- is_red_team: false,
priority: source_task.priority,
repository_url: source_task.repository_url.clone(),
base_branch: source_task.base_branch.clone(),
diff --git a/makima/src/server/handlers/mesh_chat.rs b/makima/src/server/handlers/mesh_chat.rs
index eee899f..a6a3a3c 100644
--- a/makima/src/server/handlers/mesh_chat.rs
+++ b/makima/src/server/handlers/mesh_chat.rs
@@ -1017,7 +1017,6 @@ async fn handle_mesh_request(
continue_from_task_id: None,
copy_files: None,
is_supervisor: false,
- is_red_team: false,
checkpoint_sha: None,
branched_from_task_id: None,
conversation_history: None,
diff --git a/makima/src/server/handlers/mesh_daemon.rs b/makima/src/server/handlers/mesh_daemon.rs
index 34e2cc3..cb929ea 100644
--- a/makima/src/server/handlers/mesh_daemon.rs
+++ b/makima/src/server/handlers/mesh_daemon.rs
@@ -1870,6 +1870,68 @@ async fn handle_daemon_connection(socket: WebSocket, state: SharedState, auth_re
}
}
}
+ } else if let (Some(patch_b64), Some(base_sha)) = (&patch_data, &patch_base_sha) {
+ // Ephemeral patch-only checkpoint (no git commit)
+ // Store patch directly in checkpoint_patches without a task_checkpoint
+ if let Some(pool) = state.db_pool.as_ref() {
+ match base64::engine::general_purpose::STANDARD.decode(patch_b64) {
+ Ok(patch_bytes) => {
+ let files_count = patch_files_count.unwrap_or(0);
+ // Default TTL: 7 days (168 hours)
+ let ttl_hours = 168i64;
+ match repository::create_checkpoint_patch(
+ pool,
+ task_id,
+ None, // No checkpoint_id for ephemeral patches
+ base_sha,
+ &patch_bytes,
+ files_count,
+ ttl_hours,
+ ).await {
+ Ok(patch) => {
+ tracing::info!(
+ task_id = %task_id,
+ patch_id = %patch.id,
+ patch_size = patch_bytes.len(),
+ files_count = files_count,
+ "Ephemeral patch stored for recovery"
+ );
+
+ state.broadcast_task_output(TaskOutputNotification {
+ task_id,
+ owner_id: Some(owner_id),
+ message_type: "system".to_string(),
+ content: format!(
+ "✓ Patch saved: {} ({} files)",
+ message,
+ files_count
+ ),
+ tool_name: None,
+ tool_input: None,
+ is_error: Some(false),
+ cost_usd: None,
+ duration_ms: None,
+ is_partial: false,
+ });
+ }
+ Err(e) => {
+ tracing::warn!(
+ task_id = %task_id,
+ error = %e,
+ "Failed to store ephemeral patch"
+ );
+ }
+ }
+ }
+ Err(e) => {
+ tracing::warn!(
+ task_id = %task_id,
+ error = %e,
+ "Failed to decode ephemeral patch base64 data"
+ );
+ }
+ }
+ }
}
} else {
// Broadcast failure
diff --git a/makima/src/server/handlers/mesh_red_team.rs b/makima/src/server/handlers/mesh_red_team.rs
deleted file mode 100644
index c5af60e..0000000
--- a/makima/src/server/handlers/mesh_red_team.rs
+++ /dev/null
@@ -1,497 +0,0 @@
-//! HTTP handlers for red team mesh operations.
-//!
-//! These endpoints are used by red team tasks (via the makima CLI) to notify
-//! supervisors of potential issues and query their own status.
-
-use axum::{
- extract::State,
- http::{HeaderMap, StatusCode},
- response::IntoResponse,
- Json,
-};
-use serde::{Deserialize, Serialize};
-use utoipa::ToSchema;
-use uuid::Uuid;
-
-use crate::db::repository;
-use crate::server::handlers::mesh::{extract_auth, AuthSource};
-use crate::server::messages::ApiError;
-use crate::server::state::{DaemonCommand, SharedState};
-
-// =============================================================================
-// Request/Response Types
-// =============================================================================
-
-/// Severity level for red team notifications.
-#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
-#[serde(rename_all = "lowercase")]
-pub enum RedTeamSeverity {
- /// Informational notice - minor issue or suggestion
- Info,
- /// Warning - potential problem that should be reviewed
- Warning,
- /// Critical - serious issue requiring immediate attention
- Critical,
-}
-
-impl Default for RedTeamSeverity {
- fn default() -> Self {
- Self::Warning
- }
-}
-
-impl std::fmt::Display for RedTeamSeverity {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- Self::Info => write!(f, "INFO"),
- Self::Warning => write!(f, "WARNING"),
- Self::Critical => write!(f, "CRITICAL"),
- }
- }
-}
-
-/// Request to notify the supervisor of a potential issue.
-#[derive(Debug, Deserialize, ToSchema)]
-#[serde(rename_all = "camelCase")]
-pub struct RedTeamNotifyRequest {
- /// The issue description/message to send to the supervisor
- pub message: String,
- /// Severity level of the issue
- #[serde(default)]
- pub severity: RedTeamSeverity,
- /// ID of the task being reviewed (optional - if not provided, assumes general contract concern)
- pub related_task_id: Option<Uuid>,
- /// File path related to the issue (optional)
- pub file_path: Option<String>,
- /// Additional context about the issue
- pub context: Option<String>,
-}
-
-/// Response from the notify endpoint.
-#[derive(Debug, Serialize, ToSchema)]
-#[serde(rename_all = "camelCase")]
-pub struct RedTeamNotifyResponse {
- /// Unique ID for this notification
- pub notification_id: Uuid,
- /// Whether the notification was successfully delivered to the supervisor
- pub delivered: bool,
- /// The supervisor task ID that received the notification
- pub supervisor_task_id: Option<Uuid>,
-}
-
-/// Response from the status endpoint.
-#[derive(Debug, Serialize, ToSchema)]
-#[serde(rename_all = "camelCase")]
-pub struct RedTeamStatusResponse {
- /// Contract ID being monitored
- pub contract_id: Uuid,
- /// Red team task ID
- pub red_team_task_id: Uuid,
- /// Current task status
- pub status: String,
- /// Number of notifications sent so far
- pub notifications_sent: i64,
-}
-
-/// Red team notification record stored in database.
-#[derive(Debug, Clone, Serialize, ToSchema)]
-#[serde(rename_all = "camelCase")]
-pub struct RedTeamNotification {
- pub id: Uuid,
- pub red_team_task_id: Uuid,
- pub contract_id: Uuid,
- pub message: String,
- pub severity: String,
- pub related_task_id: Option<Uuid>,
- pub file_path: Option<String>,
- pub context: Option<String>,
- pub delivered: bool,
- pub created_at: chrono::DateTime<chrono::Utc>,
-}
-
-// =============================================================================
-// Helper Functions
-// =============================================================================
-
-/// Verify the request comes from a red team task and extract ownership info.
-///
-/// Returns (task_id, owner_id, contract_id) on success.
-async fn verify_red_team_auth(
- state: &SharedState,
- headers: &HeaderMap,
-) -> Result<(Uuid, Uuid, Uuid), (StatusCode, Json<ApiError>)> {
- let auth = extract_auth(state, headers);
-
- let task_id = match auth {
- AuthSource::ToolKey(task_id) => task_id,
- _ => {
- return Err((
- StatusCode::UNAUTHORIZED,
- Json(ApiError::new(
- "UNAUTHORIZED",
- "Red team endpoints require tool key auth",
- )),
- ));
- }
- };
-
- // Get the task to verify it's a red team task and get owner_id
- let pool = state.db_pool.as_ref().ok_or_else(|| {
- (
- StatusCode::SERVICE_UNAVAILABLE,
- Json(ApiError::new("DB_UNAVAILABLE", "Database not configured")),
- )
- })?;
-
- let task = repository::get_task(pool, task_id)
- .await
- .map_err(|e| {
- tracing::error!(error = %e, "Failed to get red team task");
- (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(ApiError::new("DB_ERROR", "Failed to verify red team task")),
- )
- })?
- .ok_or_else(|| {
- (
- StatusCode::NOT_FOUND,
- Json(ApiError::new("NOT_FOUND", "Task not found")),
- )
- })?;
-
- // Verify task is a red team task
- // NOTE: This requires the is_red_team field to be added to the Task struct.
- // For now, we check if the task name contains "red-team" or "red_team" as a fallback.
- let is_red_team = task.name.to_lowercase().contains("red-team")
- || task.name.to_lowercase().contains("red_team")
- || task.name.to_lowercase().contains("redteam");
-
- if !is_red_team {
- return Err((
- StatusCode::FORBIDDEN,
- Json(ApiError::new(
- "NOT_RED_TEAM",
- "Only red team tasks can use these endpoints",
- )),
- ));
- }
-
- // Red team tasks must be associated with a contract
- let contract_id = task.contract_id.ok_or_else(|| {
- (
- StatusCode::BAD_REQUEST,
- Json(ApiError::new(
- "NO_CONTRACT",
- "Red team task must be associated with a contract",
- )),
- )
- })?;
-
- Ok((task_id, task.owner_id, contract_id))
-}
-
-/// Format an alert message for the supervisor.
-///
-/// Creates a formatted alert with clear visual markers to grab attention.
-fn format_alert_message(
- severity: &RedTeamSeverity,
- message: &str,
- related_task_id: Option<Uuid>,
- file_path: Option<&str>,
- context: Option<&str>,
-) -> String {
- let severity_marker = match severity {
- RedTeamSeverity::Info => "ℹ️",
- RedTeamSeverity::Warning => "⚠️",
- RedTeamSeverity::Critical => "🚨",
- };
-
- let border = match severity {
- RedTeamSeverity::Info => "─".repeat(60),
- RedTeamSeverity::Warning => "━".repeat(60),
- RedTeamSeverity::Critical => "═".repeat(60),
- };
-
- let mut alert = format!(
- r#"
-{}
-{} [RED TEAM ALERT] - {}
-{}
-
-Issue: {}
-"#,
- border, severity_marker, severity, border, message
- );
-
- if let Some(task_id) = related_task_id {
- alert.push_str(&format!("\nRelated Task: {}\n", task_id));
- }
-
- if let Some(path) = file_path {
- alert.push_str(&format!("File: {}\n", path));
- }
-
- if let Some(ctx) = context {
- alert.push_str(&format!("\nContext:\n{}\n", ctx));
- }
-
- // Add action suggestions based on severity
- let actions = match severity {
- RedTeamSeverity::Info => {
- "Suggested Actions:\n- Review when convenient\n- Consider if changes are needed"
- }
- RedTeamSeverity::Warning => {
- "Suggested Actions:\n- Review the flagged item soon\n- Check if this deviates from the contract\n- Consider pausing related work until reviewed"
- }
- RedTeamSeverity::Critical => {
- "Suggested Actions:\n- STOP related work immediately\n- Review the flagged item urgently\n- Verify compliance with contract requirements\n- Consider reverting recent changes if necessary"
- }
- };
-
- alert.push_str(&format!("\n{}\n{}\n", actions, border));
-
- alert
-}
-
-// =============================================================================
-// Handlers
-// =============================================================================
-
-/// Notify the supervisor of a potential issue.
-///
-/// POST /api/v1/mesh/red-team/notify
-///
-/// This endpoint allows red team tasks to alert supervisors about issues they've
-/// identified during code review. The notification is sent as a message to the
-/// supervisor task.
-#[utoipa::path(
- post,
- path = "/api/v1/mesh/red-team/notify",
- request_body = RedTeamNotifyRequest,
- responses(
- (status = 200, description = "Notification sent", body = RedTeamNotifyResponse),
- (status = 401, description = "Unauthorized - tool key required"),
- (status = 403, description = "Forbidden - not a red team task"),
- (status = 404, description = "Task not found"),
- (status = 503, description = "Database not available"),
- (status = 500, description = "Internal server error"),
- ),
- security(
- ("tool_key" = [])
- ),
- tag = "Mesh Red Team"
-)]
-pub async fn notify_supervisor(
- State(state): State<SharedState>,
- headers: HeaderMap,
- Json(request): Json<RedTeamNotifyRequest>,
-) -> impl IntoResponse {
- let (red_team_task_id, owner_id, contract_id) =
- match verify_red_team_auth(&state, &headers).await {
- Ok(ids) => ids,
- Err(e) => return e.into_response(),
- };
-
- let pool = state.db_pool.as_ref().unwrap();
-
- // Generate notification ID
- let notification_id = Uuid::new_v4();
-
- // Get the contract to find the supervisor task
- let contract = match repository::get_contract_for_owner(pool, contract_id, owner_id).await {
- Ok(Some(c)) => c,
- Ok(None) => {
- return (
- StatusCode::NOT_FOUND,
- Json(ApiError::new("NOT_FOUND", "Contract not found")),
- )
- .into_response();
- }
- Err(e) => {
- tracing::error!(error = %e, "Failed to get contract");
- return (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(ApiError::new("DB_ERROR", "Failed to get contract")),
- )
- .into_response();
- }
- };
-
- let supervisor_task_id = contract.supervisor_task_id;
-
- // Format the alert message
- let alert_message = format_alert_message(
- &request.severity,
- &request.message,
- request.related_task_id,
- request.file_path.as_deref(),
- request.context.as_deref(),
- );
-
- // Record the notification in the database as a history event
- let event_data = serde_json::json!({
- "notification_id": notification_id.to_string(),
- "red_team_task_id": red_team_task_id.to_string(),
- "severity": request.severity.to_string(),
- "message": request.message,
- "related_task_id": request.related_task_id.map(|id| id.to_string()),
- "file_path": request.file_path,
- "context": request.context,
- });
-
- let _ = repository::record_history_event(
- pool,
- owner_id,
- Some(contract_id),
- Some(red_team_task_id),
- "red_team_alert",
- Some(&request.severity.to_string().to_lowercase()),
- Some(&request.message),
- event_data,
- )
- .await;
-
- // Try to send the message to the supervisor
- let mut delivered = false;
- if let Some(sup_task_id) = supervisor_task_id {
- // Get the supervisor task to find its daemon
- if let Ok(Some(supervisor_task)) = repository::get_task(pool, sup_task_id).await {
- if let Some(daemon_id) = supervisor_task.daemon_id {
- // Send the alert message to the supervisor
- let cmd = DaemonCommand::SendMessage {
- task_id: sup_task_id,
- message: alert_message.clone(),
- };
-
- if let Err(e) = state.send_daemon_command(daemon_id, cmd).await {
- tracing::warn!(
- error = %e,
- supervisor_task_id = %sup_task_id,
- daemon_id = %daemon_id,
- "Failed to send red team alert to supervisor"
- );
- } else {
- delivered = true;
- tracing::info!(
- notification_id = %notification_id,
- red_team_task_id = %red_team_task_id,
- supervisor_task_id = %sup_task_id,
- severity = %request.severity,
- "Red team alert delivered to supervisor"
- );
- }
- } else {
- tracing::warn!(
- supervisor_task_id = %sup_task_id,
- "Supervisor task has no assigned daemon - alert not delivered"
- );
- }
- }
- } else {
- tracing::warn!(
- contract_id = %contract_id,
- "Contract has no supervisor task - alert not delivered"
- );
- }
-
- (
- StatusCode::OK,
- Json(RedTeamNotifyResponse {
- notification_id,
- delivered,
- supervisor_task_id,
- }),
- )
- .into_response()
-}
-
-/// Get the status of the red team task.
-///
-/// GET /api/v1/mesh/red-team/status
-///
-/// Returns information about the current red team task including the contract
-/// being monitored and notification statistics.
-#[utoipa::path(
- get,
- path = "/api/v1/mesh/red-team/status",
- responses(
- (status = 200, description = "Red team status", body = RedTeamStatusResponse),
- (status = 401, description = "Unauthorized - tool key required"),
- (status = 403, description = "Forbidden - not a red team task"),
- (status = 404, description = "Task not found"),
- (status = 503, description = "Database not available"),
- (status = 500, description = "Internal server error"),
- ),
- security(
- ("tool_key" = [])
- ),
- tag = "Mesh Red Team"
-)]
-pub async fn get_status(
- State(state): State<SharedState>,
- headers: HeaderMap,
-) -> impl IntoResponse {
- let (red_team_task_id, owner_id, contract_id) =
- match verify_red_team_auth(&state, &headers).await {
- Ok(ids) => ids,
- Err(e) => return e.into_response(),
- };
-
- let pool = state.db_pool.as_ref().unwrap();
-
- // Get the red team task status
- let task = match repository::get_task(pool, red_team_task_id).await {
- Ok(Some(t)) => t,
- Ok(None) => {
- return (
- StatusCode::NOT_FOUND,
- Json(ApiError::new("NOT_FOUND", "Red team task not found")),
- )
- .into_response();
- }
- Err(e) => {
- tracing::error!(error = %e, "Failed to get red team task");
- return (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(ApiError::new("DB_ERROR", "Failed to get task")),
- )
- .into_response();
- }
- };
-
- // Count notifications sent by this red team task
- // Query history_events for red_team_alert events from this task
- let notifications_sent = match sqlx::query_scalar::<_, i64>(
- r#"
- SELECT COUNT(*)
- FROM history_events
- WHERE owner_id = $1
- AND contract_id = $2
- AND task_id = $3
- AND event_type = 'red_team_alert'
- "#,
- )
- .bind(owner_id)
- .bind(contract_id)
- .bind(red_team_task_id)
- .fetch_one(pool)
- .await
- {
- Ok(count) => count,
- Err(e) => {
- tracing::warn!(error = %e, "Failed to count red team notifications");
- 0
- }
- };
-
- (
- StatusCode::OK,
- Json(RedTeamStatusResponse {
- contract_id,
- red_team_task_id,
- status: task.status,
- notifications_sent,
- }),
- )
- .into_response()
-}
diff --git a/makima/src/server/handlers/mesh_supervisor.rs b/makima/src/server/handlers/mesh_supervisor.rs
index a29b666..43388a8 100644
--- a/makima/src/server/handlers/mesh_supervisor.rs
+++ b/makima/src/server/handlers/mesh_supervisor.rs
@@ -37,10 +37,6 @@ pub struct SpawnTaskRequest {
pub checkpoint_sha: Option<String>,
/// Repository URL for the task (optional - if not provided, will be looked up from contract).
pub repository_url: Option<String>,
- /// If true, create a separate worktree for the task (requires merge after).
- /// If false (default), the task shares the supervisor's worktree.
- #[serde(default)]
- pub use_own_worktree: bool,
}
/// Request to wait for task completion.
@@ -610,8 +606,8 @@ pub async fn spawn_task(
}
// Create task request
- // Share supervisor's worktree by default; separate worktree only when explicitly requested
- let supervisor_worktree_task_id = if request.use_own_worktree { None } else { Some(supervisor_id) };
+ // All tasks share the supervisor's worktree
+ let supervisor_worktree_task_id = Some(supervisor_id);
let create_req = CreateTaskRequest {
name: request.name.clone(),
@@ -621,7 +617,6 @@ pub async fn spawn_task(
contract_id: Some(request.contract_id),
parent_task_id: request.parent_task_id,
is_supervisor: false,
- is_red_team: false,
checkpoint_sha: request.checkpoint_sha.clone(),
merge_mode: Some("manual".to_string()),
priority: 0,
@@ -733,8 +728,8 @@ pub async fn spawn_task(
patch_base_sha: None,
local_only: contract.local_only,
auto_merge_local: contract.auto_merge_local,
- // Share supervisor's worktree by default; separate worktree only when explicitly requested
- supervisor_worktree_task_id: if request.use_own_worktree { None } else { Some(supervisor_id) },
+ // All tasks share the supervisor's worktree
+ supervisor_worktree_task_id: Some(supervisor_id),
};
if let Err(e) = state.send_daemon_command(daemon.id, cmd).await {
@@ -762,66 +757,6 @@ pub async fn spawn_task(
updated_by: "supervisor".to_string(),
});
- // Check if we should spawn a red team task
- // Conditions:
- // 1. This is not a supervisor task
- // 2. This is not already a red team task
- // 3. Contract has red_team_enabled = true
- // 4. No red team task exists for this contract yet
- if !updated_task.is_supervisor && !updated_task.is_red_team && contract.red_team_enabled {
- if let Some(contract_id) = updated_task.contract_id {
- // Check if a red team task already exists
- match repository::get_red_team_task_for_contract(pool, contract_id).await {
- Ok(None) => {
- // No red team task exists, spawn one
- tracing::info!(
- contract_id = %contract_id,
- work_task_id = %updated_task.id,
- "Spawning red team task for contract (first work task started)"
- );
- match spawn_red_team_task(
- pool,
- &state,
- contract_id,
- owner_id,
- &contract.name,
- &contract.phase,
- contract.red_team_prompt.as_deref(),
- ).await {
- Ok(red_team_task) => {
- tracing::info!(
- contract_id = %contract_id,
- red_team_task_id = %red_team_task.id,
- "Red team task spawned successfully"
- );
- }
- Err(e) => {
- // Log error but don't fail the work task spawn
- tracing::error!(
- contract_id = %contract_id,
- error = %e,
- "Failed to spawn red team task"
- );
- }
- }
- }
- Ok(Some(existing)) => {
- tracing::debug!(
- contract_id = %contract_id,
- red_team_task_id = %existing.id,
- "Red team task already exists for contract"
- );
- }
- Err(e) => {
- tracing::error!(
- contract_id = %contract_id,
- error = %e,
- "Error checking for existing red team task"
- );
- }
- }
- }
- }
}
break;
}
@@ -2584,239 +2519,6 @@ pub async fn rewind_conversation(
}
// =============================================================================
-// Red Team Task Spawning
-// =============================================================================
-
-/// Generate the system prompt/plan for a red team task.
-///
-/// This creates detailed instructions for the red team monitor, including
-/// what to look for, severity levels, and how to report issues.
-pub fn generate_red_team_plan(
- contract_name: &str,
- contract_phase: &str,
- custom_prompt: Option<&str>,
-) -> String {
- let custom_criteria = if let Some(prompt) = custom_prompt {
- format!(
- r#"
-
-## Custom Review Criteria
-
-The contract owner has specified additional review criteria:
-{}
-"#,
- prompt
- )
- } else {
- String::new()
- };
-
- format!(
- r#"# Red Team Monitor
-
-You are an adversarial quality reviewer for a software development contract. Your role is to monitor work task outputs in real-time and flag potential issues BEFORE they compound into larger problems.
-
-## Your Mission
-
-Monitor all task outputs and verify:
-1. **Plan Adherence**: Are tasks following the implementation plan?
-2. **Code Quality**: Does the code meet repository standards?
-3. **Contract Requirements**: Does the implementation match the specification?
-4. **Best Practices**: Are there obvious anti-patterns or issues?
-
-## Access Available
-
-You have read-only access to:
-- Task outputs (streamed in real-time)
-- Task diffs (code changes)
-- Contract specifications and plan documents
-- Repository configuration files (CONTRIBUTING.md, linting configs, etc.)
-
-## How to Monitor
-
-1. **Subscribe to task outputs**: You'll receive outputs from all work tasks
-2. **Analyze code changes**: Request diffs for completed tasks
-3. **Cross-reference**: Compare outputs against the plan and specifications
-4. **Report issues**: Use `makima red-team notify` when you detect problems
-
-## When to Notify
-
-NOTIFY the supervisor when you observe:
-- **Critical**: Security vulnerabilities, data loss risks, breaking changes
-- **High**: Significant deviations from the plan, major code quality issues
-- **Medium**: Missing tests, suboptimal implementations, minor standard violations
-- **Low**: Style inconsistencies, documentation gaps (use sparingly)
-
-## What NOT to Do
-
-- Do NOT nitpick minor style issues (that's what linters are for)
-- Do NOT block progress for trivial concerns
-- Do NOT write code or make changes yourself
-- Do NOT notify for things that are already in progress and being addressed
-- Do NOT create duplicate notifications for the same issue
-
-## Notification Format
-
-When notifying, always include:
-1. A clear, concise description of the issue
-2. The severity level (critical/high/medium/low)
-3. The related task ID if applicable
-4. The specific file or code location if known
-5. Why this matters (reference to plan, spec, or standards)
-
-## Example Notification
-
-```
-makima red-team notify "Task is implementing authentication with plaintext password storage, which contradicts the security requirements in the specification document" \
- --severity critical \
- --task <task_id> \
- --file "src/auth/user.rs" \
- --context "Specification section 3.2 requires bcrypt hashing for all passwords"
-```
-{}
-## Contract Context
-
-Contract: {}
-Phase: {}
-
-Focus your monitoring on outputs that relate to the active work tasks. Prioritize issues that could affect the success of the contract or introduce technical debt.
-"#,
- custom_criteria, contract_name, contract_phase
- )
-}
-
-/// Spawn a red team task for a contract.
-///
-/// This creates a red team monitor task that will observe work task outputs
-/// and can notify the supervisor about potential issues.
-pub async fn spawn_red_team_task(
- pool: &sqlx::PgPool,
- state: &SharedState,
- contract_id: Uuid,
- owner_id: Uuid,
- contract_name: &str,
- contract_phase: &str,
- red_team_prompt: Option<&str>,
-) -> Result<Task, String> {
- // Generate the red team plan/prompt
- let plan = generate_red_team_plan(contract_name, contract_phase, red_team_prompt);
-
- // Create task request
- let create_req = CreateTaskRequest {
- name: "Red Team Monitor".to_string(),
- description: Some("Adversarial review task monitoring work task outputs".to_string()),
- plan,
- contract_id: Some(contract_id),
- parent_task_id: None,
- is_supervisor: false,
- is_red_team: true,
- priority: 0,
- repository_url: None, // Red team doesn't need a repo
- base_branch: None,
- target_branch: None,
- merge_mode: None,
- target_repo_path: None,
- completion_action: None,
- continue_from_task_id: None,
- copy_files: None,
- checkpoint_sha: None,
- branched_from_task_id: None,
- conversation_history: None,
- supervisor_worktree_task_id: None, // Red team uses its own working area
- };
-
- // Create task in DB
- let task = repository::create_task_for_owner(pool, owner_id, create_req)
- .await
- .map_err(|e| format!("Failed to create red team task: {}", e))?;
-
- tracing::info!(
- contract_id = %contract_id,
- red_team_task_id = %task.id,
- "Created red team task for contract"
- );
-
- // Find a daemon to run the red team task
- for entry in state.daemon_connections.iter() {
- let daemon = entry.value();
- if daemon.owner_id == owner_id {
- // Update task with daemon assignment
- let update_req = UpdateTaskRequest {
- status: Some("starting".to_string()),
- daemon_id: Some(daemon.id),
- version: Some(task.version),
- ..Default::default()
- };
-
- match repository::update_task_for_owner(pool, task.id, owner_id, update_req).await {
- Ok(Some(updated_task)) => {
- // Send spawn command to daemon
- let cmd = DaemonCommand::SpawnTask {
- task_id: updated_task.id,
- task_name: updated_task.name.clone(),
- plan: updated_task.plan.clone(),
- repo_url: None, // Red team doesn't need a repo
- base_branch: None,
- target_branch: None,
- parent_task_id: None,
- depth: 0,
- is_orchestrator: false,
- target_repo_path: None,
- completion_action: None,
- continue_from_task_id: None,
- copy_files: None,
- contract_id: Some(contract_id),
- is_supervisor: false,
- autonomous_loop: false,
- resume_session: false,
- conversation_history: None,
- patch_data: None,
- patch_base_sha: None,
- local_only: true, // Red team is always local-only
- auto_merge_local: false, // Red team doesn't auto-merge
- supervisor_worktree_task_id: None,
- };
-
- if let Err(e) = state.send_daemon_command(daemon.id, cmd).await {
- tracing::warn!(
- error = %e,
- daemon_id = %daemon.id,
- red_team_task_id = %task.id,
- "Failed to send red team spawn command"
- );
- // Rollback
- let rollback_req = UpdateTaskRequest {
- status: Some("pending".to_string()),
- clear_daemon_id: true,
- ..Default::default()
- };
- let _ = repository::update_task_for_owner(pool, task.id, owner_id, rollback_req).await;
- } else {
- tracing::info!(
- red_team_task_id = %task.id,
- daemon_id = %daemon.id,
- "Red team task spawn command sent"
- );
- return Ok(updated_task);
- }
- }
- Ok(None) => {
- tracing::warn!(red_team_task_id = %task.id, "Red team task not found when updating daemon_id");
- }
- Err(e) => {
- tracing::error!(red_team_task_id = %task.id, error = %e, "Failed to update red team task with daemon_id");
- }
- }
- break;
- }
- }
-
- // Return the task even if we couldn't start it on a daemon
- // It will remain pending and can be started later
- Ok(task)
-}
-
-// =============================================================================
// Supervisor State Persistence Helpers (Task 3.3)
// =============================================================================
diff --git a/makima/src/server/handlers/mod.rs b/makima/src/server/handlers/mod.rs
index 8af2a37..a14c4f7 100644
--- a/makima/src/server/handlers/mod.rs
+++ b/makima/src/server/handlers/mod.rs
@@ -13,7 +13,6 @@ pub mod mesh;
pub mod mesh_chat;
pub mod mesh_daemon;
pub mod mesh_merge;
-pub mod mesh_red_team;
pub mod mesh_supervisor;
pub mod mesh_ws;
pub mod repository_history;
diff --git a/makima/src/server/handlers/templates.rs b/makima/src/server/handlers/templates.rs
index 0cc5657..aa97876 100644
--- a/makima/src/server/handlers/templates.rs
+++ b/makima/src/server/handlers/templates.rs
@@ -1,27 +1,19 @@
//! Contract types API handler.
+//! Only returns built-in contract types (simple, specification, execute).
use axum::{
- extract::{Path, State},
http::StatusCode,
response::IntoResponse,
Json,
};
use serde::Serialize;
use utoipa::ToSchema;
-use uuid::Uuid;
-use crate::db::models::{
- ContractTypeTemplateRecord, ContractTypeTemplateSummary, CreateTemplateRequest,
- UpdateTemplateRequest,
-};
-use crate::db::repository;
use crate::llm::templates;
use crate::llm::templates::ContractTypeTemplate;
-use crate::server::auth::{Authenticated, MaybeAuthenticated};
-use crate::server::state::SharedState;
// =============================================================================
-// Contract Type Templates (Workflow Definitions)
+// Contract Type Templates (Built-in Only)
// =============================================================================
/// Response for listing contract types
@@ -31,14 +23,7 @@ pub struct ListContractTypesResponse {
pub contract_types: Vec<ContractTypeTemplate>,
}
-/// Response for a single custom template
-#[derive(Debug, Serialize, ToSchema)]
-#[serde(rename_all = "camelCase")]
-pub struct TemplateResponse {
- pub template: ContractTypeTemplateSummary,
-}
-
-/// List all available contract type templates (built-in + custom)
+/// List all available contract type templates (built-in only)
#[utoipa::path(
get,
path = "/api/v1/contract-types",
@@ -47,404 +32,12 @@ pub struct TemplateResponse {
),
tag = "templates"
)]
-pub async fn list_contract_types(
- State(state): State<SharedState>,
- MaybeAuthenticated(auth): MaybeAuthenticated,
-) -> impl IntoResponse {
- // Start with built-in types
- let mut contract_types = templates::all_contract_types();
-
- // If authenticated, also fetch custom templates for this owner
- if let Some(user) = auth {
- if let Some(ref pool) = state.db_pool {
- if let Ok(custom_templates) =
- repository::list_templates_for_owner(pool, user.owner_id).await
- {
- for template in custom_templates {
- contract_types.push(template_record_to_api(&template));
- }
- }
- }
- }
-
+pub async fn list_contract_types() -> impl IntoResponse {
+ // Only return built-in types (simple, specification, execute)
+ let contract_types = templates::all_contract_types();
(
StatusCode::OK,
Json(ListContractTypesResponse { contract_types }),
)
.into_response()
}
-
-/// Create a new custom contract type template
-#[utoipa::path(
- post,
- path = "/api/v1/contract-types",
- request_body = CreateTemplateRequest,
- responses(
- (status = 201, description = "Template created successfully", body = TemplateResponse),
- (status = 400, description = "Invalid request"),
- (status = 401, description = "Unauthorized"),
- (status = 409, description = "Template with this name already exists")
- ),
- tag = "templates"
-)]
-pub async fn create_template(
- State(state): State<SharedState>,
- Authenticated(auth): Authenticated,
- Json(req): Json<CreateTemplateRequest>,
-) -> impl IntoResponse {
- let Some(ref pool) = state.db_pool else {
- return (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(serde_json::json!({
- "code": "DB_ERROR",
- "message": "Database not configured"
- })),
- )
- .into_response();
- };
-
- // Validate request
- if req.name.trim().is_empty() {
- return (
- StatusCode::BAD_REQUEST,
- Json(serde_json::json!({
- "code": "INVALID_REQUEST",
- "message": "Template name cannot be empty"
- })),
- )
- .into_response();
- }
-
- if req.phases.is_empty() {
- return (
- StatusCode::BAD_REQUEST,
- Json(serde_json::json!({
- "code": "INVALID_REQUEST",
- "message": "Template must have at least one phase"
- })),
- )
- .into_response();
- }
-
- // Validate default_phase is in the phases list
- if !req.phases.iter().any(|p| p.id == req.default_phase) {
- return (
- StatusCode::BAD_REQUEST,
- Json(serde_json::json!({
- "code": "INVALID_REQUEST",
- "message": format!("Default phase '{}' is not in the phases list", req.default_phase)
- })),
- )
- .into_response();
- }
-
- // Check that template name doesn't conflict with built-in types
- let builtin_names = ["simple", "specification", "execute"];
- if builtin_names.contains(&req.name.to_lowercase().as_str()) {
- return (
- StatusCode::CONFLICT,
- Json(serde_json::json!({
- "code": "NAME_CONFLICT",
- "message": "Cannot create a template with the same name as a built-in type"
- })),
- )
- .into_response();
- }
-
- match repository::create_template_for_owner(pool, auth.owner_id, req).await {
- Ok(template) => (
- StatusCode::CREATED,
- Json(serde_json::json!({
- "template": template_record_to_summary(&template)
- })),
- )
- .into_response(),
- Err(e) => {
- // Check for unique constraint violation
- let error_str = e.to_string();
- if error_str.contains("unique") || error_str.contains("duplicate") {
- return (
- StatusCode::CONFLICT,
- Json(serde_json::json!({
- "code": "NAME_CONFLICT",
- "message": "A template with this name already exists"
- })),
- )
- .into_response();
- }
- (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(serde_json::json!({
- "code": "DB_ERROR",
- "message": format!("Failed to create template: {}", e)
- })),
- )
- .into_response()
- }
- }
-}
-
-/// Get a specific contract type template by ID
-#[utoipa::path(
- get,
- path = "/api/v1/contract-types/{id}",
- params(
- ("id" = Uuid, Path, description = "Template ID")
- ),
- responses(
- (status = 200, description = "Template retrieved successfully", body = TemplateResponse),
- (status = 401, description = "Unauthorized"),
- (status = 404, description = "Template not found")
- ),
- tag = "templates"
-)]
-pub async fn get_template(
- State(state): State<SharedState>,
- Authenticated(auth): Authenticated,
- Path(id): Path<Uuid>,
-) -> impl IntoResponse {
- let Some(ref pool) = state.db_pool else {
- return (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(serde_json::json!({
- "code": "DB_ERROR",
- "message": "Database not configured"
- })),
- )
- .into_response();
- };
-
- match repository::get_template_for_owner(pool, id, auth.owner_id).await {
- Ok(Some(template)) => (
- StatusCode::OK,
- Json(serde_json::json!({
- "template": template_record_to_summary(&template)
- })),
- )
- .into_response(),
- Ok(None) => (
- StatusCode::NOT_FOUND,
- Json(serde_json::json!({
- "code": "NOT_FOUND",
- "message": "Template not found"
- })),
- )
- .into_response(),
- Err(e) => (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(serde_json::json!({
- "code": "DB_ERROR",
- "message": format!("Failed to get template: {}", e)
- })),
- )
- .into_response(),
- }
-}
-
-/// Update a contract type template
-#[utoipa::path(
- put,
- path = "/api/v1/contract-types/{id}",
- params(
- ("id" = Uuid, Path, description = "Template ID")
- ),
- request_body = UpdateTemplateRequest,
- responses(
- (status = 200, description = "Template updated successfully", body = TemplateResponse),
- (status = 400, description = "Invalid request"),
- (status = 401, description = "Unauthorized"),
- (status = 404, description = "Template not found"),
- (status = 409, description = "Version conflict")
- ),
- tag = "templates"
-)]
-pub async fn update_template(
- State(state): State<SharedState>,
- Authenticated(auth): Authenticated,
- Path(id): Path<Uuid>,
- Json(req): Json<UpdateTemplateRequest>,
-) -> impl IntoResponse {
- let Some(ref pool) = state.db_pool else {
- return (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(serde_json::json!({
- "code": "DB_ERROR",
- "message": "Database not configured"
- })),
- )
- .into_response();
- };
-
- // Validate phases if provided
- if let Some(ref phases) = req.phases {
- if phases.is_empty() {
- return (
- StatusCode::BAD_REQUEST,
- Json(serde_json::json!({
- "code": "INVALID_REQUEST",
- "message": "Template must have at least one phase"
- })),
- )
- .into_response();
- }
-
- // If default_phase is also provided, validate it's in the phases
- if let Some(ref default_phase) = req.default_phase {
- if !phases.iter().any(|p| &p.id == default_phase) {
- return (
- StatusCode::BAD_REQUEST,
- Json(serde_json::json!({
- "code": "INVALID_REQUEST",
- "message": format!("Default phase '{}' is not in the phases list", default_phase)
- })),
- )
- .into_response();
- }
- }
- }
-
- // Check that template name doesn't conflict with built-in types
- if let Some(ref name) = req.name {
- let builtin_names = ["simple", "specification", "execute"];
- if builtin_names.contains(&name.to_lowercase().as_str()) {
- return (
- StatusCode::CONFLICT,
- Json(serde_json::json!({
- "code": "NAME_CONFLICT",
- "message": "Cannot rename template to a built-in type name"
- })),
- )
- .into_response();
- }
- }
-
- match repository::update_template_for_owner(pool, id, auth.owner_id, req).await {
- Ok(Some(template)) => (
- StatusCode::OK,
- Json(serde_json::json!({
- "template": template_record_to_summary(&template)
- })),
- )
- .into_response(),
- Ok(None) => (
- StatusCode::NOT_FOUND,
- Json(serde_json::json!({
- "code": "NOT_FOUND",
- "message": "Template not found"
- })),
- )
- .into_response(),
- Err(repository::RepositoryError::VersionConflict { expected, actual }) => (
- StatusCode::CONFLICT,
- Json(serde_json::json!({
- "code": "VERSION_CONFLICT",
- "message": format!("Version conflict: expected {}, found {}", expected, actual),
- "expectedVersion": expected,
- "actualVersion": actual
- })),
- )
- .into_response(),
- Err(e) => {
- let error_str = e.to_string();
- if error_str.contains("unique") || error_str.contains("duplicate") {
- return (
- StatusCode::CONFLICT,
- Json(serde_json::json!({
- "code": "NAME_CONFLICT",
- "message": "A template with this name already exists"
- })),
- )
- .into_response();
- }
- (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(serde_json::json!({
- "code": "DB_ERROR",
- "message": format!("Failed to update template: {}", e)
- })),
- )
- .into_response()
- }
- }
-}
-
-/// Delete a contract type template
-#[utoipa::path(
- delete,
- path = "/api/v1/contract-types/{id}",
- params(
- ("id" = Uuid, Path, description = "Template ID")
- ),
- responses(
- (status = 204, description = "Template deleted successfully"),
- (status = 401, description = "Unauthorized"),
- (status = 404, description = "Template not found")
- ),
- tag = "templates"
-)]
-pub async fn delete_template(
- State(state): State<SharedState>,
- Authenticated(auth): Authenticated,
- Path(id): Path<Uuid>,
-) -> impl IntoResponse {
- let Some(ref pool) = state.db_pool else {
- return (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(serde_json::json!({
- "code": "DB_ERROR",
- "message": "Database not configured"
- })),
- )
- .into_response();
- };
-
- match repository::delete_template_for_owner(pool, id, auth.owner_id).await {
- Ok(true) => StatusCode::NO_CONTENT.into_response(),
- Ok(false) => (
- StatusCode::NOT_FOUND,
- Json(serde_json::json!({
- "code": "NOT_FOUND",
- "message": "Template not found"
- })),
- )
- .into_response(),
- Err(e) => (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(serde_json::json!({
- "code": "DB_ERROR",
- "message": format!("Failed to delete template: {}", e)
- })),
- )
- .into_response(),
- }
-}
-
-// =============================================================================
-// Helper Functions
-// =============================================================================
-
-/// Convert a database template record to the API template format
-fn template_record_to_api(template: &ContractTypeTemplateRecord) -> ContractTypeTemplate {
- ContractTypeTemplate {
- id: template.id.to_string(),
- name: template.name.clone(),
- description: template.description.clone().unwrap_or_default(),
- phases: template.phases.iter().map(|p| p.id.clone()).collect(),
- default_phase: template.default_phase.clone(),
- is_builtin: false,
- }
-}
-
-/// Convert a database template record to the summary format
-fn template_record_to_summary(template: &ContractTypeTemplateRecord) -> ContractTypeTemplateSummary {
- ContractTypeTemplateSummary {
- id: template.id,
- name: template.name.clone(),
- description: template.description.clone(),
- phases: template.phases.clone(),
- default_phase: template.default_phase.clone(),
- is_builtin: false,
- version: template.version,
- created_at: template.created_at,
- }
-}
diff --git a/makima/src/server/handlers/transcript_analysis.rs b/makima/src/server/handlers/transcript_analysis.rs
index d987d08..62c65a6 100644
--- a/makima/src/server/handlers/transcript_analysis.rs
+++ b/makima/src/server/handlers/transcript_analysis.rs
@@ -280,8 +280,6 @@ pub async fn create_contract_from_analysis(
phase_guard: None,
local_only: None,
auto_merge_local: None,
- red_team_enabled: None,
- red_team_prompt: None,
template_id: None,
};
@@ -362,7 +360,6 @@ pub async fn create_contract_from_analysis(
continue_from_task_id: None,
copy_files: None,
is_supervisor: false,
- is_red_team: false,
checkpoint_sha: None,
priority: match item.priority.as_deref() {
Some("high") => 10,
@@ -537,7 +534,6 @@ pub async fn update_contract_from_analysis(
continue_from_task_id: None,
copy_files: None,
is_supervisor: false,
- is_red_team: false,
checkpoint_sha: None,
priority: 0,
merge_mode: None,
diff --git a/makima/src/server/mod.rs b/makima/src/server/mod.rs
index e5415ae..b351ac1 100644
--- a/makima/src/server/mod.rs
+++ b/makima/src/server/mod.rs
@@ -18,7 +18,7 @@ use tower_http::trace::TraceLayer;
use utoipa::OpenApi;
use utoipa_swagger_ui::SwaggerUi;
-use crate::server::handlers::{api_keys, chat, contract_chat, contract_daemon, contracts, file_ws, files, history, listen, mesh, mesh_chat, mesh_daemon, mesh_merge, mesh_red_team, mesh_supervisor, mesh_ws, repository_history, speak, templates, transcript_analysis, users, versions};
+use crate::server::handlers::{api_keys, chat, contract_chat, contract_daemon, contracts, file_ws, files, history, listen, mesh, mesh_chat, mesh_daemon, mesh_merge, mesh_supervisor, mesh_ws, repository_history, speak, templates, transcript_analysis, users, versions};
use crate::server::openapi::ApiDoc;
use crate::server::state::SharedState;
@@ -132,9 +132,6 @@ pub fn make_router(state: SharedState) -> Router {
.route("/mesh/supervisor/questions", post(mesh_supervisor::ask_question))
.route("/mesh/questions", get(mesh_supervisor::list_pending_questions))
.route("/mesh/questions/{question_id}/answer", post(mesh_supervisor::answer_question))
- // Red team endpoints (for red team tasks to notify supervisors)
- .route("/mesh/red-team/notify", post(mesh_red_team::notify_supervisor))
- .route("/mesh/red-team/status", get(mesh_red_team::get_status))
// Mesh WebSocket endpoints
.route("/mesh/tasks/subscribe", get(mesh_ws::task_subscription_handler))
.route("/mesh/daemons/connect", get(mesh_daemon::daemon_handler))
@@ -216,17 +213,8 @@ pub fn make_router(state: SharedState) -> Router {
)
// Timeline endpoint (unified history for user)
.route("/timeline", get(history::get_timeline))
- // Contract type templates (workflow definitions)
- .route(
- "/contract-types",
- get(templates::list_contract_types).post(templates::create_template),
- )
- .route(
- "/contract-types/{id}",
- get(templates::get_template)
- .put(templates::update_template)
- .delete(templates::delete_template),
- )
+ // Contract type templates (built-in only)
+ .route("/contract-types", get(templates::list_contract_types))
// Settings endpoints
.route(
"/settings/repository-history",