decision_cockpit 0.1.0

Layer — product decision memory with MCP tools and an embedded review dashboard
Documentation
use sqlx::PgPool;
use uuid::Uuid;

use crate::domain::drift::DriftSignal;

pub struct NewDriftSignal<'a> {
    pub drift_type: &'a str,
    pub target_entity_id: Uuid,
    pub target_entity_type: &'a str,
    pub summary: &'a str,
    pub severity: &'a str,
    pub explanation: &'a str,
}

pub async fn create(pool: &PgPool, new: NewDriftSignal<'_>) -> Result<DriftSignal, sqlx::Error> {
    let id = Uuid::new_v4();
    sqlx::query_as::<_, DriftSignal>(
        r#"
        INSERT INTO drift_signals
            (id, drift_type, target_entity_id, target_entity_type, summary, severity, explanation, status)
        VALUES ($1, $2, $3, $4, $5, $6, $7, 'open')
        RETURNING id, drift_type, target_entity_id, target_entity_type, summary, severity,
                  explanation, status, created_at
        "#,
    )
    .bind(id)
    .bind(new.drift_type)
    .bind(new.target_entity_id)
    .bind(new.target_entity_type)
    .bind(new.summary)
    .bind(new.severity)
    .bind(new.explanation)
    .fetch_one(pool)
    .await
}

pub async fn get(pool: &PgPool, id: Uuid) -> Result<Option<DriftSignal>, sqlx::Error> {
    sqlx::query_as::<_, DriftSignal>(
        r#"
        SELECT id, drift_type, target_entity_id, target_entity_type, summary, severity,
               explanation, status, created_at
        FROM drift_signals
        WHERE id = $1
        "#,
    )
    .bind(id)
    .fetch_optional(pool)
    .await
}

pub async fn list(
    pool: &PgPool,
    status: Option<&str>,
    limit: i64,
) -> Result<Vec<DriftSignal>, sqlx::Error> {
    sqlx::query_as::<_, DriftSignal>(
        r#"
        SELECT id, drift_type, target_entity_id, target_entity_type, summary, severity,
               explanation, status, created_at
        FROM drift_signals
        WHERE ($1::text IS NULL OR status = $1)
        ORDER BY created_at DESC
        LIMIT $2
        "#,
    )
    .bind(status)
    .bind(limit)
    .fetch_all(pool)
    .await
}

pub async fn list_for_entity(
    pool: &PgPool,
    entity_id: Uuid,
) -> Result<Vec<DriftSignal>, sqlx::Error> {
    sqlx::query_as::<_, DriftSignal>(
        r#"
        SELECT id, drift_type, target_entity_id, target_entity_type, summary, severity,
               explanation, status, created_at
        FROM drift_signals
        WHERE target_entity_id = $1
        ORDER BY created_at DESC
        "#,
    )
    .bind(entity_id)
    .fetch_all(pool)
    .await
}

pub async fn update_status(
    pool: &PgPool,
    id: Uuid,
    status: &str,
) -> Result<Option<DriftSignal>, sqlx::Error> {
    sqlx::query_as::<_, DriftSignal>(
        r#"
        UPDATE drift_signals
        SET status = $2
        WHERE id = $1
        RETURNING id, drift_type, target_entity_id, target_entity_type, summary, severity,
                  explanation, status, created_at
        "#,
    )
    .bind(id)
    .bind(status)
    .fetch_optional(pool)
    .await
}