From 6cd5b20670d7ecd3d48539ff898e021988f2a503 Mon Sep 17 00:00:00 2001 From: soryu Date: Tue, 27 Jan 2026 01:05:25 +0000 Subject: Add Red Team adversarial review system for contract monitoring (#35) Implements a parallel "red team" task that monitors work task outputs in real-time, verifying implementations stick to contract requirements, repository standards, and the execution plan. Key features: - New `red_team_enabled` and `red_team_prompt` contract configuration - Red team tasks auto-spawn when first work task is created - `makima red-team notify` CLI command for alerting supervisors - POST /api/v1/mesh/red-team/notify and /status endpoints - Alert delivery to supervisor via SendMessage daemon command - Notification audit trail via history_events table Database changes: - Add red_team_enabled/red_team_prompt columns to contracts - Add is_red_team flag to tasks with partial index - Create red_team_notifications table for audit logging Co-authored-by: Claude Opus 4.5 --- makima/src/db/repository.rs | 125 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 112 insertions(+), 13 deletions(-) (limited to 'makima/src/db/repository.rs') 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 Result Result, 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, + file_path: Option<&str>, + context: Option<&str>, +) -> Result { + 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 { + 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, 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 { + 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) +} -- cgit v1.2.3