diff --git a/maskinrommet/src/intentions.rs b/maskinrommet/src/intentions.rs index 2e309e1..2af475a 100644 --- a/maskinrommet/src/intentions.rs +++ b/maskinrommet/src/intentions.rs @@ -317,6 +317,69 @@ fn validate_orchestration_metadata( Ok(()) } +/// Validerer metadata for oppgave-noder (proposal, assignment, task). +/// +/// Ref: docs/infra/oppgaver.md +fn validate_task_metadata( + node_kind: &str, + metadata: &serde_json::Value, +) -> Result<(), String> { + match node_kind { + "proposal" => { + // Valgfri status, default "draft" + if let Some(status) = metadata.get("status").and_then(|v| v.as_str()) { + let valid = ["draft", "discussed", "approved", "rejected", "parked"]; + if !valid.contains(&status) { + return Err(format!( + "proposal metadata.status må være en av: {}", + valid.join(", ") + )); + } + } + Ok(()) + } + "assignment" => { + // Valgfri status, default "open" + if let Some(status) = metadata.get("status").and_then(|v| v.as_str()) { + let valid = ["open", "planning", "active", "paused", "done", "blocked"]; + if !valid.contains(&status) { + return Err(format!( + "assignment metadata.status må være en av: {}", + valid.join(", ") + )); + } + } + // Valgfri priority (heltall) + if let Some(val) = metadata.get("priority") { + if !val.is_number() { + return Err("assignment metadata.priority må være et tall".into()); + } + } + Ok(()) + } + "task" => { + // Valgfri status, default "open" + if let Some(status) = metadata.get("status").and_then(|v| v.as_str()) { + let valid = ["open", "active", "done", "failed", "skipped"]; + if !valid.contains(&status) { + return Err(format!( + "task metadata.status må være en av: {}", + valid.join(", ") + )); + } + } + // Valgfri priority (heltall) + if let Some(val) = metadata.get("priority") { + if !val.is_number() { + return Err("task metadata.priority må være et tall".into()); + } + } + Ok(()) + } + _ => Ok(()), + } +} + #[derive(Serialize)] pub struct ErrorResponse { pub error: String, @@ -649,6 +712,9 @@ pub async fn create_node( // -- Valider metadata for orchestration-noder (oppgave 24.1) -- validate_orchestration_metadata(&node_kind, &metadata).map_err(|e| bad_request(&e))?; + // -- Valider metadata for oppgave-noder (proposal/assignment/task) -- + validate_task_metadata(&node_kind, &metadata).map_err(|e| bad_request(&e))?; + // -- Kontekstbasert identitet (oppgave 8.2) -- // Hvis context_id er satt, sjekk om brukeren har et alias som er // deltaker i kommunikasjonsnoden. I så fall brukes aliaset som created_by. @@ -1186,6 +1252,9 @@ pub async fn update_node( // -- Valider metadata for orchestration-noder (oppgave 24.1) -- validate_orchestration_metadata(&node_kind, &metadata).map_err(|e| bad_request(&e))?; + // -- Valider metadata for oppgave-noder -- + validate_task_metadata(&node_kind, &metadata).map_err(|e| bad_request(&e))?; + // -- Beskytt model_profile for egendefinerte AI-presets (oppgave 18.6) -- // Kun admin/owner på en samling kan endre model_profile. Vanlige brukere // som eier et custom preset kan redigere alt annet (prompt, icon, farge osv.)