Update an intent's fields.
(
&self,
intent_id: &str,
options: IntentUpdateOptions,
)
| 414 | |
| 415 | /// Update an intent's fields. |
| 416 | pub fn vault_intent_update( |
| 417 | &self, |
| 418 | intent_id: &str, |
| 419 | options: IntentUpdateOptions, |
| 420 | ) -> Result<IntentInfo, RepositoryError> { |
| 421 | let full_id = self.normalize_intent_id(intent_id)?; |
| 422 | let intent_file = |
| 423 | self.find_intent_path(&full_id)? |
| 424 | .ok_or_else(|| RepositoryError::InvalidOperation { |
| 425 | message: format!("Intent '{}' not found", full_id), |
| 426 | })?; |
| 427 | |
| 428 | // Read from disk first so we pick up any user/agent edits to the |
| 429 | // markdown body. If the file doesn't exist on disk, fall back to |
| 430 | // the redb entry. |
| 431 | let disk_path = self.vault_dir().join(&intent_file); |
| 432 | let (content_bytes, frontmatter_json) = if disk_path.exists() { |
| 433 | let file_content = std::fs::read_to_string(&disk_path)?; |
| 434 | let (fm_json, body) = crate::repository::vault::parse_vault_frontmatter(&file_content); |
| 435 | (body.into_bytes(), fm_json) |
| 436 | } else { |
| 437 | let entry = self.vault_retrieve(&intent_file)?.ok_or_else(|| { |
| 438 | RepositoryError::InvalidOperation { |
| 439 | message: format!("Intent '{}' not found", full_id), |
| 440 | } |
| 441 | })?; |
| 442 | (entry.content_bytes.clone(), entry.frontmatter_json.clone()) |
| 443 | }; |
| 444 | |
| 445 | // Update frontmatter |
| 446 | let mut fm: serde_json::Map<String, serde_json::Value> = |
| 447 | serde_json::from_str(&frontmatter_json).unwrap_or_default(); |
| 448 | |
| 449 | if options.content.is_some() && !options.force { |
| 450 | let manifest = self.vault_manifest()?; |
| 451 | let summary = manifest.intents.get(&full_id); |
| 452 | let current_status = summary |
| 453 | .map(|intent| intent.status.as_str()) |
| 454 | .or_else(|| fm.get("status").and_then(|value| value.as_str())) |
| 455 | .unwrap_or("unknown"); |
| 456 | let has_linked_goal = summary.is_some_and(|intent| intent.goals > 0) |
| 457 | || manifest.goals.values().any(|goal| { |
| 458 | goal.intent |
| 459 | .as_deref() |
| 460 | .is_some_and(|id| id.eq_ignore_ascii_case(&full_id)) |
| 461 | }); |
| 462 | |
| 463 | if current_status != "backlog" || has_linked_goal { |
| 464 | return Err(RepositoryError::InvalidOperation { |
| 465 | message: format!( |
| 466 | "Intent '{}' has started or is linked to a goal; rewriting its body would \ |
| 467 | change the execution context. Retry with force enabled.", |
| 468 | full_id |
| 469 | ), |
| 470 | }); |
| 471 | } |
| 472 | } |
| 473 |