summaryrefslogtreecommitdiff
path: root/makima/src/db/repository.rs
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-02-07 00:01:50 +0000
committersoryu <soryu@soryu.co>2026-02-07 00:01:50 +0000
commitb8d563d45f14a2b1db1f684aa0a8dcd7e5b6ad56 (patch)
tree95543fd150270018e384fbcf9d3df3dc45f052f6 /makima/src/db/repository.rs
parentcececbf326e258211ceae7afce716a5d1e46014f (diff)
downloadsoryu-b8d563d45f14a2b1db1f684aa0a8dcd7e5b6ad56.tar.gz
soryu-b8d563d45f14a2b1db1f684aa0a8dcd7e5b6ad56.zip
Remove directives for reimplementation
Diffstat (limited to 'makima/src/db/repository.rs')
-rw-r--r--makima/src/db/repository.rs1169
1 files changed, 0 insertions, 1169 deletions
diff --git a/makima/src/db/repository.rs b/makima/src/db/repository.rs
index cd806f0..863d927 100644
--- a/makima/src/db/repository.rs
+++ b/makima/src/db/repository.rs
@@ -6,7 +6,6 @@ use sqlx::PgPool;
use uuid::Uuid;
use super::models::{
- // Core types
CheckpointPatch, CheckpointPatchInfo, Contract, ContractChatConversation,
ContractChatMessageRecord, ContractEvent, ContractRepository, ContractSummary,
ContractTypeTemplateRecord, ConversationMessage, ConversationSnapshot,
@@ -17,11 +16,6 @@ use super::models::{
PhaseDefinition, SupervisorHeartbeatRecord, SupervisorState, Task, TaskCheckpoint,
TaskEvent, TaskSummary, UpdateContractRequest, UpdateFileRequest, UpdateTaskRequest,
UpdateTemplateRequest,
- // Directive types
- AddStepRequest, ChainStep, CreateDirectiveRequest, Directive, DirectiveApproval,
- DirectiveChain, DirectiveChainGraphEdge, DirectiveChainGraphNode, DirectiveChainGraphResponse,
- DirectiveEvaluation, DirectiveEvent, DirectiveSummary, DirectiveVerifier,
- DirectiveWithProgress, UpdateDirectiveRequest, UpdateStepRequest,
};
/// Repository error types.
@@ -4905,1169 +4899,6 @@ pub async fn sync_supervisor_state(
}
// =============================================================================
-// Directive Operations (top-level orchestration entity)
-// =============================================================================
-// TODO: Implement directive CRUD functions
-// - create_directive_for_owner
-// - get_directive_for_owner
-// - list_directives_for_owner
-// - update_directive_for_owner
-// - archive_directive_for_owner
-// - update_directive_status
-
-// =============================================================================
-// Directive Chain Operations (generated execution plans)
-// =============================================================================
-// TODO: Implement chain CRUD functions
-// - create_directive_chain
-// - get_current_chain
-// - supersede_chain
-
-// =============================================================================
-// Chain Step Operations (nodes in the DAG)
-// =============================================================================
-// TODO: Implement step CRUD functions
-// - create_chain_step
-// - update_chain_step
-// - delete_chain_step
-// - find_ready_steps
-// - update_step_status
-// - update_step_contract
-// - update_step_confidence
-// - increment_step_rework_count
-
-// =============================================================================
-// Directive Evaluation Operations
-// =============================================================================
-// TODO: Implement evaluation functions
-// - create_directive_evaluation
-// - list_step_evaluations
-// - list_directive_evaluations
-
-// =============================================================================
-// Directive Event Operations (audit stream)
-// =============================================================================
-// TODO: Implement event functions
-// - emit_directive_event
-// - list_directive_events
-
-// =============================================================================
-// Directive Verifier Operations
-// =============================================================================
-// TODO: Implement verifier CRUD functions
-// - create_directive_verifier
-// - list_directive_verifiers
-// - update_directive_verifier
-
-// =============================================================================
-// Directive Approval Operations (human-in-the-loop)
-// =============================================================================
-// TODO: Implement approval functions
-// - create_approval_request
-// - resolve_approval
-// - list_pending_approvals
-
-// NOTE: Old chain functions removed. See git history for reference.
-// Old functions included: create_chain_for_owner, get_chain_for_owner,
-// list_chains_for_owner, update_chain_for_owner, delete_chain_for_owner,
-// add_contract_to_chain, remove_contract_from_chain, list_chain_contracts,
-// get_chain_with_contracts, list_chain_repositories, add_chain_repository,
-// delete_chain_repository, set_chain_repository_primary, get_chain_graph,
-// record_chain_event, list_chain_events, increment_chain_loop, complete_chain,
-// get_ready_chain_contracts, is_chain_complete, get_chain_editor_data,
-// create_chain_contract_definition, list_chain_contract_definitions,
-// update_chain_contract_definition, delete_chain_contract_definition,
-// get_chain_definition_graph, update_chain_status, progress_chain,
-// create_chain_directive, get_chain_directive, update_chain_directive,
-// delete_chain_directive, create_contract_evaluation, get_contract_evaluation,
-// list_chain_evaluations, update_chain_contract_evaluation_status,
-// mark_chain_contract_original_completion, get_chain_contract_by_contract_id,
-// init_chain_for_owner.
-
-// =============================================================================
-// Directive Operations
-// =============================================================================
-
-/// Create a new directive for an owner.
-pub async fn create_directive_for_owner(
- pool: &PgPool,
- owner_id: Uuid,
- req: CreateDirectiveRequest,
-) -> Result<Directive, sqlx::Error> {
- let title = req.title.unwrap_or_else(|| truncate_string(&req.goal, 100));
- let autonomy_level = req.autonomy_level.unwrap_or_else(|| "guardrails".to_string());
- let green_threshold = req.confidence_threshold_green.unwrap_or(0.85);
- let yellow_threshold = req.confidence_threshold_yellow.unwrap_or(0.60);
- let requirements = req.requirements.unwrap_or(serde_json::json!([]));
- let acceptance_criteria = req.acceptance_criteria.unwrap_or(serde_json::json!([]));
-
- sqlx::query_as::<_, Directive>(
- r#"
- INSERT INTO directives (
- owner_id, title, goal, requirements, acceptance_criteria,
- autonomy_level, confidence_threshold_green, confidence_threshold_yellow,
- repository_url, local_path, base_branch,
- max_total_cost_usd, max_wall_time_minutes
- )
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
- RETURNING *
- "#,
- )
- .bind(owner_id)
- .bind(&title)
- .bind(&req.goal)
- .bind(&requirements)
- .bind(&acceptance_criteria)
- .bind(&autonomy_level)
- .bind(green_threshold)
- .bind(yellow_threshold)
- .bind(&req.repository_url)
- .bind(&req.local_path)
- .bind(&req.base_branch)
- .bind(req.max_total_cost_usd)
- .bind(req.max_wall_time_minutes)
- .fetch_one(pool)
- .await
-}
-
-/// Get a directive by ID, scoped to owner.
-pub async fn get_directive_for_owner(
- pool: &PgPool,
- id: Uuid,
- owner_id: Uuid,
-) -> Result<Option<Directive>, sqlx::Error> {
- sqlx::query_as::<_, Directive>(
- r#"
- SELECT * FROM directives WHERE id = $1 AND owner_id = $2
- "#,
- )
- .bind(id)
- .bind(owner_id)
- .fetch_optional(pool)
- .await
-}
-
-/// Get a directive by ID (no owner check - for internal use).
-pub async fn get_directive(pool: &PgPool, id: Uuid) -> Result<Option<Directive>, sqlx::Error> {
- sqlx::query_as::<_, Directive>(
- r#"SELECT * FROM directives WHERE id = $1"#,
- )
- .bind(id)
- .fetch_optional(pool)
- .await
-}
-
-/// List directives for an owner.
-pub async fn list_directives_for_owner(
- pool: &PgPool,
- owner_id: Uuid,
- status_filter: Option<&str>,
-) -> Result<Vec<DirectiveSummary>, sqlx::Error> {
- let query = if let Some(status) = status_filter {
- sqlx::query_as::<_, DirectiveSummary>(
- r#"
- SELECT
- d.id, d.title, d.goal, d.status, d.autonomy_level,
- dc.current_confidence,
- COALESCE(dc.completed_steps, 0) as completed_steps,
- COALESCE(dc.total_steps, 0) as total_steps,
- d.chain_generation_count, d.started_at, d.created_at
- FROM directives d
- LEFT JOIN directive_chains dc ON dc.id = d.current_chain_id
- WHERE d.owner_id = $1 AND d.status = $2
- ORDER BY d.created_at DESC
- "#,
- )
- .bind(owner_id)
- .bind(status)
- } else {
- sqlx::query_as::<_, DirectiveSummary>(
- r#"
- SELECT
- d.id, d.title, d.goal, d.status, d.autonomy_level,
- dc.current_confidence,
- COALESCE(dc.completed_steps, 0) as completed_steps,
- COALESCE(dc.total_steps, 0) as total_steps,
- d.chain_generation_count, d.started_at, d.created_at
- FROM directives d
- LEFT JOIN directive_chains dc ON dc.id = d.current_chain_id
- WHERE d.owner_id = $1
- ORDER BY d.created_at DESC
- "#,
- )
- .bind(owner_id)
- };
- query.fetch_all(pool).await
-}
-
-/// Update a directive with optimistic locking.
-pub async fn update_directive_for_owner(
- pool: &PgPool,
- id: Uuid,
- owner_id: Uuid,
- req: UpdateDirectiveRequest,
-) -> Result<Directive, RepositoryError> {
- // First get current version
- let current = sqlx::query_scalar::<_, i32>(
- "SELECT version FROM directives WHERE id = $1 AND owner_id = $2"
- )
- .bind(id)
- .bind(owner_id)
- .fetch_optional(pool)
- .await?
- .ok_or_else(|| RepositoryError::Database(sqlx::Error::RowNotFound))?;
-
- if current != req.version {
- return Err(RepositoryError::VersionConflict {
- expected: req.version,
- actual: current,
- });
- }
-
- let directive = sqlx::query_as::<_, Directive>(
- r#"
- UPDATE directives SET
- title = COALESCE($3, title),
- goal = COALESCE($4, goal),
- requirements = COALESCE($5, requirements),
- acceptance_criteria = COALESCE($6, acceptance_criteria),
- constraints = COALESCE($7, constraints),
- external_dependencies = COALESCE($8, external_dependencies),
- autonomy_level = COALESCE($9, autonomy_level),
- confidence_threshold_green = COALESCE($10, confidence_threshold_green),
- confidence_threshold_yellow = COALESCE($11, confidence_threshold_yellow),
- max_total_cost_usd = COALESCE($12, max_total_cost_usd),
- max_wall_time_minutes = COALESCE($13, max_wall_time_minutes),
- max_rework_cycles = COALESCE($14, max_rework_cycles),
- max_chain_regenerations = COALESCE($15, max_chain_regenerations),
- version = version + 1,
- updated_at = NOW()
- WHERE id = $1 AND owner_id = $2 AND version = $16
- RETURNING *
- "#,
- )
- .bind(id)
- .bind(owner_id)
- .bind(&req.title)
- .bind(&req.goal)
- .bind(&req.requirements)
- .bind(&req.acceptance_criteria)
- .bind(&req.constraints)
- .bind(&req.external_dependencies)
- .bind(&req.autonomy_level)
- .bind(req.confidence_threshold_green)
- .bind(req.confidence_threshold_yellow)
- .bind(req.max_total_cost_usd)
- .bind(req.max_wall_time_minutes)
- .bind(req.max_rework_cycles)
- .bind(req.max_chain_regenerations)
- .bind(req.version)
- .fetch_one(pool)
- .await?;
-
- Ok(directive)
-}
-
-/// Update directive status.
-pub async fn update_directive_status(
- pool: &PgPool,
- id: Uuid,
- status: &str,
-) -> Result<Directive, sqlx::Error> {
- sqlx::query_as::<_, Directive>(
- r#"
- UPDATE directives SET
- status = $2,
- started_at = CASE WHEN $2 = 'active' AND started_at IS NULL THEN NOW() ELSE started_at END,
- completed_at = CASE WHEN $2 IN ('completed', 'failed', 'archived') THEN NOW() ELSE completed_at END,
- updated_at = NOW()
- WHERE id = $1
- RETURNING *
- "#,
- )
- .bind(id)
- .bind(status)
- .fetch_one(pool)
- .await
-}
-
-/// Set the orchestrator contract ID for a directive.
-pub async fn set_directive_orchestrator_contract(
- pool: &PgPool,
- directive_id: Uuid,
- contract_id: Uuid,
-) -> Result<(), sqlx::Error> {
- sqlx::query(
- r#"
- UPDATE directives SET orchestrator_contract_id = $2, updated_at = NOW()
- WHERE id = $1
- "#,
- )
- .bind(directive_id)
- .bind(contract_id)
- .execute(pool)
- .await?;
- Ok(())
-}
-
-/// Find a directive by its orchestrator contract ID.
-pub async fn get_directive_by_orchestrator_contract_id(
- pool: &PgPool,
- contract_id: Uuid,
-) -> Result<Option<Directive>, sqlx::Error> {
- sqlx::query_as::<_, Directive>(
- r#"
- SELECT * FROM directives WHERE orchestrator_contract_id = $1
- "#,
- )
- .bind(contract_id)
- .fetch_optional(pool)
- .await
-}
-
-/// Archive a directive (soft delete).
-pub async fn archive_directive_for_owner(
- pool: &PgPool,
- id: Uuid,
- owner_id: Uuid,
-) -> Result<bool, sqlx::Error> {
- let result = sqlx::query(
- r#"
- UPDATE directives SET status = 'archived', updated_at = NOW()
- WHERE id = $1 AND owner_id = $2
- "#,
- )
- .bind(id)
- .bind(owner_id)
- .execute(pool)
- .await?;
- Ok(result.rows_affected() > 0)
-}
-
-/// Get directive with full progress info.
-pub async fn get_directive_with_progress(
- pool: &PgPool,
- id: Uuid,
- owner_id: Uuid,
-) -> Result<Option<DirectiveWithProgress>, sqlx::Error> {
- let directive = match get_directive_for_owner(pool, id, owner_id).await? {
- Some(d) => d,
- None => return Ok(None),
- };
-
- let chain = if let Some(chain_id) = directive.current_chain_id {
- get_directive_chain(pool, chain_id).await?
- } else {
- None
- };
-
- let steps = if let Some(ref c) = chain {
- list_chain_steps(pool, c.id).await?
- } else {
- vec![]
- };
-
- let recent_events = list_directive_events(pool, id, Some(20)).await?;
- let pending_approvals = list_pending_approvals(pool, id).await?;
-
- Ok(Some(DirectiveWithProgress {
- directive,
- chain,
- steps,
- recent_events,
- pending_approvals,
- }))
-}
-
-// =============================================================================
-// Directive Chain Operations
-// =============================================================================
-
-/// Create a new chain generation for a directive.
-pub async fn create_directive_chain(
- pool: &PgPool,
- directive_id: Uuid,
- name: &str,
- description: Option<&str>,
- rationale: Option<&str>,
- planning_model: Option<&str>,
-) -> Result<DirectiveChain, sqlx::Error> {
- // Get next generation number
- let generation = sqlx::query_scalar::<_, i32>(
- "SELECT COALESCE(MAX(generation), 0) + 1 FROM directive_chains WHERE directive_id = $1"
- )
- .bind(directive_id)
- .fetch_one(pool)
- .await?;
-
- let chain = sqlx::query_as::<_, DirectiveChain>(
- r#"
- INSERT INTO directive_chains (directive_id, generation, name, description, rationale, planning_model)
- VALUES ($1, $2, $3, $4, $5, $6)
- RETURNING *
- "#,
- )
- .bind(directive_id)
- .bind(generation)
- .bind(name)
- .bind(description)
- .bind(rationale)
- .bind(planning_model)
- .fetch_one(pool)
- .await?;
-
- // Update directive to point to new chain and increment generation count
- sqlx::query(
- r#"
- UPDATE directives SET
- current_chain_id = $2,
- chain_generation_count = chain_generation_count + 1,
- updated_at = NOW()
- WHERE id = $1
- "#,
- )
- .bind(directive_id)
- .bind(chain.id)
- .execute(pool)
- .await?;
-
- Ok(chain)
-}
-
-/// Get a directive chain by ID.
-pub async fn get_directive_chain(pool: &PgPool, id: Uuid) -> Result<Option<DirectiveChain>, sqlx::Error> {
- sqlx::query_as::<_, DirectiveChain>(
- "SELECT * FROM directive_chains WHERE id = $1"
- )
- .bind(id)
- .fetch_optional(pool)
- .await
-}
-
-/// Get the current chain for a directive.
-pub async fn get_current_chain(pool: &PgPool, directive_id: Uuid) -> Result<Option<DirectiveChain>, sqlx::Error> {
- sqlx::query_as::<_, DirectiveChain>(
- r#"
- SELECT dc.* FROM directive_chains dc
- JOIN directives d ON d.current_chain_id = dc.id
- WHERE d.id = $1
- "#,
- )
- .bind(directive_id)
- .fetch_optional(pool)
- .await
-}
-
-/// Update chain status.
-pub async fn update_chain_status(
- pool: &PgPool,
- chain_id: Uuid,
- status: &str,
-) -> Result<DirectiveChain, sqlx::Error> {
- sqlx::query_as::<_, DirectiveChain>(
- r#"
- UPDATE directive_chains SET
- status = $2,
- started_at = CASE WHEN $2 = 'active' AND started_at IS NULL THEN NOW() ELSE started_at END,
- completed_at = CASE WHEN $2 IN ('completed', 'failed', 'superseded') THEN NOW() ELSE completed_at END,
- updated_at = NOW()
- WHERE id = $1
- RETURNING *
- "#,
- )
- .bind(chain_id)
- .bind(status)
- .fetch_one(pool)
- .await
-}
-
-/// Supersede a chain (mark as superseded and update directive).
-pub async fn supersede_chain(pool: &PgPool, chain_id: Uuid) -> Result<(), sqlx::Error> {
- sqlx::query(
- r#"
- UPDATE directive_chains SET status = 'superseded', completed_at = NOW(), updated_at = NOW()
- WHERE id = $1
- "#,
- )
- .bind(chain_id)
- .execute(pool)
- .await?;
- Ok(())
-}
-
-// =============================================================================
-// Chain Step Operations
-// =============================================================================
-
-/// Create a new step in a chain.
-pub async fn create_chain_step(
- pool: &PgPool,
- chain_id: Uuid,
- req: AddStepRequest,
-) -> Result<ChainStep, sqlx::Error> {
- let step_type = req.step_type.unwrap_or_else(|| "execute".to_string());
- let contract_type = req.contract_type.unwrap_or_else(|| "simple".to_string());
- let phases = req.phases.unwrap_or_default();
- let depends_on = req.depends_on.unwrap_or_default();
- let requirement_ids = req.requirement_ids.unwrap_or_default();
- let acceptance_criteria_ids = req.acceptance_criteria_ids.unwrap_or_default();
- let verifier_config = req.verifier_config.unwrap_or(serde_json::json!({}));
-
- // Get next order index
- let order_index = sqlx::query_scalar::<_, i32>(
- "SELECT COALESCE(MAX(order_index), 0) + 1 FROM chain_steps WHERE chain_id = $1"
- )
- .bind(chain_id)
- .fetch_one(pool)
- .await?;
-
- let step = sqlx::query_as::<_, ChainStep>(
- r#"
- INSERT INTO chain_steps (
- chain_id, name, description, step_type, contract_type,
- initial_phase, task_plan, phases, depends_on, parallel_group,
- requirement_ids, acceptance_criteria_ids, verifier_config,
- editor_x, editor_y, order_index
- )
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
- RETURNING *
- "#,
- )
- .bind(chain_id)
- .bind(&req.name)
- .bind(&req.description)
- .bind(&step_type)
- .bind(&contract_type)
- .bind(&req.initial_phase)
- .bind(&req.task_plan)
- .bind(&phases)
- .bind(&depends_on)
- .bind(&req.parallel_group)
- .bind(&requirement_ids)
- .bind(&acceptance_criteria_ids)
- .bind(&verifier_config)
- .bind(req.editor_x.unwrap_or(0.0))
- .bind(req.editor_y.unwrap_or(0.0))
- .bind(order_index)
- .fetch_one(pool)
- .await?;
-
- // Update chain total_steps count
- sqlx::query(
- "UPDATE directive_chains SET total_steps = total_steps + 1, updated_at = NOW() WHERE id = $1"
- )
- .bind(chain_id)
- .execute(pool)
- .await?;
-
- Ok(step)
-}
-
-/// Get a chain step by ID.
-pub async fn get_chain_step(pool: &PgPool, id: Uuid) -> Result<Option<ChainStep>, sqlx::Error> {
- sqlx::query_as::<_, ChainStep>(
- "SELECT * FROM chain_steps WHERE id = $1"
- )
- .bind(id)
- .fetch_optional(pool)
- .await
-}
-
-/// List all steps in a chain.
-pub async fn list_chain_steps(pool: &PgPool, chain_id: Uuid) -> Result<Vec<ChainStep>, sqlx::Error> {
- sqlx::query_as::<_, ChainStep>(
- "SELECT * FROM chain_steps WHERE chain_id = $1 ORDER BY order_index"
- )
- .bind(chain_id)
- .fetch_all(pool)
- .await
-}
-
-/// Update a chain step.
-pub async fn update_chain_step(
- pool: &PgPool,
- step_id: Uuid,
- req: UpdateStepRequest,
-) -> Result<ChainStep, sqlx::Error> {
- sqlx::query_as::<_, ChainStep>(
- r#"
- UPDATE chain_steps SET
- name = COALESCE($2, name),
- description = COALESCE($3, description),
- task_plan = COALESCE($4, task_plan),
- depends_on = COALESCE($5, depends_on),
- requirement_ids = COALESCE($6, requirement_ids),
- acceptance_criteria_ids = COALESCE($7, acceptance_criteria_ids),
- verifier_config = COALESCE($8, verifier_config),
- editor_x = COALESCE($9, editor_x),
- editor_y = COALESCE($10, editor_y)
- WHERE id = $1
- RETURNING *
- "#,
- )
- .bind(step_id)
- .bind(&req.name)
- .bind(&req.description)
- .bind(&req.task_plan)
- .bind(&req.depends_on)
- .bind(&req.requirement_ids)
- .bind(&req.acceptance_criteria_ids)
- .bind(&req.verifier_config)
- .bind(req.editor_x)
- .bind(req.editor_y)
- .fetch_one(pool)
- .await
-}
-
-/// Delete a chain step.
-pub async fn delete_chain_step(pool: &PgPool, step_id: Uuid) -> Result<bool, sqlx::Error> {
- // Get chain_id first for updating count
- let chain_id = sqlx::query_scalar::<_, Uuid>(
- "SELECT chain_id FROM chain_steps WHERE id = $1"
- )
- .bind(step_id)
- .fetch_optional(pool)
- .await?;
-
- let result = sqlx::query("DELETE FROM chain_steps WHERE id = $1")
- .bind(step_id)
- .execute(pool)
- .await?;
-
- // Update chain total_steps count
- if let Some(cid) = chain_id {
- sqlx::query(
- "UPDATE directive_chains SET total_steps = total_steps - 1, updated_at = NOW() WHERE id = $1"
- )
- .bind(cid)
- .execute(pool)
- .await?;
- }
-
- Ok(result.rows_affected() > 0)
-}
-
-/// Find steps that are ready to execute (all dependencies met, status=pending).
-pub async fn find_ready_steps(pool: &PgPool, chain_id: Uuid) -> Result<Vec<ChainStep>, sqlx::Error> {
- sqlx::query_as::<_, ChainStep>(
- r#"
- SELECT s.* FROM chain_steps s
- WHERE s.chain_id = $1
- AND s.status = 'pending'
- AND NOT EXISTS (
- SELECT 1 FROM chain_steps dep
- WHERE dep.id = ANY(s.depends_on)
- AND dep.status NOT IN ('passed', 'skipped')
- )
- ORDER BY s.order_index
- "#,
- )
- .bind(chain_id)
- .fetch_all(pool)
- .await
-}
-
-/// Update step status.
-pub async fn update_step_status(
- pool: &PgPool,
- step_id: Uuid,
- status: &str,
-) -> Result<ChainStep, sqlx::Error> {
- let step = sqlx::query_as::<_, ChainStep>(
- r#"
- UPDATE chain_steps SET
- status = $2,
- started_at = CASE WHEN $2 = 'running' AND started_at IS NULL THEN NOW() ELSE started_at END,
- completed_at = CASE WHEN $2 IN ('passed', 'failed', 'skipped') THEN NOW() ELSE completed_at END
- WHERE id = $1
- RETURNING *
- "#,
- )
- .bind(step_id)
- .bind(status)
- .fetch_one(pool)
- .await?;
-
- // Update chain completed_steps and failed_steps counts
- if status == "passed" || status == "skipped" {
- sqlx::query(
- "UPDATE directive_chains SET completed_steps = completed_steps + 1, updated_at = NOW() WHERE id = $1"
- )
- .bind(step.chain_id)
- .execute(pool)
- .await?;
- } else if status == "failed" {
- sqlx::query(
- "UPDATE directive_chains SET failed_steps = failed_steps + 1, updated_at = NOW() WHERE id = $1"
- )
- .bind(step.chain_id)
- .execute(pool)
- .await?;
- }
-
- Ok(step)
-}
-
-/// Link a step to a contract.
-pub async fn update_step_contract(
- pool: &PgPool,
- step_id: Uuid,
- contract_id: Uuid,
- supervisor_task_id: Option<Uuid>,
-) -> Result<ChainStep, sqlx::Error> {
- sqlx::query_as::<_, ChainStep>(
- r#"
- UPDATE chain_steps SET contract_id = $2, supervisor_task_id = $3
- WHERE id = $1
- RETURNING *
- "#,
- )
- .bind(step_id)
- .bind(contract_id)
- .bind(supervisor_task_id)
- .fetch_one(pool)
- .await
-}
-
-/// Update step confidence score and level.
-pub async fn update_step_confidence(
- pool: &PgPool,
- step_id: Uuid,
- score: f64,
- level: &str,
- evaluation_id: Uuid,
-) -> Result<ChainStep, sqlx::Error> {
- sqlx::query_as::<_, ChainStep>(
- r#"
- UPDATE chain_steps SET
- confidence_score = $2,
- confidence_level = $3,
- last_evaluation_id = $4,
- evaluation_count = evaluation_count + 1
- WHERE id = $1
- RETURNING *
- "#,
- )
- .bind(step_id)
- .bind(score)
- .bind(level)
- .bind(evaluation_id)
- .fetch_one(pool)
- .await
-}
-
-/// Increment step rework count.
-pub async fn increment_step_rework_count(pool: &PgPool, step_id: Uuid) -> Result<ChainStep, sqlx::Error> {
- sqlx::query_as::<_, ChainStep>(
- r#"
- UPDATE chain_steps SET rework_count = rework_count + 1, status = 'rework'
- WHERE id = $1
- RETURNING *
- "#,
- )
- .bind(step_id)
- .fetch_one(pool)
- .await
-}
-
-/// Get chain graph for visualization.
-pub async fn get_chain_graph(
- pool: &PgPool,
- chain_id: Uuid,
-) -> Result<DirectiveChainGraphResponse, sqlx::Error> {
- let chain = get_directive_chain(pool, chain_id).await?
- .ok_or_else(|| sqlx::Error::RowNotFound)?;
-
- let steps = list_chain_steps(pool, chain_id).await?;
-
- let nodes: Vec<DirectiveChainGraphNode> = steps.iter().map(|s| {
- DirectiveChainGraphNode {
- id: s.id,
- name: s.name.clone(),
- step_type: s.step_type.clone(),
- status: s.status.clone(),
- confidence_score: s.confidence_score,
- confidence_level: s.confidence_level.clone(),
- contract_id: s.contract_id,
- editor_x: s.editor_x,
- editor_y: s.editor_y,
- }
- }).collect();
-
- let mut edges = Vec::new();
- for step in &steps {
- for dep_id in &step.depends_on {
- edges.push(DirectiveChainGraphEdge {
- source: *dep_id,
- target: step.id,
- });
- }
- }
-
- Ok(DirectiveChainGraphResponse {
- chain_id,
- directive_id: chain.directive_id,
- nodes,
- edges,
- })
-}
-
-// =============================================================================
-// Directive Evaluation Operations
-// =============================================================================
-
-/// Create a directive evaluation.
-pub async fn create_directive_evaluation(
- pool: &PgPool,
- directive_id: Uuid,
- chain_id: Option<Uuid>,
- step_id: Option<Uuid>,
- contract_id: Option<Uuid>,
- evaluation_type: &str,
- evaluator: Option<&str>,
- passed: bool,
- overall_score: Option<f64>,
- confidence_level: Option<&str>,
- programmatic_results: serde_json::Value,
- llm_results: serde_json::Value,
- criteria_results: serde_json::Value,
- summary_feedback: &str,
- rework_instructions: Option<&str>,
-) -> Result<DirectiveEvaluation, sqlx::Error> {
- // Get next evaluation number for this step/directive
- let evaluation_number = if let Some(sid) = step_id {
- sqlx::query_scalar::<_, i32>(
- "SELECT COALESCE(MAX(evaluation_number), 0) + 1 FROM directive_evaluations WHERE step_id = $1"
- )
- .bind(sid)
- .fetch_one(pool)
- .await?
- } else {
- sqlx::query_scalar::<_, i32>(
- "SELECT COALESCE(MAX(evaluation_number), 0) + 1 FROM directive_evaluations WHERE directive_id = $1 AND step_id IS NULL"
- )
- .bind(directive_id)
- .fetch_one(pool)
- .await?
- };
-
- sqlx::query_as::<_, DirectiveEvaluation>(
- r#"
- INSERT INTO directive_evaluations (
- directive_id, chain_id, step_id, contract_id,
- evaluation_type, evaluation_number, evaluator,
- passed, overall_score, confidence_level,
- programmatic_results, llm_results, criteria_results,
- summary_feedback, rework_instructions,
- completed_at
- )
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, NOW())
- RETURNING *
- "#,
- )
- .bind(directive_id)
- .bind(chain_id)
- .bind(step_id)
- .bind(contract_id)
- .bind(evaluation_type)
- .bind(evaluation_number)
- .bind(evaluator)
- .bind(passed)
- .bind(overall_score)
- .bind(confidence_level)
- .bind(&programmatic_results)
- .bind(&llm_results)
- .bind(&criteria_results)
- .bind(summary_feedback)
- .bind(rework_instructions)
- .fetch_one(pool)
- .await
-}
-
-/// List evaluations for a step.
-pub async fn list_step_evaluations(
- pool: &PgPool,
- step_id: Uuid,
-) -> Result<Vec<DirectiveEvaluation>, sqlx::Error> {
- sqlx::query_as::<_, DirectiveEvaluation>(
- "SELECT * FROM directive_evaluations WHERE step_id = $1 ORDER BY evaluation_number DESC"
- )
- .bind(step_id)
- .fetch_all(pool)
- .await
-}
-
-/// List evaluations for a directive.
-pub async fn list_directive_evaluations(
- pool: &PgPool,
- directive_id: Uuid,
- limit: Option<i64>,
-) -> Result<Vec<DirectiveEvaluation>, sqlx::Error> {
- let limit = limit.unwrap_or(100);
- sqlx::query_as::<_, DirectiveEvaluation>(
- "SELECT * FROM directive_evaluations WHERE directive_id = $1 ORDER BY created_at DESC LIMIT $2"
- )
- .bind(directive_id)
- .bind(limit)
- .fetch_all(pool)
- .await
-}
-
-// =============================================================================
-// Directive Event Operations
-// =============================================================================
-
-/// Emit a directive event.
-pub async fn emit_directive_event(
- pool: &PgPool,
- directive_id: Uuid,
- chain_id: Option<Uuid>,
- step_id: Option<Uuid>,
- event_type: &str,
- severity: &str,
- event_data: Option<serde_json::Value>,
- actor_type: &str,
- actor_id: Option<Uuid>,
-) -> Result<DirectiveEvent, sqlx::Error> {
- sqlx::query_as::<_, DirectiveEvent>(
- r#"
- INSERT INTO directive_events (
- directive_id, chain_id, step_id, event_type, severity, event_data, actor_type, actor_id
- )
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
- RETURNING *
- "#,
- )
- .bind(directive_id)
- .bind(chain_id)
- .bind(step_id)
- .bind(event_type)
- .bind(severity)
- .bind(event_data)
- .bind(actor_type)
- .bind(actor_id)
- .fetch_one(pool)
- .await
-}
-
-/// List directive events.
-pub async fn list_directive_events(
- pool: &PgPool,
- directive_id: Uuid,
- limit: Option<i64>,
-) -> Result<Vec<DirectiveEvent>, sqlx::Error> {
- let limit = limit.unwrap_or(100);
- sqlx::query_as::<_, DirectiveEvent>(
- "SELECT * FROM directive_events WHERE directive_id = $1 ORDER BY created_at DESC LIMIT $2"
- )
- .bind(directive_id)
- .bind(limit)
- .fetch_all(pool)
- .await
-}
-
-// =============================================================================
-// Directive Verifier Operations
-// =============================================================================
-
-/// Create a directive verifier.
-pub async fn create_directive_verifier(
- pool: &PgPool,
- directive_id: Uuid,
- name: &str,
- verifier_type: &str,
- command: Option<&str>,
- working_directory: Option<&str>,
- auto_detect: bool,
- detect_files: Vec<String>,
- weight: f64,
- required: bool,
-) -> Result<DirectiveVerifier, sqlx::Error> {
- sqlx::query_as::<_, DirectiveVerifier>(
- r#"
- INSERT INTO directive_verifiers (
- directive_id, name, verifier_type, command, working_directory,
- auto_detect, detect_files, weight, required
- )
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
- RETURNING *
- "#,
- )
- .bind(directive_id)
- .bind(name)
- .bind(verifier_type)
- .bind(command)
- .bind(working_directory)
- .bind(auto_detect)
- .bind(&detect_files)
- .bind(weight)
- .bind(required)
- .fetch_one(pool)
- .await
-}
-
-/// List verifiers for a directive.
-pub async fn list_directive_verifiers(
- pool: &PgPool,
- directive_id: Uuid,
-) -> Result<Vec<DirectiveVerifier>, sqlx::Error> {
- sqlx::query_as::<_, DirectiveVerifier>(
- "SELECT * FROM directive_verifiers WHERE directive_id = $1 ORDER BY name"
- )
- .bind(directive_id)
- .fetch_all(pool)
- .await
-}
-
-/// Update a directive verifier.
-pub async fn update_directive_verifier(
- pool: &PgPool,
- verifier_id: Uuid,
- enabled: Option<bool>,
- command: Option<&str>,
- weight: Option<f64>,
- required: Option<bool>,
-) -> Result<DirectiveVerifier, sqlx::Error> {
- sqlx::query_as::<_, DirectiveVerifier>(
- r#"
- UPDATE directive_verifiers SET
- enabled = COALESCE($2, enabled),
- command = COALESCE($3, command),
- weight = COALESCE($4, weight),
- required = COALESCE($5, required),
- updated_at = NOW()
- WHERE id = $1
- RETURNING *
- "#,
- )
- .bind(verifier_id)
- .bind(enabled)
- .bind(command)
- .bind(weight)
- .bind(required)
- .fetch_one(pool)
- .await
-}
-
-/// Update verifier last run result.
-pub async fn update_verifier_result(
- pool: &PgPool,
- verifier_id: Uuid,
- result: serde_json::Value,
-) -> Result<DirectiveVerifier, sqlx::Error> {
- sqlx::query_as::<_, DirectiveVerifier>(
- r#"
- UPDATE directive_verifiers SET last_run_at = NOW(), last_result = $2, updated_at = NOW()
- WHERE id = $1
- RETURNING *
- "#,
- )
- .bind(verifier_id)
- .bind(result)
- .fetch_one(pool)
- .await
-}
-
-// =============================================================================
-// Directive Approval Operations
-// =============================================================================
-
-/// Create an approval request.
-pub async fn create_approval_request(
- pool: &PgPool,
- directive_id: Uuid,
- step_id: Option<Uuid>,
- approval_type: &str,
- description: &str,
- context: Option<serde_json::Value>,
- urgency: &str,
- expires_at: Option<chrono::DateTime<Utc>>,
-) -> Result<DirectiveApproval, sqlx::Error> {
- sqlx::query_as::<_, DirectiveApproval>(
- r#"
- INSERT INTO directive_approvals (
- directive_id, step_id, approval_type, description, context, urgency, expires_at
- )
- VALUES ($1, $2, $3, $4, $5, $6, $7)
- RETURNING *
- "#,
- )
- .bind(directive_id)
- .bind(step_id)
- .bind(approval_type)
- .bind(description)
- .bind(context)
- .bind(urgency)
- .bind(expires_at)
- .fetch_one(pool)
- .await
-}
-
-/// Resolve an approval request.
-pub async fn resolve_approval(
- pool: &PgPool,
- approval_id: Uuid,
- status: &str,
- response: Option<&str>,
- responded_by: Uuid,
-) -> Result<DirectiveApproval, sqlx::Error> {
- sqlx::query_as::<_, DirectiveApproval>(
- r#"
- UPDATE directive_approvals SET
- status = $2,
- response = $3,
- responded_by = $4,
- responded_at = NOW()
- WHERE id = $1
- RETURNING *
- "#,
- )
- .bind(approval_id)
- .bind(status)
- .bind(response)
- .bind(responded_by)
- .fetch_one(pool)
- .await
-}
-
-/// List pending approvals for a directive.
-pub async fn list_pending_approvals(
- pool: &PgPool,
- directive_id: Uuid,
-) -> Result<Vec<DirectiveApproval>, sqlx::Error> {
- sqlx::query_as::<_, DirectiveApproval>(
- r#"
- SELECT * FROM directive_approvals
- WHERE directive_id = $1 AND status = 'pending'
- ORDER BY
- CASE urgency
- WHEN 'critical' THEN 1
- WHEN 'high' THEN 2
- WHEN 'normal' THEN 3
- ELSE 4
- END,
- created_at
- "#,
- )
- .bind(directive_id)
- .fetch_all(pool)
- .await
-}
-
-/// Get step by contract ID.
-pub async fn get_step_by_contract_id(
- pool: &PgPool,
- contract_id: Uuid,
-) -> Result<Option<ChainStep>, sqlx::Error> {
- sqlx::query_as::<_, ChainStep>(
- "SELECT * FROM chain_steps WHERE contract_id = $1"
- )
- .bind(contract_id)
- .fetch_optional(pool)
- .await
-}
-
-// =============================================================================
// Helper Functions
// =============================================================================