diff options
Diffstat (limited to 'makima/src/db')
| -rw-r--r-- | makima/src/db/models.rs | 24 | ||||
| -rw-r--r-- | makima/src/db/repository.rs | 55 |
2 files changed, 79 insertions, 0 deletions
diff --git a/makima/src/db/models.rs b/makima/src/db/models.rs index 0c1d9f2..95517a1 100644 --- a/makima/src/db/models.rs +++ b/makima/src/db/models.rs @@ -1321,6 +1321,11 @@ pub struct Contract { /// phase outputs (like plans, requirements, etc.) before continuing. #[serde(default)] pub phase_guard: bool, + /// Completed deliverables per phase. + /// Structure: { "plan": ["plan-document"], "execute": ["pull-request"] } + #[sqlx(json)] + #[serde(default)] + pub completed_deliverables: serde_json::Value, pub version: i32, pub created_at: DateTime<Utc>, pub updated_at: DateTime<Utc>, @@ -1374,6 +1379,25 @@ impl Contract { _ => ContractPhase::Execute, // simple and execute both end at execute } } + + /// Get completed deliverable IDs for a specific phase + pub fn get_completed_deliverables(&self, phase: &str) -> Vec<String> { + self.completed_deliverables + .get(phase) + .and_then(|v| v.as_array()) + .map(|arr| { + arr.iter() + .filter_map(|v| v.as_str().map(String::from)) + .collect() + }) + .unwrap_or_default() + } + + /// Check if a specific deliverable is marked as complete for a phase + pub fn is_deliverable_complete(&self, phase: &str, deliverable_id: &str) -> bool { + self.get_completed_deliverables(phase) + .contains(&deliverable_id.to_string()) + } } /// Contract repository record from the database diff --git a/makima/src/db/repository.rs b/makima/src/db/repository.rs index d3e4c56..b55b05e 100644 --- a/makima/src/db/repository.rs +++ b/makima/src/db/repository.rs @@ -3018,6 +3018,61 @@ pub async fn update_contract_supervisor( .await } +/// Mark a deliverable as complete for a specific phase. +/// Uses JSONB operations to append the deliverable_id to the phase's array. +pub async fn mark_deliverable_complete( + pool: &PgPool, + contract_id: Uuid, + phase: &str, + deliverable_id: &str, +) -> Result<Contract, sqlx::Error> { + // Use jsonb_set to add the deliverable to the phase's array + // If the phase key doesn't exist, create an empty array first + // COALESCE handles the case where the phase array doesn't exist yet + sqlx::query_as::<_, Contract>( + r#" + UPDATE contracts + SET completed_deliverables = jsonb_set( + completed_deliverables, + ARRAY[$2::text], + COALESCE(completed_deliverables->$2, '[]'::jsonb) || to_jsonb($3::text), + true + ), + updated_at = NOW() + WHERE id = $1 + AND NOT (COALESCE(completed_deliverables->$2, '[]'::jsonb) ? $3) + RETURNING * + "#, + ) + .bind(contract_id) + .bind(phase) + .bind(deliverable_id) + .fetch_one(pool) + .await +} + +/// Clear all completed deliverables for a specific phase. +/// Used when phase changes or deliverables need to be reset. +pub async fn clear_phase_deliverables( + pool: &PgPool, + contract_id: Uuid, + phase: &str, +) -> Result<Contract, sqlx::Error> { + sqlx::query_as::<_, Contract>( + r#" + UPDATE contracts + SET completed_deliverables = completed_deliverables - $2, + updated_at = NOW() + WHERE id = $1 + RETURNING * + "#, + ) + .bind(contract_id) + .bind(phase) + .fetch_one(pool) + .await +} + /// Get the supervisor task for a contract. pub async fn get_contract_supervisor_task( pool: &PgPool, |
