diff options
Diffstat (limited to 'makima/src/db/models.rs')
| -rw-r--r-- | makima/src/db/models.rs | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/makima/src/db/models.rs b/makima/src/db/models.rs index cef0a22..45ddb52 100644 --- a/makima/src/db/models.rs +++ b/makima/src/db/models.rs @@ -1446,6 +1446,9 @@ pub struct Contract { /// Use `get_phase_config()` to get the parsed PhaseConfig. #[serde(skip_serializing_if = "Option::is_none")] pub phase_config: Option<serde_json::Value>, + /// Chain ID if this contract is part of a chain (DAG of contracts) + #[serde(skip_serializing_if = "Option::is_none")] + pub chain_id: Option<Uuid>, pub version: i32, pub created_at: DateTime<Utc>, pub updated_at: DateTime<Utc>, @@ -2586,6 +2589,359 @@ pub struct HeartbeatHistoryQuery { } // ============================================================================= +// Chains (DAG of contracts for multi-contract orchestration) +// ============================================================================= + +/// Chain status determines the overall state of the chain +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "lowercase")] +pub enum ChainStatus { + /// Chain is actively running + Active, + /// All contracts completed successfully + Completed, + /// Chain was manually archived + Archived, +} + +impl Default for ChainStatus { + fn default() -> Self { + ChainStatus::Active + } +} + +impl std::fmt::Display for ChainStatus { + 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"), + } + } +} + +impl std::str::FromStr for ChainStatus { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s.to_lowercase().as_str() { + "active" => Ok(ChainStatus::Active), + "completed" => Ok(ChainStatus::Completed), + "archived" => Ok(ChainStatus::Archived), + _ => Err(format!("Invalid chain status: {}", s)), + } + } +} + +/// Chain - a directed acyclic graph (DAG) of contracts +/// Fits Makima's control theme - she controls through invisible chains +#[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct Chain { + pub id: Uuid, + pub owner_id: Uuid, + pub name: String, + pub description: Option<String>, + 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<i32>, + /// Current loop iteration count + pub loop_current_iteration: Option<i32>, + /// Progress check prompt/criteria for evaluating loop completion + pub loop_progress_check: Option<String>, + /// Repository URL for contracts in this chain (optional) + pub repository_url: Option<String>, + /// Local path for contracts in this chain (optional) + pub local_path: Option<String>, + /// Version for optimistic locking + pub version: i32, + pub created_at: DateTime<Utc>, + pub updated_at: DateTime<Utc>, +} + +impl Chain { + /// Parse status string to ChainStatus enum + pub fn status_enum(&self) -> Result<ChainStatus, String> { + self.status.parse() + } +} + +/// Chain contract link - links contracts to chains with DAG dependency info +#[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ChainContract { + pub id: Uuid, + pub chain_id: Uuid, + pub contract_id: Uuid, + /// Contract IDs this contract depends on (DAG edges) + #[sqlx(default)] + pub depends_on: Vec<Uuid>, + /// Order for display/processing (topological sort order) + pub order_index: i32, + /// X position for GUI editor + pub editor_x: Option<f64>, + /// Y position for GUI editor + pub editor_y: Option<f64>, + pub created_at: DateTime<Utc>, +} + +/// Chain event for audit trail +#[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ChainEvent { + pub id: Uuid, + pub chain_id: Uuid, + pub event_type: String, + pub contract_id: Option<Uuid>, + #[sqlx(json)] + pub event_data: Option<serde_json::Value>, + pub created_at: DateTime<Utc>, +} + +/// Summary of a chain for list views +#[derive(Debug, Clone, FromRow, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ChainSummary { + pub id: Uuid, + pub name: String, + pub description: Option<String>, + pub status: String, + pub loop_enabled: bool, + pub loop_current_iteration: Option<i32>, + pub contract_count: i64, + pub completed_count: i64, + pub version: i32, + pub created_at: DateTime<Utc>, +} + +/// Chain with contracts for detail view +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ChainWithContracts { + #[serde(flatten)] + pub chain: Chain, + pub contracts: Vec<ChainContractDetail>, +} + +/// Contract detail within a chain (includes contract info + chain link info) +#[derive(Debug, Clone, FromRow, Serialize, 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<Uuid>, + pub order_index: i32, + pub editor_x: Option<f64>, + pub editor_y: Option<f64>, +} + +/// DAG graph structure for visualization +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ChainGraphResponse { + pub chain_id: Uuid, + pub chain_name: String, + pub chain_status: String, + pub nodes: Vec<ChainGraphNode>, + pub edges: Vec<ChainGraphEdge>, +} + +/// Node in chain DAG graph +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ChainGraphNode { + pub id: Uuid, + pub contract_id: Uuid, + pub name: String, + pub status: String, + pub phase: String, + pub x: f64, + pub y: f64, +} + +/// Edge in chain DAG graph +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ChainGraphEdge { + pub from: Uuid, + pub to: Uuid, +} + +/// Response for chain list endpoint +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ChainListResponse { + pub chains: Vec<ChainSummary>, + pub total: i64, +} + +/// Request payload for creating a new chain +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct CreateChainRequest { + /// Name of the chain + pub name: String, + /// Optional description + pub description: Option<String>, + /// Repository URL for contracts in this chain + pub repository_url: Option<String>, + /// Local path for contracts in this chain + pub local_path: Option<String>, + /// Enable loop mode for iterative execution + #[serde(default)] + pub loop_enabled: Option<bool>, + /// Maximum loop iterations (default: 10) + pub loop_max_iterations: Option<i32>, + /// Progress check prompt for evaluating loop completion + pub loop_progress_check: Option<String>, + /// Contracts to create within this chain + pub contracts: Option<Vec<CreateChainContractRequest>>, +} + +/// Request to create a contract within a chain +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct CreateChainContractRequest { + /// Name of the contract + pub name: String, + /// Optional description + pub description: Option<String>, + /// Contract type + #[serde(default)] + pub contract_type: Option<String>, + /// Initial phase + pub initial_phase: Option<String>, + /// Phases for the contract + pub phases: Option<Vec<String>>, + /// Names of contracts this depends on (resolved to IDs) + pub depends_on: Option<Vec<String>>, + /// Tasks to create in this contract + pub tasks: Option<Vec<CreateChainTaskRequest>>, + /// Deliverables for this contract + pub deliverables: Option<Vec<CreateChainDeliverableRequest>>, + /// Position in GUI editor + pub editor_x: Option<f64>, + pub editor_y: Option<f64>, +} + +/// Task definition within a chain contract +#[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 id: String, + pub name: String, + pub priority: Option<String>, +} + +/// Request to update an existing chain +#[derive(Debug, Clone, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct UpdateChainRequest { + pub name: Option<String>, + pub description: Option<String>, + pub status: Option<String>, + pub loop_enabled: Option<bool>, + pub loop_max_iterations: Option<i32>, + pub loop_progress_check: Option<String>, + /// Version for optimistic locking + pub version: Option<i32>, +} + +/// 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<Uuid>, + /// Or create a new contract with this definition + pub new_contract: Option<CreateChainContractRequest>, + /// Contract IDs this depends on + pub depends_on: Option<Vec<Uuid>>, + /// Position in GUI editor + pub editor_x: Option<f64>, + pub editor_y: Option<f64>, +} + +/// Editor data model for GUI chain editor +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ChainEditorData { + pub id: Option<Uuid>, + pub name: String, + pub description: Option<String>, + pub repository_url: Option<String>, + pub local_path: Option<String>, + pub loop_enabled: bool, + pub loop_max_iterations: Option<i32>, + pub loop_progress_check: Option<String>, + pub nodes: Vec<ChainEditorNode>, + pub edges: Vec<ChainEditorEdge>, +} + +/// 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<String>, + #[serde(rename = "type")] + pub contract_type: String, + pub phases: Vec<String>, + pub tasks: Vec<ChainEditorTask>, + pub deliverables: Vec<ChainEditorDeliverable>, +} + +/// Task in chain editor +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ChainEditorTask { + pub name: String, + pub plan: String, +} + +/// Deliverable in chain editor +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ChainEditorDeliverable { + pub id: String, + pub name: String, + pub priority: String, +} + +/// Edge in chain editor +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ChainEditorEdge { + pub from: String, + pub to: String, +} + +// ============================================================================= // Unit Tests // ============================================================================= |
