From 88a4f15ce1310f8ee8693835be14aa5280233f17 Mon Sep 17 00:00:00 2001 From: soryu Date: Thu, 5 Feb 2026 23:42:48 +0000 Subject: Add directive-first chain system redesign Redesigns the chain system with a directive-first architecture where Directive is the top-level entity (the "why/what") and Chains are generated execution plans (the "how") that can be dynamically modified. Backend: - Add database migration for directive system tables - Add Directive, DirectiveChain, ChainStep, DirectiveEvent models - Add DirectiveVerifier and DirectiveApproval models - Add orchestration module with engine, planner, and verifier - Add comprehensive API handlers for directives - Add daemon CLI commands for directive management - Add directive skill documentation - Integrate contract completion with directive engine - Add SSE endpoint for real-time directive events Frontend: - Add directives route with split-view layout - Add 6-tab detail view (Overview, Chain, Events, Evaluations, Approvals, Verifiers) - Add React Flow DAG visualization for chain steps - Add SSE subscription hook for real-time event updates - Add useDirectives and useDirectiveEventSubscription hooks - Add directive types and API functions Fixes: - Fix test failures in ws/protocol, task_output, completion_gate, patch - Fix word boundary matching in looks_like_task() - Fix parse_last() to find actual last completion gate - Fix create_export_patch when merge-base equals HEAD - Clean up clippy warnings in new code Co-Authored-By: Claude Opus 4.5 --- makima/src/db/models.rs | 1216 ++++++++++++++++++----------------------------- 1 file changed, 475 insertions(+), 741 deletions(-) (limited to 'makima/src/db/models.rs') diff --git a/makima/src/db/models.rs b/makima/src/db/models.rs index e861f1d..3a96165 100644 --- a/makima/src/db/models.rs +++ b/makima/src/db/models.rs @@ -1446,16 +1446,16 @@ pub struct Contract { /// Use `get_phase_config()` to get the parsed PhaseConfig. #[serde(skip_serializing_if = "Option::is_none")] pub phase_config: Option, - /// Chain ID if this contract is part of a chain (DAG of contracts) + /// Directive ID if this contract is part of a directive's chain #[serde(skip_serializing_if = "Option::is_none")] - pub chain_id: Option, - /// Reference to chain spawned by this directive contract - #[serde(skip_serializing_if = "Option::is_none")] - pub spawned_chain_id: Option, - /// Whether this contract is a chain directive orchestrator + pub directive_id: Option, + /// Whether this contract is a directive orchestrator #[serde(default)] #[sqlx(default)] - pub is_chain_directive: bool, + pub is_directive_orchestrator: bool, + /// Reference to directive spawned by this orchestrator contract + #[serde(skip_serializing_if = "Option::is_none")] + pub spawned_directive_id: Option, pub version: i32, pub created_at: DateTime, pub updated_at: DateTime, @@ -2596,914 +2596,648 @@ pub struct HeartbeatHistoryQuery { } // ============================================================================= -// Chains (DAG of contracts for multi-contract orchestration) +// Directives (Goal-driven orchestration with chains of steps) // ============================================================================= -/// Chain status determines the overall state of the chain +/// Directive status #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "lowercase")] -pub enum ChainStatus { - /// Chain is actively running +#[serde(rename_all = "snake_case")] +pub enum DirectiveStatus { + Draft, + Planning, Active, - /// All contracts completed successfully + Paused, Completed, - /// Chain was manually archived Archived, + Failed, } -impl Default for ChainStatus { +impl Default for DirectiveStatus { fn default() -> Self { - ChainStatus::Active + DirectiveStatus::Draft } } -impl std::fmt::Display for ChainStatus { +impl std::fmt::Display for DirectiveStatus { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - ChainStatus::Active => write!(f, "active"), - ChainStatus::Completed => write!(f, "completed"), - ChainStatus::Archived => write!(f, "archived"), + DirectiveStatus::Draft => write!(f, "draft"), + DirectiveStatus::Planning => write!(f, "planning"), + DirectiveStatus::Active => write!(f, "active"), + DirectiveStatus::Paused => write!(f, "paused"), + DirectiveStatus::Completed => write!(f, "completed"), + DirectiveStatus::Archived => write!(f, "archived"), + DirectiveStatus::Failed => write!(f, "failed"), } } } -impl std::str::FromStr for ChainStatus { +impl std::str::FromStr for DirectiveStatus { type Err = String; fn from_str(s: &str) -> Result { match s.to_lowercase().as_str() { - "active" => Ok(ChainStatus::Active), - "completed" => Ok(ChainStatus::Completed), - "archived" => Ok(ChainStatus::Archived), - _ => Err(format!("Invalid chain status: {}", s)), + "draft" => Ok(DirectiveStatus::Draft), + "planning" => Ok(DirectiveStatus::Planning), + "active" => Ok(DirectiveStatus::Active), + "paused" => Ok(DirectiveStatus::Paused), + "completed" => Ok(DirectiveStatus::Completed), + "archived" => Ok(DirectiveStatus::Archived), + "failed" => Ok(DirectiveStatus::Failed), + _ => Err(format!("Invalid directive status: {}", s)), } } } -/// Chain - a directed acyclic graph (DAG) of contracts -/// Fits Makima's control theme - she controls through invisible chains +/// Directive - the top-level goal-driven orchestration entity #[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub struct Chain { +pub struct Directive { pub id: Uuid, pub owner_id: Uuid, - pub name: String, - pub description: Option, + pub title: String, + pub goal: String, + /// Structured requirements: [{ id, title, description, priority, category }] + #[sqlx(json)] + pub requirements: serde_json::Value, + /// Acceptance criteria: [{ id, requirementIds, description, testable, verificationMethod }] + #[sqlx(json)] + pub acceptance_criteria: serde_json::Value, + /// Constraints: [{ id, type, description, impact }] + #[sqlx(json)] + pub constraints: serde_json::Value, + /// External dependencies: [{ id, name, type, status, requiredBy }] + #[sqlx(json)] + pub external_dependencies: serde_json::Value, pub status: String, - /// Whether loop mode is enabled for iterative execution - #[serde(default)] - pub loop_enabled: bool, - /// Maximum loop iterations (default: 10) - pub loop_max_iterations: Option, - /// Current loop iteration count - pub loop_current_iteration: Option, - /// Progress check prompt/criteria for evaluating loop completion - pub loop_progress_check: Option, - /// Reference to the directive contract that created/orchestrates this chain - pub directive_contract_id: Option, - /// The directive document text (formal specification) - pub directive_document: Option, - /// Whether LLM evaluation is enabled after contract completion - #[serde(default = "default_evaluation_enabled")] - #[sqlx(default)] - pub evaluation_enabled: bool, - /// Default pass threshold for evaluations (0.0-1.0) - pub default_pass_threshold: Option, - /// Default max retry attempts for evaluations - pub default_max_retries: Option, - /// Version for optimistic locking + pub autonomy_level: String, + pub confidence_threshold_green: f64, + pub confidence_threshold_yellow: f64, + pub max_total_cost_usd: Option, + pub max_wall_time_minutes: Option, + pub max_rework_cycles: Option, + pub max_chain_regenerations: Option, + pub repository_url: Option, + pub local_path: Option, + pub base_branch: Option, + pub orchestrator_contract_id: Option, + pub current_chain_id: Option, + pub chain_generation_count: i32, + pub total_cost_usd: f64, + pub started_at: Option>, + pub completed_at: Option>, pub version: i32, pub created_at: DateTime, pub updated_at: DateTime, } -fn default_evaluation_enabled() -> bool { - true +impl Directive { + /// Parse status string to DirectiveStatus enum + pub fn status_enum(&self) -> Result { + self.status.parse() + } } -/// Chain repository record from the database +/// Directive chain - a generated execution plan (DAG) for a directive #[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub struct ChainRepository { +pub struct DirectiveChain { pub id: Uuid, - pub chain_id: Uuid, + pub directive_id: Uuid, + pub generation: i32, pub name: String, - pub repository_url: Option, - pub local_path: Option, - pub source_type: String, + pub description: Option, + pub rationale: Option, + pub planning_model: Option, pub status: String, - pub is_primary: bool, + pub total_steps: i32, + pub completed_steps: i32, + pub failed_steps: i32, + pub current_confidence: Option, + pub started_at: Option>, + pub completed_at: Option>, + pub version: i32, pub created_at: DateTime, pub updated_at: DateTime, } -impl ChainRepository { - /// Parse source_type string to RepositorySourceType enum - pub fn source_type_enum(&self) -> Result { - self.source_type.parse() +/// Chain step status +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "snake_case")] +pub enum StepStatus { + Pending, + Ready, + Running, + Evaluating, + Passed, + Failed, + Rework, + Skipped, + Blocked, +} + +impl Default for StepStatus { + fn default() -> Self { + StepStatus::Pending } +} - /// Parse status string to RepositoryStatus enum - pub fn status_enum(&self) -> Result { - self.status.parse() +impl std::fmt::Display for StepStatus { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + StepStatus::Pending => write!(f, "pending"), + StepStatus::Ready => write!(f, "ready"), + StepStatus::Running => write!(f, "running"), + StepStatus::Evaluating => write!(f, "evaluating"), + StepStatus::Passed => write!(f, "passed"), + StepStatus::Failed => write!(f, "failed"), + StepStatus::Rework => write!(f, "rework"), + StepStatus::Skipped => write!(f, "skipped"), + StepStatus::Blocked => write!(f, "blocked"), + } } } -impl Chain { - /// Parse status string to ChainStatus enum - pub fn status_enum(&self) -> Result { - self.status.parse() +impl std::str::FromStr for StepStatus { + type Err = String; + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "pending" => Ok(StepStatus::Pending), + "ready" => Ok(StepStatus::Ready), + "running" => Ok(StepStatus::Running), + "evaluating" => Ok(StepStatus::Evaluating), + "passed" => Ok(StepStatus::Passed), + "failed" => Ok(StepStatus::Failed), + "rework" => Ok(StepStatus::Rework), + "skipped" => Ok(StepStatus::Skipped), + "blocked" => Ok(StepStatus::Blocked), + _ => Err(format!("Invalid step status: {}", s)), + } } } -/// Chain contract link - links contracts to chains with DAG dependency info +/// Chain step - a node in the DAG execution plan #[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub struct ChainContract { +pub struct ChainStep { pub id: Uuid, pub chain_id: Uuid, - pub contract_id: Uuid, - /// Contract IDs this contract depends on (DAG edges) + pub name: String, + pub description: Option, + pub step_type: String, + pub contract_type: String, + pub initial_phase: Option, + pub task_plan: Option, #[sqlx(default)] - pub depends_on: Vec, - /// Order for display/processing (topological sort order) - pub order_index: i32, - /// X position for GUI editor - pub editor_x: Option, - /// Y position for GUI editor - pub editor_y: Option, - /// Evaluation status: pending, evaluating, passed, failed, rework, escalated - #[serde(default = "default_evaluation_status")] + pub phases: Vec, #[sqlx(default)] - pub evaluation_status: String, - /// Number of evaluation retry attempts - #[serde(default)] + pub depends_on: Vec, + pub parallel_group: Option, #[sqlx(default)] - pub evaluation_retry_count: i32, - /// Maximum evaluation retry attempts (default: 3) - #[serde(default = "default_max_evaluation_retries")] + pub requirement_ids: Vec, #[sqlx(default)] - pub max_evaluation_retries: i32, - /// Reference to the last evaluation result + pub acceptance_criteria_ids: Vec, + #[sqlx(json)] + #[serde(default)] + pub verifier_config: serde_json::Value, + pub status: String, + pub contract_id: Option, + pub supervisor_task_id: Option, + pub confidence_score: Option, + pub confidence_level: Option, + pub evaluation_count: i32, + pub rework_count: i32, pub last_evaluation_id: Option, - /// Rework feedback/instructions from failed evaluation - pub rework_feedback: Option, - /// When rework was started - pub rework_started_at: Option>, - /// When contract originally completed (before rework) - pub original_completion_at: Option>, + pub editor_x: Option, + pub editor_y: Option, + pub order_index: i32, + pub started_at: Option>, + pub completed_at: Option>, pub created_at: DateTime, } -fn default_evaluation_status() -> String { - "pending".to_string() +impl ChainStep { + /// Parse status string to StepStatus enum + pub fn status_enum(&self) -> Result { + self.status.parse() + } } -fn default_max_evaluation_retries() -> i32 { - 3 +/// Confidence level (traffic light) +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "snake_case")] +pub enum ConfidenceLevel { + Green, + Yellow, + Red, +} + +impl ConfidenceLevel { + pub fn from_score(score: f64, green_threshold: f64, yellow_threshold: f64) -> Self { + if score >= green_threshold { + Self::Green + } else if score >= yellow_threshold { + Self::Yellow + } else { + Self::Red + } + } +} + +impl std::fmt::Display for ConfidenceLevel { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ConfidenceLevel::Green => write!(f, "green"), + ConfidenceLevel::Yellow => write!(f, "yellow"), + ConfidenceLevel::Red => write!(f, "red"), + } + } } -/// Chain event for audit trail +/// Directive evaluation - composite programmatic + LLM evaluation result #[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub struct ChainEvent { +pub struct DirectiveEvaluation { pub id: Uuid, - pub chain_id: Uuid, - pub event_type: String, + pub directive_id: Uuid, + pub chain_id: Option, + pub step_id: Option, pub contract_id: Option, + pub evaluation_type: String, + pub evaluation_number: i32, + pub evaluator: Option, + pub passed: bool, + pub overall_score: Option, + pub confidence_level: Option, + #[sqlx(json)] + #[serde(default)] + pub programmatic_results: serde_json::Value, + #[sqlx(json)] + #[serde(default)] + pub llm_results: serde_json::Value, + #[sqlx(json)] + #[serde(default)] + pub criteria_results: serde_json::Value, + pub summary_feedback: String, + pub rework_instructions: Option, + #[sqlx(json)] + pub directive_snapshot: Option, + #[sqlx(json)] + pub deliverables_snapshot: Option, + pub started_at: DateTime, + pub completed_at: Option>, + pub created_at: DateTime, +} + +/// Directive event - audit stream entry +#[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct DirectiveEvent { + pub id: Uuid, + pub directive_id: Uuid, + pub chain_id: Option, + pub step_id: Option, + pub event_type: String, + pub severity: String, #[sqlx(json)] pub event_data: Option, + pub actor_type: String, + pub actor_id: Option, pub created_at: DateTime, } -/// Summary of a chain for list views -#[derive(Debug, Clone, FromRow, Serialize, ToSchema)] +/// Directive verifier - pluggable verification configuration +#[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub struct ChainSummary { +pub struct DirectiveVerifier { pub id: Uuid, + pub directive_id: Uuid, pub name: String, - pub description: Option, + pub verifier_type: String, + pub command: Option, + pub working_directory: Option, + pub timeout_seconds: Option, + #[sqlx(json)] + #[serde(default)] + pub environment: serde_json::Value, + pub auto_detect: bool, + #[sqlx(default)] + pub detect_files: Vec, + pub weight: f64, + pub required: bool, + pub enabled: bool, + pub last_run_at: Option>, + #[sqlx(json)] + pub last_result: Option, + pub created_at: DateTime, + pub updated_at: DateTime, +} + +/// Directive approval - human-in-the-loop gate +#[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct DirectiveApproval { + pub id: Uuid, + pub directive_id: Uuid, + pub step_id: Option, + pub approval_type: String, + pub description: String, + #[sqlx(json)] + pub context: Option, + pub urgency: String, pub status: String, - pub loop_enabled: bool, - pub loop_current_iteration: Option, - pub contract_count: i64, - pub completed_count: i64, + pub response: Option, + pub responded_by: Option, + pub responded_at: Option>, + pub expires_at: Option>, + pub created_at: DateTime, +} + +// ============================================================================= +// Directive Request/Response Types +// ============================================================================= + +/// Request to create a directive from a goal +#[derive(Debug, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct CreateDirectiveRequest { + pub goal: String, + pub title: Option, + pub repository_url: Option, + pub local_path: Option, + pub base_branch: Option, + pub autonomy_level: Option, + pub requirements: Option, + pub acceptance_criteria: Option, + pub confidence_threshold_green: Option, + pub confidence_threshold_yellow: Option, + pub max_total_cost_usd: Option, + pub max_wall_time_minutes: Option, +} + +/// Request to update a directive +#[derive(Debug, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct UpdateDirectiveRequest { + pub title: Option, + pub goal: Option, + pub requirements: Option, + pub acceptance_criteria: Option, + pub constraints: Option, + pub external_dependencies: Option, + pub autonomy_level: Option, + pub confidence_threshold_green: Option, + pub confidence_threshold_yellow: Option, + pub max_total_cost_usd: Option, + pub max_wall_time_minutes: Option, + pub max_rework_cycles: Option, + pub max_chain_regenerations: Option, pub version: i32, +} + +/// Directive summary for list views +#[derive(Debug, Clone, Serialize, FromRow, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct DirectiveSummary { + pub id: Uuid, + pub title: String, + pub goal: String, + pub status: String, + pub autonomy_level: String, + pub current_confidence: Option, + pub completed_steps: i32, + pub total_steps: i32, + pub chain_generation_count: i32, + pub started_at: Option>, pub created_at: DateTime, } -/// Chain with contracts for detail view +/// Directive with progress, chain, events, and approvals #[derive(Debug, Serialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub struct ChainWithContracts { +pub struct DirectiveWithProgress { #[serde(flatten)] - pub chain: Chain, - pub contracts: Vec, - pub repositories: Vec, + pub directive: Directive, + pub chain: Option, + pub steps: Vec, + pub recent_events: Vec, + pub pending_approvals: Vec, } -/// Contract detail within a chain (includes contract info + chain link info) -#[derive(Debug, Clone, FromRow, Serialize, ToSchema)] +/// Request to add a step to a chain +#[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub struct ChainContractDetail { - pub chain_contract_id: Uuid, - pub contract_id: Uuid, - pub contract_name: String, - pub contract_status: String, - pub contract_phase: String, - #[sqlx(default)] - pub depends_on: Vec, - pub order_index: i32, +pub struct AddStepRequest { + pub name: String, + pub description: Option, + pub step_type: Option, + pub contract_type: Option, + pub initial_phase: Option, + pub task_plan: Option, + pub phases: Option>, + pub depends_on: Option>, + pub parallel_group: Option, + pub requirement_ids: Option>, + pub acceptance_criteria_ids: Option>, + pub verifier_config: Option, + pub editor_x: Option, + pub editor_y: Option, +} + +/// Request to update a step +#[derive(Debug, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct UpdateStepRequest { + pub name: Option, + pub description: Option, + pub task_plan: Option, + pub depends_on: Option>, + pub requirement_ids: Option>, + pub acceptance_criteria_ids: Option>, + pub verifier_config: Option, pub editor_x: Option, pub editor_y: Option, - /// Evaluation status: pending, passed, failed, rework - #[sqlx(default)] - pub evaluation_status: Option, - /// Number of evaluation retries - #[sqlx(default)] - pub evaluation_retry_count: i32, - /// Maximum evaluation retry attempts - #[sqlx(default)] - pub max_evaluation_retries: i32, - /// When the chain contract was created - pub created_at: DateTime, } -/// DAG graph structure for visualization +/// Chain graph response for DAG visualization #[derive(Debug, Serialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub struct ChainGraphResponse { +pub struct DirectiveChainGraphResponse { pub chain_id: Uuid, - pub chain_name: String, - pub chain_status: String, - pub nodes: Vec, - pub edges: Vec, + pub directive_id: Uuid, + pub nodes: Vec, + pub edges: Vec, } -/// Node in chain DAG graph +/// Node in directive chain graph #[derive(Debug, Serialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub struct ChainGraphNode { +pub struct DirectiveChainGraphNode { pub id: Uuid, - pub contract_id: Uuid, pub name: String, + pub step_type: String, pub status: String, - pub phase: String, - pub x: f64, - pub y: f64, + pub confidence_score: Option, + pub confidence_level: Option, + pub contract_id: Option, + pub editor_x: Option, + pub editor_y: Option, } -/// Edge in chain DAG graph -#[derive(Debug, Clone, Serialize, ToSchema)] +/// Edge in directive chain graph +#[derive(Debug, Serialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub struct ChainGraphEdge { - pub from: Uuid, - pub to: Uuid, +pub struct DirectiveChainGraphEdge { + pub source: Uuid, + pub target: Uuid, } -/// Response for chain list endpoint +/// Start directive response #[derive(Debug, Serialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub struct ChainListResponse { - pub chains: Vec, - pub total: i64, +pub struct StartDirectiveResponse { + pub directive_id: Uuid, + pub chain_id: Uuid, + pub chain_generation: i32, + pub steps: Vec, + pub status: String, } -/// Request payload for creating a new chain -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +/// Request to create a verifier +#[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub struct CreateChainRequest { - /// Name of the chain +pub struct CreateVerifierRequest { pub name: String, - /// Optional description - pub description: Option, - /// Repositories for this chain - pub repositories: Option>, - /// Enable loop mode for iterative execution - #[serde(default)] - pub loop_enabled: Option, - /// Maximum loop iterations (default: 10) - pub loop_max_iterations: Option, - /// Progress check prompt for evaluating loop completion - pub loop_progress_check: Option, - /// Contracts to create within this chain - pub contracts: Option>, + pub verifier_type: String, + pub command: Option, + pub working_directory: Option, + pub timeout_seconds: Option, + pub environment: Option, + pub weight: Option, + pub required: Option, } -/// Request to add a repository to a chain -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +/// Request to update a verifier +#[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub struct AddChainRepositoryRequest { - /// Display name for the repository - pub name: String, - /// Remote repository URL (for remote repos) - pub repository_url: Option, - /// Local filesystem path (for local repos) - pub local_path: Option, - /// Source type: remote, local, or managed - #[serde(default = "default_source_type")] - pub source_type: String, - /// Whether this is the primary repository - #[serde(default)] - pub is_primary: bool, -} - -fn default_source_type() -> String { - "remote".to_string() +pub struct UpdateVerifierRequest { + pub name: Option, + pub command: Option, + pub working_directory: Option, + pub timeout_seconds: Option, + pub weight: Option, + pub required: Option, + pub enabled: Option, } -/// Request to create a contract within a chain -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +/// Approval action request +#[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub struct CreateChainContractRequest { - /// Name of the contract - pub name: String, - /// Optional description - pub description: Option, - /// Contract type - #[serde(default)] - pub contract_type: Option, - /// Initial phase - pub initial_phase: Option, - /// Phases for the contract - pub phases: Option>, - /// Names of contracts this depends on (resolved to IDs) - pub depends_on: Option>, - /// Tasks to create in this contract - pub tasks: Option>, - /// Deliverables for this contract - pub deliverables: Option>, - /// Position in GUI editor - pub editor_x: Option, - pub editor_y: Option, +pub struct ApprovalActionRequest { + pub response: Option, } -/// Task definition within a chain contract +/// Directive requirement (shared type used in directive specification) #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub struct CreateChainTaskRequest { - pub name: String, - pub plan: String, -} - -/// Deliverable definition within a chain contract -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct CreateChainDeliverableRequest { +pub struct DirectiveRequirement { pub id: String, - pub name: String, - pub priority: Option, + pub title: String, + pub description: String, + pub priority: String, + pub category: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub parent_id: Option, } -/// Validation configuration for checkpoint contracts. -/// Checkpoint contracts validate the outputs of their upstream dependencies -/// before allowing downstream contracts to proceed. +/// Directive acceptance criterion #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub struct CheckpointValidation { - /// Check that all required deliverables from upstream contracts exist - #[serde(default)] - pub check_deliverables: bool, - - /// Run tests in the repository (requires repository to be configured) +pub struct DirectiveAcceptanceCriterion { + pub id: String, #[serde(default)] - pub run_tests: bool, - - /// Custom validation instructions for Claude to execute. - /// Claude will review the outputs of upstream contracts and verify they meet these criteria. - pub check_content: Option, - - /// Action to take on validation failure: "block" (default), "retry", "warn" - /// - block: Fail the checkpoint and block downstream contracts - /// - retry: Mark upstream contracts for retry (up to max_retries) - /// - warn: Log warning but allow downstream to proceed - #[serde(default = "default_checkpoint_on_failure")] - pub on_failure: String, - - /// Maximum retry attempts for upstream contracts (when on_failure = "retry") - #[serde(default = "default_checkpoint_max_retries")] - pub max_retries: i32, -} - -fn default_checkpoint_on_failure() -> String { - "block".to_string() -} - -fn default_checkpoint_max_retries() -> i32 { - 3 + pub requirement_ids: Vec, + pub description: String, + #[serde(default = "default_true")] + pub testable: bool, + pub verification_method: Option, } -impl Default for CheckpointValidation { - fn default() -> Self { - Self { - check_deliverables: false, - run_tests: false, - check_content: None, - on_failure: default_checkpoint_on_failure(), - max_retries: default_checkpoint_max_retries(), - } - } +fn default_true() -> bool { + true } -/// Request to update an existing chain -#[derive(Debug, Clone, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct UpdateChainRequest { - pub name: Option, - pub description: Option, - pub status: Option, - pub loop_enabled: Option, - pub loop_max_iterations: Option, - pub loop_progress_check: Option, - /// Version for optimistic locking - pub version: Option, -} +// Old chain types (Chain, ChainContract, ChainContractDefinition, ChainDirective, +// ContractEvaluation, ChainEvent, ChainRepository, etc.) have been replaced by +// the directive system above: Directive, DirectiveChain, ChainStep, +// DirectiveEvaluation, DirectiveEvent, DirectiveVerifier, DirectiveApproval. -/// Request to add a contract to a chain -#[derive(Debug, Clone, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct AddContractToChainRequest { - /// Existing contract ID to add - pub contract_id: Option, - /// Or create a new contract with this definition - pub new_contract: Option, - /// Contract IDs this depends on - pub depends_on: Option>, - /// Position in GUI editor - pub editor_x: Option, - pub editor_y: Option, -} +// Legacy types kept temporarily for chain runner/parser compatibility during migration. +// These will be removed once the chain daemon module is replaced. -/// Editor data model for GUI chain editor -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +/// Request payload for creating a new chain (legacy - used by chain runner) +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct ChainEditorData { - pub id: Option, +pub struct CreateChainRequest { pub name: String, pub description: Option, - pub repositories: Vec, - pub loop_enabled: bool, + pub repository_url: Option, + pub repositories: Option>, + pub loop_enabled: Option, pub loop_max_iterations: Option, pub loop_progress_check: Option, - pub nodes: Vec, - pub edges: Vec, -} - -/// Node in chain editor -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct ChainEditorNode { - pub id: String, - pub x: f64, - pub y: f64, - pub contract: ChainEditorContract, -} - -/// Contract data in chain editor node -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct ChainEditorContract { - pub name: String, - pub description: Option, - #[serde(rename = "type")] - pub contract_type: String, - pub phases: Vec, - pub tasks: Vec, - pub deliverables: Vec, -} - -/// Task in chain editor -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct ChainEditorTask { - pub name: String, - pub plan: String, + pub contracts: Option>, } -/// Deliverable in chain editor -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +/// Request to add a repository to a chain (legacy - used by chain runner) +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct ChainEditorDeliverable { - pub id: String, +pub struct AddChainRepositoryRequest { pub name: String, - pub priority: String, + pub repository_url: Option, + pub local_path: Option, + #[serde(default = "default_source_type")] + pub source_type: String, + #[serde(default)] + pub is_primary: bool, } -/// Edge in chain editor -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct ChainEditorEdge { - pub from: String, - pub to: String, +fn default_source_type() -> String { + "remote".to_string() } -// ============================================================================= -// Chain Contract Definitions (stored specs for on-demand contract creation) -// ============================================================================= - -/// Contract definition within a chain - stored spec before actual contract is created -#[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)] +/// Request to create a contract within a chain (legacy - used by chain runner) +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct ChainContractDefinition { - pub id: Uuid, - pub chain_id: Uuid, +pub struct CreateChainContractRequest { pub name: String, pub description: Option, - pub contract_type: String, - pub initial_phase: Option, - /// Names of other definitions this depends on - #[sqlx(default)] - pub depends_on_names: Vec, - /// Task definitions as JSON: [{name, plan}, ...] - pub tasks: Option, - /// Deliverable definitions as JSON: [{id, name, priority}, ...] - pub deliverables: Option, - /// Validation configuration for checkpoint contracts (JSON) - pub validation: Option, - /// Requirement IDs this contract addresses (for traceability) - #[sqlx(default)] #[serde(default)] - pub requirement_ids: Vec, - /// Acceptance criteria for this contract (JSON array) - #[serde(default)] - pub acceptance_criteria: Option, - /// Whether LLM evaluation is enabled for this contract - #[serde(default = "default_evaluation_enabled")] - #[sqlx(default)] - pub evaluation_enabled: bool, - /// Pass threshold for evaluation (0.0-1.0) - pub pass_threshold: Option, - /// Position in GUI editor - pub editor_x: Option, - pub editor_y: Option, - pub order_index: i32, - pub created_at: DateTime, -} - -/// Request to add a contract definition to a chain -#[derive(Debug, Clone, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct AddContractDefinitionRequest { - pub name: String, - pub description: Option, - #[serde(default = "default_contract_type")] - pub contract_type: String, - pub initial_phase: Option, - /// Names of other definitions this depends on - pub depends_on: Option>, - /// Task definitions - pub tasks: Option>, - /// Deliverable definitions - pub deliverables: Option>, - /// Validation configuration (for checkpoint contracts) - pub validation: Option, - /// Position in GUI editor - pub editor_x: Option, - pub editor_y: Option, -} - -fn default_contract_type() -> String { - "simple".to_string() -} - -/// Request to update a contract definition -#[derive(Debug, Clone, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct UpdateContractDefinitionRequest { - pub name: Option, - pub description: Option, pub contract_type: Option, pub initial_phase: Option, + pub phases: Option>, pub depends_on: Option>, pub tasks: Option>, pub deliverables: Option>, - /// Validation configuration (for checkpoint contracts) - pub validation: Option, pub editor_x: Option, pub editor_y: Option, } -/// Request to start a chain (kept for backwards compatibility) -#[derive(Debug, Clone, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct StartChainRequest { - /// Repository URL (reserved for future use) - pub repository_url: Option, -} - -/// Response when starting a chain -#[derive(Debug, Clone, Serialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct StartChainResponse { - pub chain_id: Uuid, - /// Root contracts created (those with no dependencies) - pub contracts_created: Vec, - pub status: String, -} - -/// Graph node for definitions (before contracts are created) -#[derive(Debug, Clone, Serialize, ToSchema)] +/// Task definition within a chain contract (legacy - used by chain runner) +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct ChainDefinitionGraphNode { - pub id: Uuid, +pub struct CreateChainTaskRequest { pub name: String, - pub contract_type: String, - pub x: f64, - pub y: f64, - /// Whether this definition has been instantiated as a contract - pub is_instantiated: bool, - /// The contract ID if instantiated - pub contract_id: Option, - pub contract_status: Option, -} - -/// Graph response for definitions -#[derive(Debug, Clone, Serialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct ChainDefinitionGraphResponse { - pub chain_id: Uuid, - pub chain_name: String, - pub chain_status: String, - pub nodes: Vec, - pub edges: Vec, -} - -// ============================================================================= -// Chain Directives (formal specification documents for directive-driven chains) -// ============================================================================= - -/// Chain directive - formal specification document that drives chain creation and evaluation -#[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct ChainDirective { - pub id: Uuid, - pub chain_id: Uuid, - pub version: i32, - /// Requirements as JSON: [{ id, title, description, priority, category, parentId? }] - #[sqlx(json)] - pub requirements: serde_json::Value, - /// Acceptance criteria as JSON: [{ id, requirementIds[], description, testable, verificationMethod }] - #[sqlx(json)] - pub acceptance_criteria: serde_json::Value, - /// Constraints as JSON: [{ id, type, description, impact }] - #[sqlx(json)] - pub constraints: serde_json::Value, - /// External dependencies as JSON: [{ id, name, type, status, requiredBy[] }] - #[sqlx(json)] - pub external_dependencies: serde_json::Value, - /// Source type: 'manual', 'llm_generated', 'imported' - pub source_type: String, - pub created_at: DateTime, - pub updated_at: DateTime, -} - -/// Requirement in a directive -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct DirectiveRequirement { - pub id: String, - pub title: String, - pub description: String, - /// Priority: 'must', 'should', 'could', 'wont' - pub priority: String, - /// Category: 'feature', 'infrastructure', 'testing', etc. - pub category: Option, - /// Parent requirement ID for hierarchical requirements - pub parent_id: Option, -} - -/// Acceptance criterion in a directive -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct DirectiveAcceptanceCriterion { - pub id: String, - /// Requirement IDs this criterion validates - pub requirement_ids: Vec, - pub description: String, - pub testable: bool, - /// Verification method: 'automated', 'manual', 'review', 'llm' - pub verification_method: String, -} - -/// Constraint in a directive -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct DirectiveConstraint { - pub id: String, - /// Type: 'technical', 'business', 'time', 'resource' - #[serde(rename = "type")] - pub constraint_type: String, - pub description: String, - /// Impact: 'high', 'medium', 'low' - pub impact: String, + pub plan: String, } -/// External dependency in a directive -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +/// Deliverable definition within a chain contract (legacy - used by chain runner) +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct DirectiveExternalDependency { +pub struct CreateChainDeliverableRequest { pub id: String, pub name: String, - /// Type: 'api', 'service', 'library', 'data' - #[serde(rename = "type")] - pub dependency_type: String, - /// Status: 'available', 'pending', 'blocked' - pub status: String, - /// Requirement IDs that need this dependency - pub required_by: Vec, -} - -/// Request to create or update a chain directive -#[derive(Debug, Clone, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct CreateChainDirectiveRequest { - pub requirements: Option>, - pub acceptance_criteria: Option>, - pub constraints: Option>, - pub external_dependencies: Option>, - pub source_type: Option, -} - -/// Request to initialize a directive-driven chain -#[derive(Debug, Clone, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct InitChainRequest { - /// High-level goal/description for the directive contract - pub goal: String, - /// Repository URL for chain contracts - pub repository_url: Option, - /// Local path for chain contracts - pub local_path: Option, - /// Whether to enable phase guard (user approval between phases) - #[serde(default)] - pub phase_guard: bool, -} - -/// Response from initializing a directive-driven chain -#[derive(Debug, Clone, Serialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct InitChainResponse { - pub chain_id: Uuid, - pub directive_contract_id: Uuid, - pub supervisor_task_id: Option, -} - -// ============================================================================= -// Contract Evaluations (LLM evaluation results for completed contracts) -// ============================================================================= - -/// Evaluation status for chain contracts -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "snake_case")] -pub enum EvaluationStatus { - /// Not yet evaluated - Pending, - /// Currently being evaluated - Evaluating, - /// Evaluation passed - Passed, - /// Evaluation failed - Failed, - /// Contract is being reworked after failed evaluation - Rework, - /// Max retries exceeded, escalated to user - Escalated, - /// User approved despite partial failure - ApprovedWithIssues, -} - -impl std::fmt::Display for EvaluationStatus { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Pending => write!(f, "pending"), - Self::Evaluating => write!(f, "evaluating"), - Self::Passed => write!(f, "passed"), - Self::Failed => write!(f, "failed"), - Self::Rework => write!(f, "rework"), - Self::Escalated => write!(f, "escalated"), - Self::ApprovedWithIssues => write!(f, "approved_with_issues"), - } - } -} - -impl std::str::FromStr for EvaluationStatus { - type Err = String; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "pending" => Ok(Self::Pending), - "evaluating" => Ok(Self::Evaluating), - "passed" => Ok(Self::Passed), - "failed" => Ok(Self::Failed), - "rework" => Ok(Self::Rework), - "escalated" => Ok(Self::Escalated), - "approved_with_issues" => Ok(Self::ApprovedWithIssues), - _ => Err(format!("Unknown evaluation status: {}", s)), - } - } -} - -/// Contract evaluation - LLM evaluation result after contract completion -#[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct ContractEvaluation { - pub id: Uuid, - pub contract_id: Uuid, - pub chain_id: Option, - pub chain_contract_id: Option, - /// Evaluation attempt number (1-based) - pub evaluation_number: i32, - /// Model used for evaluation - pub evaluator_model: Option, - /// Whether the evaluation passed - pub passed: bool, - /// Overall score (0.0-1.0) - pub overall_score: Option, - /// Per-criterion results as JSON - #[sqlx(json)] - pub criteria_results: serde_json::Value, - /// Summary feedback from the evaluator - pub summary_feedback: String, - /// Instructions for rework if evaluation failed - pub rework_instructions: Option, - /// Snapshot of directive at evaluation time - pub directive_snapshot: Option, - /// Snapshot of deliverables at evaluation time - pub deliverables_snapshot: Option, - pub started_at: DateTime, - pub completed_at: Option>, - pub created_at: DateTime, -} - -/// Per-criterion evaluation result -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct EvaluationCriterionResult { - pub criterion_id: String, - pub criterion_text: String, - pub passed: bool, - /// Score (0.0-1.0) - pub score: f64, - pub feedback: String, - /// Evidence supporting the evaluation - pub evidence: Vec, -} - -/// Request to create a contract evaluation -#[derive(Debug, Clone, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct CreateContractEvaluationRequest { - pub contract_id: Uuid, - pub chain_id: Option, - pub chain_contract_id: Option, - pub evaluator_model: Option, - pub passed: bool, - pub overall_score: Option, - pub criteria_results: Vec, - pub summary_feedback: String, - pub rework_instructions: Option, -} - -/// Summary of contract evaluation for list views -#[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct ContractEvaluationSummary { - pub id: Uuid, - pub contract_id: Uuid, - pub evaluation_number: i32, - pub passed: bool, - pub overall_score: Option, - pub summary_feedback: String, - pub created_at: DateTime, -} - -/// Response listing evaluations for a chain or contract -#[derive(Debug, Clone, Serialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct ContractEvaluationsResponse { - pub evaluations: Vec, - pub total: i64, -} - -/// Traceability matrix entry - maps requirements to contracts -#[derive(Debug, Clone, Serialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct TraceabilityEntry { - pub requirement_id: String, - pub requirement_title: String, - pub contract_definition_ids: Vec, - pub contract_definition_names: Vec, - pub acceptance_criteria_ids: Vec, -} - -/// Response for directive traceability -#[derive(Debug, Clone, Serialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct DirectiveTraceabilityResponse { - pub chain_id: Uuid, - pub entries: Vec, - /// Requirements not mapped to any contract - pub uncovered_requirements: Vec, + pub priority: Option, } // ============================================================================= -- cgit v1.2.3