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 thiserror::Error;
10
11use crate::context::Context;
12use crate::invariant::InvariantClass;
13
14/// Top-level error type for Converge operations.
15///
16/// Note: Context is boxed in error variants to keep the error type small,
17/// as recommended by clippy. Access via `error.context()` method.
18#[derive(Debug, Error)]
19pub enum ConvergeError {
20    /// Budget limit exceeded (cycles, facts, or time).
21    #[error("budget exhausted: {kind}")]
22    BudgetExhausted { kind: String },
23
24    /// An invariant was violated during execution.
25    #[error("{class:?} invariant '{name}' violated: {reason}")]
26    InvariantViolation {
27        /// Name of the violated invariant.
28        name: String,
29        /// Class of the invariant (Structural, Semantic, Acceptance).
30        class: InvariantClass,
31        /// Reason for the violation.
32        reason: String,
33        /// Final context state (including diagnostic facts). Boxed to reduce error size.
34        context: Box<Context>,
35    },
36
37    /// Agent execution failed.
38    #[error("agent failed: {agent_id}")]
39    AgentFailed { agent_id: String },
40
41    /// Conflicting facts detected for the same ID.
42    #[error("conflict detected for fact '{id}': existing content '{existing}' vs new content '{new}'")]
43    Conflict {
44        /// ID of the conflicting fact.
45        id: String,
46        /// Existing content.
47        existing: String,
48        /// New conflicting content.
49        new: String,
50        /// Final context state. Boxed to reduce error size.
51        context: Box<Context>,
52    },
53}
54
55impl ConvergeError {
56    /// Returns a reference to the context if this error variant carries one.
57    #[must_use]
58    pub fn context(&self) -> Option<&Context> {
59        match self {
60            Self::InvariantViolation { context, .. } | Self::Conflict { context, .. } => {
61                Some(context)
62            }
63            Self::BudgetExhausted { .. } | Self::AgentFailed { .. } => None,
64        }
65    }
66}