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