From f84a7f2d820f6f432be2b1d78d6bf833b5b19380 Mon Sep 17 00:00:00 2001 From: soryu Date: Fri, 16 Jan 2026 17:07:44 +0000 Subject: Fixup: fix history call and try to start pending tasks when a daemon is available --- makima/src/db/models.rs | 38 ++++++++++++++++++++++++++++++++++++++ makima/src/db/repository.rs | 21 +++++++++++++++++++++ 2 files changed, 59 insertions(+) (limited to 'makima/src/db') diff --git a/makima/src/db/models.rs b/makima/src/db/models.rs index 6accb48..0e1303c 100644 --- a/makima/src/db/models.rs +++ b/makima/src/db/models.rs @@ -6,6 +6,42 @@ use sqlx::FromRow; use utoipa::ToSchema; use uuid::Uuid; +/// Flexible datetime deserialization module. +/// Accepts both date-only ("2026-01-15") and full ISO 8601 datetime ("2026-01-15T00:00:00Z") formats. +pub mod flexible_datetime { + use chrono::{DateTime, NaiveDate, NaiveTime, TimeZone, Utc}; + use serde::{self, Deserialize, Deserializer}; + + /// Deserializes a datetime from either date-only or full datetime format. + pub fn deserialize<'de, D>(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + { + let s: Option = Option::deserialize(deserializer)?; + match s { + None => Ok(None), + Some(s) if s.is_empty() => Ok(None), + Some(s) => { + // Try full datetime first (RFC 3339 / ISO 8601) + if let Ok(dt) = DateTime::parse_from_rfc3339(&s) { + return Ok(Some(dt.with_timezone(&Utc))); + } + + // Try date-only format (YYYY-MM-DD) and convert to start of day UTC + if let Ok(date) = NaiveDate::parse_from_str(&s, "%Y-%m-%d") { + let datetime = date.and_time(NaiveTime::MIN); + return Ok(Some(Utc.from_utc_datetime(&datetime))); + } + + Err(serde::de::Error::custom(format!( + "Invalid datetime format '{}'. Expected ISO 8601 datetime (e.g., '2026-01-15T00:00:00Z') or date (e.g., '2026-01-15')", + s + ))) + } + } + } +} + /// TranscriptEntry stored in JSONB - matches frontend TranscriptEntry #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] @@ -1646,7 +1682,9 @@ pub struct ToolCallInfo { pub struct HistoryQueryFilters { pub phase: Option, pub event_types: Option>, + #[serde(default, deserialize_with = "flexible_datetime::deserialize")] pub from: Option>, + #[serde(default, deserialize_with = "flexible_datetime::deserialize")] pub to: Option>, pub limit: Option, pub cursor: Option, diff --git a/makima/src/db/repository.rs b/makima/src/db/repository.rs index cb9d52f..2b069d5 100644 --- a/makima/src/db/repository.rs +++ b/makima/src/db/repository.rs @@ -789,6 +789,27 @@ pub async fn list_tasks_by_contract( .await } +/// Get pending tasks for a contract (non-supervisor tasks only). +pub async fn get_pending_tasks_for_contract( + pool: &PgPool, + contract_id: Uuid, + owner_id: Uuid, +) -> Result, sqlx::Error> { + sqlx::query_as::<_, Task>( + r#" + SELECT * FROM tasks + WHERE contract_id = $1 AND owner_id = $2 + AND status = 'pending' + AND is_supervisor = false + ORDER BY priority DESC, created_at ASC + "#, + ) + .bind(contract_id) + .bind(owner_id) + .fetch_all(pool) + .await +} + /// Update a task by ID with optimistic locking. pub async fn update_task( pool: &PgPool, -- cgit v1.2.3