summaryrefslogtreecommitdiff
path: root/makima/src/db/models.rs
diff options
context:
space:
mode:
Diffstat (limited to 'makima/src/db/models.rs')
-rw-r--r--makima/src/db/models.rs356
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
// =============================================================================