1use std::path::PathBuf;
2
3pub use forge_policy::{Violation, ViolationKind};
4
5#[derive(Debug, thiserror::Error)]
7pub enum ForgeError {
8 #[error("refuse to open database: {reason}")]
9 RefuseToOpenDb { reason: String },
10
11 #[error("database error: {0}")]
12 Database(#[from] rusqlite::Error),
13
14 #[error("IO error: {0}")]
15 Io(#[from] std::io::Error),
16
17 #[error("serialization error: {0}")]
18 Serialization(#[from] serde_json::Error),
19
20 #[error("patch validation failed: {violations:?}")]
21 PatchValidation { violations: Vec<Violation> },
22
23 #[error("anchor resolution failed: {0}")]
24 AnchorResolution(String),
25
26 #[error("patch apply failed: {0}")]
27 PatchApply(String),
28
29 #[error("command timeout after {timeout_secs}s: {command}")]
30 CommandTimeout { command: String, timeout_secs: u64 },
31
32 #[error("command failed (exit {exit_code}): {command}")]
33 CommandFailed { command: String, exit_code: i32 },
34
35 #[error("sealed mode unsupported for runtime: {runtime}")]
36 SealedModeUnsupported { runtime: String },
37
38 #[error("remote model forbidden in sealed mode")]
39 RemoteModelForbiddenInSealedMode,
40
41 #[error("no container runtime found")]
42 NoContainerRuntime,
43
44 #[error("promotion failed: {criterion} = {value}")]
45 PromotionFailed { criterion: String, value: String },
46
47 #[error("golden mindstate mismatch: version={version_id}, input_hash={input_hash}")]
48 GoldenMindStateMismatch {
49 version_id: String,
50 input_hash: String,
51 },
52
53 #[error("CEA: raw source detected in node")]
54 CeaRawSourceDetected,
55
56 #[error("fixture error: {0}")]
57 Fixture(String),
58
59 #[error("config error: {0}")]
60 Config(String),
61
62 #[error("not found: {0}")]
63 NotFound(String),
64
65 #[error("workspace path error: {0}")]
66 WorkspacePath(PathBuf),
67
68 #[error("experiment failed: {0}")]
69 ExperimentFailed(String),
70
71 #[error("limit exceeded: {limit} (value={value}, max={max})")]
72 LimitExceeded { limit: String, value: u64, max: u64 },
73
74 #[error("export error: {0}")]
75 Export(String),
76
77 #[error("write-through blocked: danger-sm-write feature not enabled")]
78 WriteThroughBlocked,
79
80 #[error("pair incomparable: {reasons:?}")]
81 PairIncomparable { reasons: Vec<String> },
82
83 #[error("sealed bundle cannot be mutated")]
84 SealedBundle,
85
86 #[error("{0}")]
87 Other(String),
88}
89
90impl ForgeError {
91 pub fn kind(&self) -> &'static str {
93 match self {
94 Self::RefuseToOpenDb { .. } => "refuse_to_open_db",
95 Self::Database(_) => "database",
96 Self::Io(_) => "io",
97 Self::Serialization(_) => "serialization",
98 Self::PatchValidation { .. } => "patch_validation",
99 Self::AnchorResolution(_) => "anchor_resolution",
100 Self::PatchApply(_) => "patch_apply",
101 Self::CommandTimeout { .. } => "command_timeout",
102 Self::CommandFailed { .. } => "command_failed",
103 Self::SealedModeUnsupported { .. } => "sealed_mode_unsupported",
104 Self::RemoteModelForbiddenInSealedMode => "remote_model_forbidden",
105 Self::NoContainerRuntime => "no_container_runtime",
106 Self::PromotionFailed { .. } => "promotion_failed",
107 Self::GoldenMindStateMismatch { .. } => "golden_mindstate_mismatch",
108 Self::CeaRawSourceDetected => "cea_raw_source",
109 Self::Fixture(_) => "fixture",
110 Self::Config(_) => "config",
111 Self::NotFound(_) => "not_found",
112 Self::WorkspacePath(_) => "workspace_path",
113 Self::ExperimentFailed(_) => "experiment_failed",
114 Self::LimitExceeded { .. } => "limit_exceeded",
115 Self::Export(_) => "export",
116 Self::WriteThroughBlocked => "write_through_blocked",
117 Self::PairIncomparable { .. } => "pair_incomparable",
118 Self::SealedBundle => "sealed_bundle",
119 Self::Other(_) => "other",
120 }
121 }
122}
123
124pub type ForgeResult<T> = Result<T, ForgeError>;
125
126impl From<sandbox_workspace::WorkspaceError> for ForgeError {
127 fn from(error: sandbox_workspace::WorkspaceError) -> Self {
128 match error {
129 sandbox_workspace::WorkspaceError::Io(error) => Self::Io(error),
130 sandbox_workspace::WorkspaceError::Policy(error) => Self::Other(error.to_string()),
131 }
132 }
133}
134
135impl From<mindstate_core::MindStateError> for ForgeError {
136 fn from(error: mindstate_core::MindStateError) -> Self {
137 Self::Other(error.to_string())
138 }
139}
140
141impl From<stabilizer_core::StabilizerError> for ForgeError {
142 fn from(error: stabilizer_core::StabilizerError) -> Self {
143 Self::Other(error.to_string())
144 }
145}
146
147impl From<typed_patch::PatchError> for ForgeError {
148 fn from(error: typed_patch::PatchError) -> Self {
149 match error {
150 typed_patch::PatchError::Validation { violations } => {
151 Self::PatchValidation { violations }
152 }
153 typed_patch::PatchError::AnchorResolution(message) => Self::AnchorResolution(message),
154 typed_patch::PatchError::Apply(message) => Self::PatchApply(message),
155 typed_patch::PatchError::Io(error) => Self::Io(error),
156 typed_patch::PatchError::Workspace(error) => Self::from(error),
157 typed_patch::PatchError::Policy(error) => Self::Other(error.to_string()),
158 typed_patch::PatchError::Other(message) => Self::Other(message),
159 }
160 }
161}
162
163impl From<check_runner::RunnerError> for ForgeError {
164 fn from(error: check_runner::RunnerError) -> Self {
165 match error {
166 check_runner::RunnerError::Io(error) => Self::Io(error),
167 check_runner::RunnerError::Policy(error) => Self::Other(error.to_string()),
168 check_runner::RunnerError::Workspace(error) => Self::from(error),
169 check_runner::RunnerError::CommandTimeout {
170 command,
171 timeout_secs,
172 } => Self::CommandTimeout {
173 command,
174 timeout_secs,
175 },
176 check_runner::RunnerError::CommandFailed { command, exit_code } => {
177 Self::CommandFailed { command, exit_code }
178 }
179 check_runner::RunnerError::SealedModeUnsupported { runtime } => {
180 Self::SealedModeUnsupported { runtime }
181 }
182 check_runner::RunnerError::NoContainerRuntime => Self::NoContainerRuntime,
183 check_runner::RunnerError::Other(message) => Self::Other(message),
184 }
185 }
186}
187
188impl From<cea_core::CeaCoreError> for ForgeError {
189 fn from(error: cea_core::CeaCoreError) -> Self {
190 Self::Other(error.to_string())
191 }
192}
193
194impl From<cea_store::CeaStoreError> for ForgeError {
195 fn from(error: cea_store::CeaStoreError) -> Self {
196 match error {
197 cea_store::CeaStoreError::Serialization(error) => Self::Serialization(error),
198 cea_store::CeaStoreError::Backend(message) => Self::Other(message),
199 }
200 }
201}
202
203impl From<semantic_memory_forge::ExportEnvelopeError> for ForgeError {
204 fn from(error: semantic_memory_forge::ExportEnvelopeError) -> Self {
205 Self::Export(error.to_string())
206 }
207}
208
209impl From<forge_memory_bridge::BridgeError> for ForgeError {
210 fn from(error: forge_memory_bridge::BridgeError) -> Self {
211 Self::Export(error.to_string())
212 }
213}
214
215impl From<semantic_memory::MemoryError> for ForgeError {
216 fn from(error: semantic_memory::MemoryError) -> Self {
217 Self::Other(format!("semantic-memory write: {error}"))
218 }
219}