converge_core/
error.rs

1// Copyright 2024-2025 Aprio One AB, Sweden
2// Author: Kenneth Pernyer, kenneth@aprio.one
3// SPDX-License-Identifier: LicenseRef-Proprietary
4// All rights reserved. This source code is proprietary and confidential.
5// Unauthorized copying, modification, or distribution is strictly prohibited.
6
7//! Error types for Converge.
8
9use serde::{Deserialize, Serialize};
10use thiserror::Error;
11
12use crate::context::Context;
13use crate::invariant::InvariantClass;
14
15/// Top-level error type for Converge operations.
16///
17/// Note: Context is boxed in error variants to keep the error type small,
18/// as recommended by clippy. Access via `error.context()` method.
19#[derive(Debug, Error, Serialize, Deserialize)]
20pub enum ConvergeError {
21    /// Budget limit exceeded (cycles, facts, or time).
22    #[error("budget exhausted: {kind}")]
23    BudgetExhausted { kind: String },
24
25    /// An invariant was violated during execution.
26    #[error("{class:?} invariant '{name}' violated: {reason}")]
27    InvariantViolation {
28        /// Name of the violated invariant.
29        name: String,
30        /// Class of the invariant (Structural, Semantic, Acceptance).
31        class: InvariantClass,
32        /// Reason for the violation.
33        reason: String,
34        /// Final context state (including diagnostic facts). Boxed to reduce error size.
35        context: Box<Context>,
36    },
37
38    /// Agent execution failed.
39    #[error("agent failed: {agent_id}")]
40    AgentFailed { agent_id: String },
41
42    /// Conflicting facts detected for the same ID.
43    #[error(
44        "conflict detected for fact '{id}': existing content '{existing}' vs new content '{new}'"
45    )]
46    Conflict {
47        /// ID of the conflicting fact.
48        id: String,
49        /// Existing content.
50        existing: String,
51        /// New conflicting content.
52        new: String,
53        /// Final context state. Boxed to reduce error size.
54        context: Box<Context>,
55    },
56}
57
58impl ConvergeError {
59    /// Returns a reference to the context if this error variant carries one.
60    #[must_use]
61    pub fn context(&self) -> Option<&Context> {
62        match self {
63            Self::InvariantViolation { context, .. } | Self::Conflict { context, .. } => {
64                Some(context)
65            }
66            Self::BudgetExhausted { .. } | Self::AgentFailed { .. } => None,
67        }
68    }
69}