Skip to main content

mockforge_intelligence/ai_studio/
debug_context_integrator.rs

1//! Debug context integrator for collecting context from multiple subsystems
2//!
3//! This module provides functionality to collect debugging context from various
4//! MockForge subsystems (Reality, Contracts, Scenarios, Personas, Chaos) and
5//! combine them into a unified DebugContext for AI-guided debugging.
6
7use crate::ai_studio::debug_context::{
8    ChaosContext, ChaosRule, ContractContext, ContractValidationResult, DebugContext,
9    DriftHistoryEntry, FailureInjectionConfig, PersonaContext, RealityContext, ScenarioContext,
10};
11use crate::reality::{RealityConfig, RealityEngine, RealityLevel};
12use async_trait::async_trait;
13use mockforge_foundation::Result;
14use serde_json::Value;
15use std::collections::HashMap;
16
17/// Trait for accessing reality subsystem
18#[async_trait]
19pub trait RealityAccessor: Send + Sync {
20    /// Get current reality level
21    async fn get_level(&self) -> Option<RealityLevel>;
22    /// Get reality configuration
23    async fn get_config(&self) -> Option<RealityConfig>;
24}
25
26/// Trait for accessing contract subsystem
27#[async_trait]
28pub trait ContractAccessor: Send + Sync {
29    /// Get contract validation results
30    async fn get_validation_result(
31        &self,
32        workspace_id: Option<&str>,
33    ) -> Result<Option<ContractValidationResult>>;
34    /// Get contract enforcement mode
35    async fn get_enforcement_mode(&self, workspace_id: Option<&str>) -> Result<String>;
36    /// Get drift history
37    async fn get_drift_history(&self, workspace_id: Option<&str>)
38        -> Result<Vec<DriftHistoryEntry>>;
39    /// Get active contract paths
40    async fn get_active_contracts(&self, workspace_id: Option<&str>) -> Result<Vec<String>>;
41}
42
43/// Trait for accessing scenario subsystem
44#[async_trait]
45pub trait ScenarioAccessor: Send + Sync {
46    /// Get active scenario ID
47    async fn get_active_scenario(&self, workspace_id: Option<&str>) -> Result<Option<String>>;
48    /// Get current state machine state
49    async fn get_current_state(
50        &self,
51        workspace_id: Option<&str>,
52        scenario_id: Option<&str>,
53    ) -> Result<Option<String>>;
54    /// Get available state transitions
55    async fn get_available_transitions(
56        &self,
57        workspace_id: Option<&str>,
58        scenario_id: Option<&str>,
59    ) -> Result<Vec<String>>;
60    /// Get scenario configuration
61    async fn get_scenario_config(
62        &self,
63        workspace_id: Option<&str>,
64        scenario_id: Option<&str>,
65    ) -> Result<Option<Value>>;
66}
67
68/// Trait for accessing persona subsystem
69#[async_trait]
70pub trait PersonaAccessor: Send + Sync {
71    /// Get active persona ID
72    async fn get_active_persona_id(&self, workspace_id: Option<&str>) -> Result<Option<String>>;
73    /// Get persona details
74    async fn get_persona_details(
75        &self,
76        workspace_id: Option<&str>,
77        persona_id: Option<&str>,
78    ) -> Result<Option<PersonaDetails>>;
79}
80
81/// Persona details for debug context
82#[derive(Debug, Clone)]
83pub struct PersonaDetails {
84    /// Persona ID
85    pub id: String,
86    /// Persona name
87    pub name: Option<String>,
88    /// Persona traits (key-value pairs)
89    pub traits: HashMap<String, String>,
90    /// Persona domain (e.g., "ecommerce", "saas")
91    pub domain: Option<String>,
92    /// Persona backstory/narrative
93    pub backstory: Option<String>,
94    /// Persona relationships to other entities
95    pub relationships: HashMap<String, Vec<String>>,
96    /// Current lifecycle state (if applicable)
97    pub lifecycle_state: Option<String>,
98}
99
100/// Trait for accessing chaos subsystem
101#[async_trait]
102pub trait ChaosAccessor: Send + Sync {
103    /// Get chaos configuration
104    async fn get_chaos_config(&self, workspace_id: Option<&str>) -> Result<Option<ChaosConfig>>;
105}
106
107/// Chaos configuration structure
108/// Chaos configuration for debug context
109#[derive(Debug, Clone)]
110pub struct ChaosConfig {
111    /// Whether chaos engineering is enabled
112    pub enabled: bool,
113    /// Active chaos rules
114    pub active_rules: Vec<ChaosRule>,
115    /// Failure injection configuration
116    pub failure_injection: Option<FailureInjectionConfig>,
117    /// Chaos tags
118    pub tags: Vec<String>,
119}
120
121/// Debug context integrator
122///
123/// Collects context from multiple subsystems and combines them into a unified DebugContext.
124/// All accessors are optional - if not provided, the corresponding context will be empty/default.
125pub struct DebugContextIntegrator {
126    /// Optional reality accessor
127    reality_accessor: Option<Box<dyn RealityAccessor>>,
128    /// Optional contract accessor
129    contract_accessor: Option<Box<dyn ContractAccessor>>,
130    /// Optional scenario accessor
131    scenario_accessor: Option<Box<dyn ScenarioAccessor>>,
132    /// Optional persona accessor
133    persona_accessor: Option<Box<dyn PersonaAccessor>>,
134    /// Optional chaos accessor
135    chaos_accessor: Option<Box<dyn ChaosAccessor>>,
136}
137
138impl DebugContextIntegrator {
139    /// Create a new debug context integrator
140    pub fn new() -> Self {
141        Self {
142            reality_accessor: None,
143            contract_accessor: None,
144            scenario_accessor: None,
145            persona_accessor: None,
146            chaos_accessor: None,
147        }
148    }
149
150    /// Set reality accessor
151    pub fn with_reality(mut self, accessor: Box<dyn RealityAccessor>) -> Self {
152        self.reality_accessor = Some(accessor);
153        self
154    }
155
156    /// Set contract accessor
157    pub fn with_contract(mut self, accessor: Box<dyn ContractAccessor>) -> Self {
158        self.contract_accessor = Some(accessor);
159        self
160    }
161
162    /// Set scenario accessor
163    pub fn with_scenario(mut self, accessor: Box<dyn ScenarioAccessor>) -> Self {
164        self.scenario_accessor = Some(accessor);
165        self
166    }
167
168    /// Set persona accessor
169    pub fn with_persona(mut self, accessor: Box<dyn PersonaAccessor>) -> Self {
170        self.persona_accessor = Some(accessor);
171        self
172    }
173
174    /// Set chaos accessor
175    pub fn with_chaos(mut self, accessor: Box<dyn ChaosAccessor>) -> Self {
176        self.chaos_accessor = Some(accessor);
177        self
178    }
179
180    /// Collect unified context from all subsystems
181    ///
182    /// This method queries all available subsystems and combines their contexts
183    /// into a single DebugContext structure.
184    pub async fn collect_unified_context(
185        &self,
186        workspace_id: Option<&str>,
187    ) -> Result<DebugContext> {
188        let reality = self.collect_reality_context().await?;
189        let contract = self.collect_contract_context(workspace_id).await?;
190        let scenario = self.collect_scenario_context(workspace_id).await?;
191        let persona = self.collect_persona_context(workspace_id).await?;
192        let chaos = self.collect_chaos_context(workspace_id).await?;
193
194        Ok(DebugContext {
195            reality,
196            contract,
197            scenario,
198            persona,
199            chaos,
200            collected_at: chrono::Utc::now(),
201        })
202    }
203
204    /// Collect reality subsystem context
205    async fn collect_reality_context(&self) -> Result<RealityContext> {
206        if let Some(accessor) = &self.reality_accessor {
207            let level = accessor.get_level().await;
208            let config = accessor.get_config().await;
209
210            if let Some(config) = config {
211                Ok(RealityContext::from_config(&config))
212            } else if let Some(level) = level {
213                // Create minimal context from level only
214                Ok(RealityContext {
215                    level: Some(level),
216                    level_name: Some(level.name().to_string()),
217                    ..Default::default()
218                })
219            } else {
220                Ok(RealityContext::default())
221            }
222        } else {
223            Ok(RealityContext::default())
224        }
225    }
226
227    /// Collect contract subsystem context
228    async fn collect_contract_context(
229        &self,
230        workspace_id: Option<&str>,
231    ) -> Result<ContractContext> {
232        if let Some(accessor) = &self.contract_accessor {
233            let validation_result = accessor.get_validation_result(workspace_id).await?;
234            let enforcement_mode = accessor.get_enforcement_mode(workspace_id).await?;
235            let drift_history = accessor.get_drift_history(workspace_id).await?;
236            let active_contracts = accessor.get_active_contracts(workspace_id).await?;
237
238            let validation_enabled = validation_result.is_some();
239            let validation_errors =
240                validation_result.as_ref().map(|r| r.errors.clone()).unwrap_or_default();
241
242            Ok(ContractContext {
243                validation_enabled,
244                validation_result,
245                enforcement_mode,
246                drift_history,
247                active_contracts,
248                validation_errors,
249            })
250        } else {
251            Ok(ContractContext::default())
252        }
253    }
254
255    /// Collect scenario subsystem context
256    async fn collect_scenario_context(
257        &self,
258        workspace_id: Option<&str>,
259    ) -> Result<ScenarioContext> {
260        if let Some(accessor) = &self.scenario_accessor {
261            let active_scenario = accessor.get_active_scenario(workspace_id).await?;
262            let current_state = if let Some(ref scenario_id) = active_scenario {
263                accessor.get_current_state(workspace_id, Some(scenario_id)).await?
264            } else {
265                None
266            };
267            let available_transitions = if let Some(ref scenario_id) = active_scenario {
268                accessor.get_available_transitions(workspace_id, Some(scenario_id)).await?
269            } else {
270                Vec::new()
271            };
272            let scenario_config = if let Some(ref scenario_id) = active_scenario {
273                accessor.get_scenario_config(workspace_id, Some(scenario_id)).await?
274            } else {
275                None
276            };
277
278            Ok(ScenarioContext {
279                active_scenario,
280                current_state,
281                available_transitions,
282                scenario_config,
283            })
284        } else {
285            Ok(ScenarioContext::default())
286        }
287    }
288
289    /// Collect persona subsystem context
290    async fn collect_persona_context(&self, workspace_id: Option<&str>) -> Result<PersonaContext> {
291        if let Some(accessor) = &self.persona_accessor {
292            let active_persona_id = accessor.get_active_persona_id(workspace_id).await?;
293            let persona_details = if let Some(ref persona_id) = active_persona_id {
294                accessor.get_persona_details(workspace_id, Some(persona_id)).await?
295            } else {
296                None
297            };
298
299            if let Some(details) = persona_details {
300                Ok(PersonaContext {
301                    active_persona_id: Some(details.id.clone()),
302                    active_persona_name: details.name,
303                    traits: details.traits,
304                    domain: details.domain,
305                    backstory: details.backstory,
306                    relationships: details.relationships,
307                    lifecycle_state: details.lifecycle_state,
308                })
309            } else if let Some(persona_id) = active_persona_id {
310                // We have an ID but no details
311                Ok(PersonaContext {
312                    active_persona_id: Some(persona_id),
313                    ..Default::default()
314                })
315            } else {
316                Ok(PersonaContext::default())
317            }
318        } else {
319            Ok(PersonaContext::default())
320        }
321    }
322
323    /// Collect chaos subsystem context
324    async fn collect_chaos_context(&self, workspace_id: Option<&str>) -> Result<ChaosContext> {
325        if let Some(accessor) = &self.chaos_accessor {
326            if let Some(config) = accessor.get_chaos_config(workspace_id).await? {
327                Ok(ChaosContext {
328                    enabled: config.enabled,
329                    active_rules: config.active_rules,
330                    failure_injection: config.failure_injection,
331                    tags: config.tags,
332                })
333            } else {
334                Ok(ChaosContext::default())
335            }
336        } else {
337            Ok(ChaosContext::default())
338        }
339    }
340}
341
342impl Default for DebugContextIntegrator {
343    fn default() -> Self {
344        Self::new()
345    }
346}
347
348/// Implementation of RealityAccessor for RealityEngine
349pub struct RealityEngineAccessor {
350    engine: std::sync::Arc<tokio::sync::RwLock<RealityEngine>>,
351}
352
353impl RealityEngineAccessor {
354    /// Create a new reality engine accessor
355    pub fn new(engine: std::sync::Arc<tokio::sync::RwLock<RealityEngine>>) -> Self {
356        Self { engine }
357    }
358}
359
360#[async_trait]
361impl RealityAccessor for RealityEngineAccessor {
362    async fn get_level(&self) -> Option<RealityLevel> {
363        Some(self.engine.read().await.get_level().await)
364    }
365
366    async fn get_config(&self) -> Option<RealityConfig> {
367        Some(self.engine.read().await.get_config().await)
368    }
369}