1use std::collections::HashMap;
18use std::sync::{Arc, RwLock};
19use std::time::{SystemTime, UNIX_EPOCH};
20#[derive(Debug, Clone, PartialEq)]
21pub enum MutationType {
22 Addition,
24 Modification,
26 Removal,
28 ConfigChange,
30 NewModule,
32}
33
34#[derive(Debug, Clone, PartialEq)]
35pub enum MutationStatus {
36 Proposed,
37 InProgress,
38 Compiled,
39 Tested,
40 Active,
41 RolledBack,
42 Failed(String),
43}
44
45#[derive(Debug, Clone)]
46pub struct Mutation {
47 pub id: u64,
48 pub name: String,
49 pub mutation_type: MutationType,
50 pub target_module: String,
51 pub source_code: String,
52 pub version: u32,
53 pub status: MutationStatus,
54 pub reason: String,
55 pub created_ms: u64,
56 pub applied_ms: Option<u64>,
57 pub rollback_version: u32,
58 pub signature: Vec<u8>,
59 pub author: String, }
61
62#[derive(Debug, Clone)]
63pub struct SourceTemplate {
64 pub name: String,
65 pub target_module: String,
66 pub template: String,
67 pub variables: Vec<String>,
68 pub description: String,
69}
70
71#[derive(Debug, Clone)]
72pub struct RollbackPoint {
73 pub id: u64,
74 pub timestamp_ms: u64,
75 pub module: String,
76 pub version: u32,
77 pub snapshot: Vec<u8>,
78 pub reason: String,
79}
80
81#[derive(Debug, Clone)]
82pub struct AutopoiesisConfig {
83 pub max_mutations: usize,
84 pub require_signature: bool,
85 pub auto_rollback_on_failure: bool,
86 pub max_rollback_depth: usize,
87 pub test_before_activate: bool,
88}
89
90impl Default for AutopoiesisConfig {
91 fn default() -> Self {
92 Self {
93 max_mutations: 100,
94 require_signature: false,
95 auto_rollback_on_failure: true,
96 max_rollback_depth: 10,
97 test_before_activate: true,
98 }
99 }
100}
101
102pub struct AutopoiesisEngine {
103 mutations: Arc<RwLock<Vec<Mutation>>>,
104 templates: Arc<RwLock<Vec<SourceTemplate>>>,
105 rollback_points: Arc<RwLock<Vec<RollbackPoint>>>,
106 config: Arc<RwLock<AutopoiesisConfig>>,
107 next_id: Arc<RwLock<u64>>,
108}
109
110impl Default for AutopoiesisEngine {
111 fn default() -> Self {
112 Self::new()
113 }
114}
115
116impl AutopoiesisEngine {
117 pub fn new() -> Self {
118 Self {
119 mutations: Arc::new(RwLock::new(Vec::new())),
120 templates: Arc::new(RwLock::new(Vec::new())),
121 rollback_points: Arc::new(RwLock::new(Vec::new())),
122 config: Arc::new(RwLock::new(AutopoiesisConfig::default())),
123 next_id: Arc::new(RwLock::new(1)),
124 }
125 }
126
127 fn nid(&self) -> u64 {
128 let mut id = crate::sync_guard::write_lock(&self.next_id);
129 let n = *id;
130 *id += 1;
131 n
132 }
133 fn now(&self) -> u64 {
134 SystemTime::now()
135 .duration_since(UNIX_EPOCH)
136 .unwrap_or_default()
137 .as_millis() as u64
138 }
139
140 pub fn register_template(
143 &self,
144 name: &str,
145 target_module: &str,
146 template: &str,
147 variables: Vec<String>,
148 desc: &str,
149 ) {
150 crate::sync_guard::write_lock(&self.templates).push(SourceTemplate {
151 name: name.to_string(),
152 target_module: target_module.to_string(),
153 template: template.to_string(),
154 variables,
155 description: desc.to_string(),
156 });
157 }
158
159 pub fn list_templates(&self) -> Vec<SourceTemplate> {
160 crate::sync_guard::read_lock(&self.templates).clone()
161 }
162
163 pub fn generate_from_template(
165 &self,
166 template_name: &str,
167 var_values: &HashMap<String, String>,
168 ) -> Option<String> {
169 let templates = crate::sync_guard::read_lock(&self.templates);
170 let tmpl = templates.iter().find(|t| t.name == template_name)?;
171 let mut code = tmpl.template.clone();
172 for (key, value) in var_values {
173 code = code.replace(&format!("{{{{{}}}}}", key), value);
174 }
175 Some(code)
176 }
177
178 pub fn propose_mutation(
182 &self,
183 name: &str,
184 mtype: MutationType,
185 target: &str,
186 code: &str,
187 reason: &str,
188 author: &str,
189 ) -> u64 {
190 let id = self.nid();
191 let mutation = Mutation {
192 id,
193 name: name.to_string(),
194 mutation_type: mtype,
195 target_module: target.to_string(),
196 source_code: code.to_string(),
197 version: self.current_version(target),
198 status: MutationStatus::Proposed,
199 reason: reason.to_string(),
200 created_ms: self.now(),
201 applied_ms: None,
202 rollback_version: 0,
203 signature: vec![],
204 author: author.to_string(),
205 };
206 crate::sync_guard::write_lock(&self.mutations).push(mutation);
207 id
208 }
209
210 pub fn apply_mutation(&self, id: u64) -> bool {
212 let config = crate::sync_guard::read_lock(&self.config).clone();
213 let mut mutations = crate::sync_guard::write_lock(&self.mutations);
214 let mutation = match mutations
215 .iter_mut()
216 .find(|m| m.id == id && m.status == MutationStatus::Proposed)
217 {
218 Some(m) => m,
219 None => return false,
220 };
221
222 let rp = RollbackPoint {
224 id: self.nid(),
225 timestamp_ms: self.now(),
226 module: mutation.target_module.clone(),
227 version: mutation.version,
228 snapshot: mutation.source_code.as_bytes().to_vec(),
229 reason: format!("Before applying: {}", mutation.name),
230 };
231 crate::sync_guard::write_lock(&self.rollback_points).push(rp);
232
233 mutation.status = MutationStatus::InProgress;
234 mutation.version += 1;
235 mutation.applied_ms = Some(self.now());
236
237 if config.test_before_activate {
239 mutation.status = MutationStatus::Compiled;
240 } else {
241 mutation.status = MutationStatus::Active;
242 }
243
244 true
245 }
246
247 pub fn rollback(&self, mutation_id: u64) -> bool {
249 let mutations = crate::sync_guard::read_lock(&self.mutations);
250 let mutation = match mutations.iter().find(|m| m.id == mutation_id) {
251 Some(m) => m,
252 None => return false,
253 };
254 let rollbacks = crate::sync_guard::read_lock(&self.rollback_points);
255 let _rp = match rollbacks
256 .iter()
257 .rfind(|r| r.module == mutation.target_module)
258 {
259 Some(r) => r,
260 None => return false,
261 };
262
263 drop(mutations);
265 if let Some(m) = crate::sync_guard::write_lock(&self.mutations)
266 .iter_mut()
267 .find(|m| m.id == mutation_id)
268 {
269 m.status = MutationStatus::RolledBack;
270 }
271
272 true
273 }
274
275 pub fn mutation_status(&self, id: u64) -> Option<MutationStatus> {
277 crate::sync_guard::read_lock(&self.mutations)
278 .iter()
279 .find(|m| m.id == id)
280 .map(|m| m.status.clone())
281 }
282
283 pub fn list_mutations(&self, status: Option<&MutationStatus>) -> Vec<Mutation> {
284 crate::sync_guard::read_lock(&self.mutations)
285 .iter()
286 .filter(|m| status.is_none_or(|s| m.status == *s))
287 .cloned()
288 .collect()
289 }
290
291 fn current_version(&self, module: &str) -> u32 {
292 crate::sync_guard::read_lock(&self.mutations)
293 .iter()
294 .filter(|m| m.target_module == module)
295 .map(|m| m.version)
296 .max()
297 .unwrap_or(0)
298 }
299
300 pub fn propose_fix(&self, goal_name: &str, target_module: &str, description: &str) -> u64 {
304 let code = format!(
305 "// TODO: {} — generated by autopoiesis for '{}'\n// Target: {}\n",
306 description, goal_name, target_module
307 );
308 self.propose_mutation(
309 &format!("fix_{}_{}", target_module, self.nid()),
310 MutationType::Modification,
311 target_module,
312 &code,
313 &format!("Planning goal: {} — {}", goal_name, description),
314 "planning",
315 )
316 }
317
318 pub fn stats(&self) -> (usize, usize, usize, usize) {
321 let m = crate::sync_guard::read_lock(&self.mutations);
322 (
323 m.len(),
324 m.iter()
325 .filter(|m| m.status == MutationStatus::Active)
326 .count(),
327 m.iter()
328 .filter(|m| m.status == MutationStatus::RolledBack)
329 .count(),
330 crate::sync_guard::read_lock(&self.rollback_points).len(),
331 )
332 }
333}