diff options
| author | soryu <soryu@soryu.co> | 2026-01-26 23:57:27 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-01-26 23:57:27 +0000 |
| commit | 2f90424509faa3e7f06146efd73c406d8f6bb3b6 (patch) | |
| tree | 138453e694ce243241adc5704dc53c4491f8a2be /makima/src | |
| parent | f6b4d06a0158fb7803a2d7a861cf891cb3b202b4 (diff) | |
| download | soryu-2f90424509faa3e7f06146efd73c406d8f6bb3b6.tar.gz soryu-2f90424509faa3e7f06146efd73c406d8f6bb3b6.zip | |
[WIP] Heartbeat checkpoint - 2026-01-26 23:57:27 UTC
Diffstat (limited to 'makima/src')
| -rw-r--r-- | makima/src/db/models.rs | 44 | ||||
| -rw-r--r-- | makima/src/db/repository.rs | 125 |
2 files changed, 156 insertions, 13 deletions
diff --git a/makima/src/db/models.rs b/makima/src/db/models.rs index 9c2d072..8207ba2 100644 --- a/makima/src/db/models.rs +++ b/makima/src/db/models.rs @@ -441,6 +441,11 @@ pub struct Task { #[serde(default)] pub is_supervisor: bool, + // Red team flag + /// True for red team tasks that monitor and review other tasks' work. + #[serde(default)] + pub is_red_team: bool, + // Daemon/container info pub daemon_id: Option<Uuid>, pub container_id: Option<String>, @@ -570,6 +575,9 @@ pub struct TaskSummary { /// True for contract supervisor tasks #[serde(default)] pub is_supervisor: bool, + /// True for red team tasks that monitor and review other tasks' work + #[serde(default)] + pub is_red_team: bool, /// Whether this task is hidden from the UI (user dismissed it) #[serde(default)] pub hidden: bool, @@ -595,6 +603,7 @@ impl From<Task> for TaskSummary { subtask_count: 0, // Would need separate query version: task.version, is_supervisor: task.is_supervisor, + is_red_team: task.is_red_team, hidden: task.hidden, created_at: task.created_at, updated_at: task.updated_at, @@ -627,6 +636,9 @@ pub struct CreateTaskRequest { /// True for contract supervisor tasks. Only supervisors can spawn new tasks. #[serde(default)] pub is_supervisor: bool, + /// True for red team tasks that monitor and review other tasks' work. + #[serde(default)] + pub is_red_team: bool, /// Priority (higher = more urgent) #[serde(default)] pub priority: i32, @@ -2074,3 +2086,35 @@ pub struct CheckpointPatchInfo { pub created_at: DateTime<Utc>, pub expires_at: DateTime<Utc>, } + +// ============================================================================ +// Red Team Notifications +// ============================================================================ + +/// A notification from a red team task to the contract supervisor. +/// Red team tasks monitor implementation work and send alerts when issues are detected. +#[derive(Debug, Clone, FromRow, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct RedTeamNotification { + pub id: Uuid, + /// The contract this notification relates to + pub contract_id: Uuid, + /// The red team task that created this notification + pub red_team_task_id: Uuid, + /// The task being reviewed (if applicable) + pub related_task_id: Option<Uuid>, + /// The notification message + pub message: String, + /// Severity level: "info", "warning", "error", "critical" + pub severity: String, + /// File path being reviewed (if applicable) + pub file_path: Option<String>, + /// Additional context (code snippet, reason, etc.) + pub context: Option<String>, + /// Whether this notification has been delivered to the supervisor + #[serde(default)] + pub delivered: bool, + /// When the notification was delivered + pub delivered_at: Option<DateTime<Utc>>, + pub created_at: DateTime<Utc>, +} diff --git a/makima/src/db/repository.rs b/makima/src/db/repository.rs index 6d6642b..de1712d 100644 --- a/makima/src/db/repository.rs +++ b/makima/src/db/repository.rs @@ -11,8 +11,8 @@ use super::models::{ ConversationMessage, ConversationSnapshot, CreateContractRequest, CreateFileRequest, CreateTaskRequest, Daemon, DaemonTaskAssignment, DaemonWithCapacity, File, FileSummary, FileVersion, HistoryEvent, HistoryQueryFilters, MeshChatConversation, MeshChatMessageRecord, - SupervisorState, Task, TaskCheckpoint, TaskEvent, TaskSummary, UpdateContractRequest, - UpdateFileRequest, UpdateTaskRequest, + RedTeamNotification, SupervisorState, Task, TaskCheckpoint, TaskEvent, TaskSummary, + UpdateContractRequest, UpdateFileRequest, UpdateTaskRequest, }; /// Repository error types. @@ -689,11 +689,11 @@ pub async fn create_task(pool: &PgPool, req: CreateTaskRequest) -> Result<Task, r#" INSERT INTO tasks ( contract_id, parent_task_id, depth, name, description, plan, priority, - is_supervisor, repository_url, base_branch, target_branch, merge_mode, + is_supervisor, is_red_team, repository_url, base_branch, target_branch, merge_mode, target_repo_path, completion_action, continue_from_task_id, copy_files, branched_from_task_id, conversation_state ) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19) RETURNING * "#, ) @@ -705,6 +705,7 @@ pub async fn create_task(pool: &PgPool, req: CreateTaskRequest) -> Result<Task, .bind(&req.plan) .bind(req.priority) .bind(req.is_supervisor) + .bind(req.is_red_team) .bind(&repo_url) .bind(&base_branch) .bind(&target_branch) @@ -744,7 +745,8 @@ pub async fn list_tasks(pool: &PgPool) -> Result<Vec<TaskSummary>, sqlx::Error> t.parent_task_id, t.depth, t.name, t.status, t.priority, t.progress_summary, (SELECT COUNT(*) FROM tasks WHERE parent_task_id = t.id) as subtask_count, - t.version, t.is_supervisor, COALESCE(t.hidden, false) as hidden, t.created_at, t.updated_at + t.version, t.is_supervisor, COALESCE(t.is_red_team, false) as is_red_team, + COALESCE(t.hidden, false) as hidden, t.created_at, t.updated_at FROM tasks t LEFT JOIN contracts c ON t.contract_id = c.id WHERE t.parent_task_id IS NULL AND COALESCE(t.hidden, false) = false @@ -765,7 +767,8 @@ pub async fn list_subtasks(pool: &PgPool, parent_id: Uuid) -> Result<Vec<TaskSum t.parent_task_id, t.depth, t.name, t.status, t.priority, t.progress_summary, (SELECT COUNT(*) FROM tasks WHERE parent_task_id = t.id) as subtask_count, - t.version, t.is_supervisor, COALESCE(t.hidden, false) as hidden, t.created_at, t.updated_at + t.version, t.is_supervisor, COALESCE(t.is_red_team, false) as is_red_team, + COALESCE(t.hidden, false) as hidden, t.created_at, t.updated_at FROM tasks t LEFT JOIN contracts c ON t.contract_id = c.id WHERE t.parent_task_id = $1 @@ -1100,11 +1103,11 @@ pub async fn create_task_for_owner( r#" INSERT INTO tasks ( owner_id, contract_id, parent_task_id, depth, name, description, plan, priority, - is_supervisor, repository_url, base_branch, target_branch, merge_mode, + is_supervisor, is_red_team, repository_url, base_branch, target_branch, merge_mode, target_repo_path, completion_action, continue_from_task_id, copy_files, branched_from_task_id, conversation_state ) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20) RETURNING * "#, ) @@ -1117,6 +1120,7 @@ pub async fn create_task_for_owner( .bind(&req.plan) .bind(req.priority) .bind(req.is_supervisor) + .bind(req.is_red_team) .bind(&repo_url) .bind(&base_branch) .bind(&target_branch) @@ -1164,7 +1168,8 @@ pub async fn list_tasks_for_owner( t.parent_task_id, t.depth, t.name, t.status, t.priority, t.progress_summary, (SELECT COUNT(*) FROM tasks WHERE parent_task_id = t.id) as subtask_count, - t.version, t.is_supervisor, COALESCE(t.hidden, false) as hidden, t.created_at, t.updated_at + t.version, t.is_supervisor, COALESCE(t.is_red_team, false) as is_red_team, + COALESCE(t.hidden, false) as hidden, t.created_at, t.updated_at FROM tasks t LEFT JOIN contracts c ON t.contract_id = c.id WHERE t.owner_id = $1 AND t.parent_task_id IS NULL AND COALESCE(t.hidden, false) = false @@ -1190,7 +1195,8 @@ pub async fn list_subtasks_for_owner( t.parent_task_id, t.depth, t.name, t.status, t.priority, t.progress_summary, (SELECT COUNT(*) FROM tasks WHERE parent_task_id = t.id) as subtask_count, - t.version, t.is_supervisor, COALESCE(t.hidden, false) as hidden, t.created_at, t.updated_at + t.version, t.is_supervisor, COALESCE(t.is_red_team, false) as is_red_team, + COALESCE(t.hidden, false) as hidden, t.created_at, t.updated_at FROM tasks t LEFT JOIN contracts c ON t.contract_id = c.id WHERE t.owner_id = $1 AND t.parent_task_id = $2 @@ -1711,7 +1717,8 @@ pub async fn list_sibling_tasks( t.parent_task_id, t.depth, t.name, t.status, t.priority, t.progress_summary, (SELECT COUNT(*) FROM tasks WHERE parent_task_id = t.id) as subtask_count, - t.version, t.is_supervisor, t.created_at, t.updated_at + t.version, t.is_supervisor, COALESCE(t.is_red_team, false) as is_red_team, + COALESCE(t.hidden, false) as hidden, t.created_at, t.updated_at FROM tasks t LEFT JOIN contracts c ON t.contract_id = c.id WHERE t.parent_task_id = $1 AND t.id != $2 @@ -1733,7 +1740,8 @@ pub async fn list_sibling_tasks( t.parent_task_id, t.depth, t.name, t.status, t.priority, t.progress_summary, (SELECT COUNT(*) FROM tasks WHERE parent_task_id = t.id) as subtask_count, - t.version, t.is_supervisor, t.created_at, t.updated_at + t.version, t.is_supervisor, COALESCE(t.is_red_team, false) as is_red_team, + COALESCE(t.hidden, false) as hidden, t.created_at, t.updated_at FROM tasks t LEFT JOIN contracts c ON t.contract_id = c.id WHERE t.parent_task_id IS NULL AND t.id != $1 @@ -2716,7 +2724,8 @@ pub async fn list_tasks_in_contract( t.parent_task_id, t.depth, t.name, t.status, t.priority, t.progress_summary, (SELECT COUNT(*) FROM tasks WHERE parent_task_id = t.id) as subtask_count, - t.version, t.is_supervisor, COALESCE(t.hidden, false) as hidden, t.created_at, t.updated_at + t.version, t.is_supervisor, COALESCE(t.is_red_team, false) as is_red_team, + COALESCE(t.hidden, false) as hidden, t.created_at, t.updated_at FROM tasks t LEFT JOIN contracts c ON t.contract_id = c.id WHERE t.contract_id = $1 AND t.owner_id = $2 @@ -3906,3 +3915,93 @@ pub async fn delete_checkpoint_patches_for_task( .await?; Ok(result.rows_affected() as i64) } + +// ============================================================================= +// Red Team Notifications +// ============================================================================= + +/// Create a red team notification. +/// Red team tasks use this to report issues found during implementation review. +pub async fn create_red_team_notification( + pool: &PgPool, + contract_id: Uuid, + red_team_task_id: Uuid, + message: &str, + severity: &str, + related_task_id: Option<Uuid>, + file_path: Option<&str>, + context: Option<&str>, +) -> Result<RedTeamNotification, RepositoryError> { + sqlx::query_as::<_, RedTeamNotification>( + r#" + INSERT INTO red_team_notifications + (contract_id, red_team_task_id, related_task_id, message, severity, file_path, context) + VALUES ($1, $2, $3, $4, $5, $6, $7) + RETURNING * + "#, + ) + .bind(contract_id) + .bind(red_team_task_id) + .bind(related_task_id) + .bind(message) + .bind(severity) + .bind(file_path) + .bind(context) + .fetch_one(pool) + .await + .map_err(RepositoryError::Database) +} + +/// Mark a notification as delivered to the supervisor. +pub async fn mark_notification_delivered( + pool: &PgPool, + notification_id: Uuid, +) -> Result<RedTeamNotification, RepositoryError> { + sqlx::query_as::<_, RedTeamNotification>( + r#" + UPDATE red_team_notifications + SET delivered = TRUE, delivered_at = NOW() + WHERE id = $1 + RETURNING * + "#, + ) + .bind(notification_id) + .fetch_one(pool) + .await + .map_err(RepositoryError::Database) +} + +/// Get the red team task for a contract (if one exists). +/// Returns the most recently created red team task for the contract. +pub async fn get_red_team_task_for_contract( + pool: &PgPool, + contract_id: Uuid, +) -> Result<Option<Task>, RepositoryError> { + sqlx::query_as::<_, Task>( + r#" + SELECT * FROM tasks + WHERE contract_id = $1 AND is_red_team = TRUE + ORDER BY created_at DESC + LIMIT 1 + "#, + ) + .bind(contract_id) + .fetch_optional(pool) + .await + .map_err(RepositoryError::Database) +} + +/// Get the count of notifications for a red team task. +pub async fn get_notification_count_for_task( + pool: &PgPool, + red_team_task_id: Uuid, +) -> Result<i64, RepositoryError> { + let result: (i64,) = sqlx::query_as( + "SELECT COUNT(*) FROM red_team_notifications WHERE red_team_task_id = $1", + ) + .bind(red_team_task_id) + .fetch_one(pool) + .await + .map_err(RepositoryError::Database)?; + Ok(result.0) +} |
