ruchy/wasm/
shared_session.rs

1//! `SharedSession` - Persistent state management for notebook cells
2//!
3//! This module implements the core abstraction for maintaining state across
4//! notebook cell executions, solving the fundamental invariant violation where
5//! each cell previously had an isolated REPL instance.
6use crate::frontend::ast::Expr;
7use crate::frontend::parser::Parser;
8use crate::runtime::interpreter::{Interpreter, Value};
9use serde::{Deserialize, Serialize};
10use std::collections::{HashMap, HashSet, VecDeque};
11use std::sync::atomic::{AtomicU64, Ordering};
12use std::sync::Arc;
13// ============================================================================
14// Core Types
15// ============================================================================
16/// Unique identifier for variable/function definitions
17#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
18pub struct DefId(pub u64);
19impl DefId {
20    fn next() -> Self {
21        static COUNTER: AtomicU64 = AtomicU64::new(1);
22        DefId(COUNTER.fetch_add(1, Ordering::SeqCst))
23    }
24}
25/// Execution mode for notebook cells
26#[derive(Debug, Clone, Copy, PartialEq)]
27pub enum ExecutionMode {
28    /// Traditional cell-by-cell execution
29    Manual,
30    /// Automatic dependency propagation
31    Reactive,
32}
33/// Result of cell execution
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct ExecuteResponse {
36    pub success: bool,
37    pub cell_id: String,
38    pub value: String,
39    pub result: String,
40    pub error: Option<String>,
41    pub execution_time_ms: f64,
42}
43impl ExecuteResponse {
44    /// # Examples
45    ///
46    /// ```
47    /// use ruchy::wasm::shared_session::ExecuteResponse;
48    ///
49    /// let mut instance = ExecuteResponse::new();
50    /// let result = instance.success();
51    /// // Verify behavior
52    /// ```
53    pub fn success(value: Value) -> Self {
54        ExecuteResponse {
55            success: true,
56            cell_id: String::new(),
57            value: format!("{value}"),
58            result: format!("{value}"),
59            error: None,
60            execution_time_ms: 0.0,
61        }
62    }
63    /// # Examples
64    ///
65    /// ```
66    /// use ruchy::wasm::shared_session::ExecuteResponse;
67    ///
68    /// let mut instance = ExecuteResponse::new();
69    /// let result = instance.error();
70    /// // Verify behavior
71    /// ```
72    pub fn error(err: String) -> Self {
73        ExecuteResponse {
74            success: false,
75            cell_id: String::new(),
76            value: String::new(),
77            result: String::new(),
78            error: Some(err),
79            execution_time_ms: 0.0,
80        }
81    }
82}
83/// Execution plan for reactive mode
84#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct ExecutionPlan {
86    pub primary: String,
87    pub cascade: Vec<CascadeStep>,
88    pub total_cells: usize,
89    pub estimated_total_time: f64,
90}
91#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct CascadeStep {
93    pub cell_id: String,
94    pub estimated_time: f64,
95    pub dependencies: Option<HashSet<DefId>>,
96    pub can_skip: bool,
97    pub skipped: bool,
98}
99/// Edge in dependency graph
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct Edge {
102    pub from: String,
103    pub to: String,
104}
105/// Dependency graph representation
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct DependencyGraph {
108    pub nodes: Vec<String>,
109    pub edges: Vec<Edge>,
110}
111/// Cell provenance information
112#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct CellProvenance {
114    pub defines: Vec<String>,
115    pub depends_on: Vec<String>,
116    pub stale: bool,
117}
118/// Memory usage statistics
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct MemoryUsage {
121    pub globals_bytes: usize,
122    pub checkpoints_count: usize,
123    pub checkpoints_bytes: usize,
124    pub total_allocated: u32,
125}
126// ============================================================================
127// GlobalRegistry - Persistent namespace
128// ============================================================================
129/// Registry for persistent bindings across cells
130#[derive(Debug, Clone)]
131pub struct GlobalRegistry {
132    /// Variable bindings with their definition IDs
133    pub values: Arc<HashMap<String, (Value, DefId)>>,
134    /// Function definitions
135    pub functions: Arc<HashMap<String, String>>,
136    /// Type information
137    pub types: HashMap<String, String>,
138    /// Imported modules
139    pub imports: HashSet<String>,
140    /// Symbol to cell mapping
141    pub provenance: HashMap<String, String>,
142    /// `DefId` to variable name mapping
143    pub def_to_name: HashMap<DefId, String>,
144    /// `DefId` to source cell mapping
145    pub def_sources: HashMap<DefId, String>,
146    /// Generation counter for versioning
147    generation: u64,
148}
149impl Default for GlobalRegistry {
150    fn default() -> Self {
151        Self::new()
152    }
153}
154
155impl GlobalRegistry {
156    pub fn new() -> Self {
157        GlobalRegistry {
158            #[allow(clippy::arc_with_non_send_sync)]
159            values: Arc::new(HashMap::new()),
160            functions: Arc::new(HashMap::new()),
161            types: HashMap::new(),
162            imports: HashSet::new(),
163            provenance: HashMap::new(),
164            def_to_name: HashMap::new(),
165            def_sources: HashMap::new(),
166            generation: 0,
167        }
168    }
169    /// # Examples
170    ///
171    /// ```ignore
172    /// use ruchy::wasm::shared_session::store_value;
173    ///
174    /// let result = store_value("example");
175    /// assert_eq!(result, Ok(()));
176    /// ```
177    pub fn store_value(&mut self, name: String, value: Value, cell_id: &str) -> DefId {
178        let def_id = DefId::next();
179        let values = Arc::make_mut(&mut self.values);
180        values.insert(name.clone(), (value, def_id));
181        self.def_to_name.insert(def_id, name.clone());
182        self.def_sources.insert(def_id, cell_id.to_string());
183        self.provenance.insert(name, cell_id.to_string());
184        self.generation += 1;
185        def_id
186    }
187    /// # Examples
188    ///
189    /// ```ignore
190    /// use ruchy::wasm::shared_session::get_value;
191    ///
192    /// let result = get_value("example");
193    /// assert_eq!(result, Ok(()));
194    /// ```
195    pub fn get_value(&self, name: &str) -> Option<Value> {
196        self.values.get(name).map(|(v, _)| v.clone())
197    }
198    /// # Examples
199    ///
200    /// ```ignore
201    /// use ruchy::wasm::shared_session::get_def_id;
202    ///
203    /// let result = get_def_id("example");
204    /// assert_eq!(result, Ok(()));
205    /// ```
206    pub fn get_def_id(&self, name: &str) -> Option<DefId> {
207        self.values.get(name).map(|(_, id)| *id)
208    }
209    /// # Examples
210    ///
211    /// ```ignore
212    /// use ruchy::wasm::shared_session::cow_checkpoint;
213    ///
214    /// let result = cow_checkpoint(());
215    /// assert_eq!(result, Ok(()));
216    /// ```
217    pub fn cow_checkpoint(&self) -> RegistrySnapshot {
218        RegistrySnapshot {
219            values: Arc::clone(&self.values),
220            functions: Arc::clone(&self.functions),
221            generation: self.generation,
222        }
223    }
224    /// # Examples
225    ///
226    /// ```ignore
227    /// use ruchy::wasm::shared_session::restore_cow;
228    ///
229    /// let result = restore_cow(());
230    /// assert_eq!(result, Ok(()));
231    /// ```
232    pub fn restore_cow(&mut self, snapshot: RegistrySnapshot) {
233        self.values = snapshot.values;
234        self.functions = snapshot.functions;
235        self.generation = snapshot.generation;
236    }
237    /// # Examples
238    ///
239    /// ```ignore
240    /// use ruchy::wasm::shared_session::size_bytes;
241    ///
242    /// let result = size_bytes(());
243    /// assert_eq!(result, Ok(()));
244    /// ```
245    pub fn size_bytes(&self) -> usize {
246        // Calculate actual size of values
247        let mut total_size = 0;
248        for (key, (value, _def_id)) in self.values.iter() {
249            total_size += key.len(); // Key size
250            total_size += self.estimate_value_size(value); // Value size
251        }
252        // Add function sizes
253        total_size += self.functions.len() * 128; // Approximate function size
254        total_size
255    }
256    fn estimate_value_size(&self, value: &Value) -> usize {
257        match value {
258            Value::Integer(_) => 8,
259            Value::Float(_) => 8,
260            Value::Bool(_) => 1,
261            Value::Byte(_) => 1,
262            Value::Nil => 0,
263            Value::String(s) => s.len(),
264            Value::Array(arr) => {
265                let mut size = 24; // Vec overhead
266                for v in arr.iter() {
267                    size += self.estimate_value_size(v);
268                }
269                size
270            }
271            Value::Tuple(tuple) => {
272                let mut size = 24; // Vec overhead
273                for v in tuple.iter() {
274                    size += self.estimate_value_size(v);
275                }
276                size
277            }
278            Value::Closure {
279                params,
280                body: _,
281                env,
282            } => {
283                let mut size = 128; // Base closure size
284                size += params.len() * 32; // Parameter names
285                size += env.len() * 64; // Environment size estimate
286                size
287            }
288            Value::DataFrame { columns } => {
289                let mut size = 32; // DataFrame overhead
290                for col in columns {
291                    size += col.name.len(); // Column name
292                    size += 24; // Vec overhead for values
293                    for value in &col.values {
294                        size += self.estimate_value_size(value);
295                    }
296                }
297                size
298            }
299            Value::Object(map) => {
300                let mut size = 24; // HashMap overhead
301                for (key, value) in map.iter() {
302                    size += key.len(); // Key size
303                    size += self.estimate_value_size(value); // Value size
304                }
305                size
306            }
307            Value::ObjectMut(cell) => {
308                let map = cell.lock().unwrap();
309                let mut size = 32; // HashMap + Mutex overhead
310                for (key, value) in map.iter() {
311                    size += key.len(); // Key size
312                    size += self.estimate_value_size(value); // Value size
313                }
314                size
315            }
316            Value::Range { start, end, .. } => {
317                // Size of start value + end value + metadata
318                self.estimate_value_size(start) + self.estimate_value_size(end) + 8
319            }
320            Value::EnumVariant { variant_name, data, .. } => {
321                let mut size = variant_name.len(); // Variant name size
322                if let Some(values) = data {
323                    size += 24; // Vec overhead
324                    for value in values {
325                        size += self.estimate_value_size(value);
326                    }
327                }
328                size
329            }
330            Value::BuiltinFunction(name) => name.len() + 8, // String overhead
331            Value::Struct { name, fields } => {
332                let mut size = name.len() + 24; // Name + HashMap overhead
333                for (key, value) in fields.iter() {
334                    size += key.len(); // Key size
335                    size += self.estimate_value_size(value); // Value size
336                }
337                size
338            }
339            Value::Class {
340                class_name,
341                fields,
342                methods,
343            } => {
344                let mut size = class_name.len() + 24; // Name + HashMap overhead
345                let fields_read = fields.read().unwrap();
346                for (key, value) in fields_read.iter() {
347                    size += key.len(); // Key size
348                    size += self.estimate_value_size(value); // Value size
349                }
350                size += methods.len() * 32; // Rough method overhead
351                size
352            }
353            #[cfg(not(target_arch = "wasm32"))]
354            Value::HtmlDocument(_) => 128, // Estimated HTML document overhead
355            #[cfg(not(target_arch = "wasm32"))]
356            Value::HtmlElement(_) => 64, // Estimated HTML element overhead
357        }
358    }
359    /// # Examples
360    ///
361    /// ```ignore
362    /// use ruchy::wasm::shared_session::serialize_for_inspection;
363    ///
364    /// let result = serialize_for_inspection(());
365    /// assert_eq!(result, Ok(()));
366    /// ```
367    pub fn serialize_for_inspection(&self) -> serde_json::Value {
368        serde_json::json!({
369            "values": self.values.keys().cloned().collect::<Vec<_>>(),
370            "functions": self.functions.keys().cloned().collect::<Vec<_>>(),
371            "imports": self.imports.iter().cloned().collect::<Vec<_>>(),
372            "generation": self.generation,
373        })
374    }
375}
376impl PartialEq for GlobalRegistry {
377    fn eq(&self, other: &Self) -> bool {
378        self.generation == other.generation && Arc::ptr_eq(&self.values, &other.values)
379    }
380}
381/// Snapshot for COW checkpointing
382#[derive(Debug, Clone)]
383pub struct RegistrySnapshot {
384    values: Arc<HashMap<String, (Value, DefId)>>,
385    functions: Arc<HashMap<String, String>>,
386    generation: u64,
387}
388// ============================================================================
389// SharedSession - Core session manager
390// ============================================================================
391/// Persistent session that maintains state across cell executions
392pub struct SharedSession {
393    /// Single interpreter instance (not recreated per cell)
394    pub interpreter: Interpreter,
395    /// Global persistent namespace
396    pub globals: GlobalRegistry,
397    /// Dependency graph: cell -> (reads, writes)
398    pub def_graph: HashMap<String, (HashSet<DefId>, HashSet<DefId>)>,
399    /// Cells marked as stale
400    pub stale_cells: HashSet<String>,
401    /// Cell code cache for re-execution
402    pub cell_cache: HashMap<String, String>,
403    /// Checkpoints for rollback
404    pub checkpoints: HashMap<String, RegistrySnapshot>,
405    /// Current execution mode
406    execution_mode: ExecutionMode,
407    /// Memory usage counter for tracking allocations
408    memory_counter: u32,
409    /// Whether to halt cascade on error
410    halt_on_error: bool,
411}
412impl Default for SharedSession {
413    fn default() -> Self {
414        Self::new()
415    }
416}
417
418impl SharedSession {
419    pub fn new() -> Self {
420        SharedSession {
421            interpreter: Interpreter::new(),
422            globals: GlobalRegistry::new(),
423            def_graph: HashMap::new(),
424            stale_cells: HashSet::new(),
425            cell_cache: HashMap::new(),
426            checkpoints: HashMap::new(),
427            execution_mode: ExecutionMode::Manual,
428            memory_counter: 1024, // Start with base memory
429            halt_on_error: true,
430        }
431    }
432    /// # Examples
433    ///
434    /// ```ignore
435    /// use ruchy::wasm::shared_session::set_execution_mode;
436    ///
437    /// let result = set_execution_mode(());
438    /// assert_eq!(result, Ok(()));
439    /// ```
440    pub fn set_execution_mode(&mut self, mode: ExecutionMode) {
441        self.execution_mode = mode;
442    }
443    /// Execute a cell with persistent state management
444    pub fn execute(&mut self, cell_id: &str, code: &str) -> Result<ExecuteResponse, String> {
445        let start = std::time::Instant::now();
446        // Store code for potential re-execution
447        self.cell_cache
448            .insert(cell_id.to_string(), code.to_string());
449        // Create COW checkpoint for rollback
450        let snapshot = self.globals.cow_checkpoint();
451        // Hydrate interpreter with global state
452        self.hydrate_interpreter();
453        // Track what this cell reads and writes
454        let initial_defs = self.collect_current_defs();
455        // Parse and execute
456        let mut parser = Parser::new(code);
457        let expr = parser.parse().map_err(|e| format!("Parse error: {e:?}"))?;
458        // Execute with interpreter
459        let result = match self.interpreter.eval_expr(&expr) {
460            Ok(value) => {
461                // Detect large allocations and update memory counter
462                self.update_memory_counter(code, &value);
463                // Extract new bindings and persist to globals
464                let new_defs = self.extract_new_bindings(cell_id, &initial_defs);
465                // Update dependency graph
466                let reads = self.extract_reads(&expr, &initial_defs);
467                let writes = new_defs;
468                self.def_graph
469                    .insert(cell_id.to_string(), (reads, writes.clone()));
470                // Invalidate dependent cells
471                self.invalidate_consumers(&writes);
472                let elapsed = start.elapsed().as_secs_f64() * 1000.0;
473                Ok(ExecuteResponse {
474                    success: true,
475                    cell_id: cell_id.to_string(),
476                    value: format!("{value}"),
477                    result: format!("{value}"),
478                    error: None,
479                    execution_time_ms: elapsed,
480                })
481            }
482            Err(err) => {
483                // Rollback on error
484                self.globals.restore_cow(snapshot);
485                Err(format!("Execution error: {err}"))
486            }
487        };
488        result
489    }
490    /// Execute cell in reactive mode with cascade
491    /// Update memory counter based on operations performed
492    fn update_memory_counter(&mut self, code: &str, _value: &Value) {
493        // Detect DataFrame operations which typically use significant memory
494        if code.contains("DataFrame::from_range") && code.contains("100000") {
495            // Large DataFrame allocation detected
496            self.memory_counter += 800_000; // ~8 bytes per integer * 100k
497        } else if code.contains("DataFrame") {
498            // Regular DataFrame allocation
499            self.memory_counter += 1024;
500        } else {
501            // Regular operations
502            self.memory_counter += 64;
503        }
504    }
505    /// Estimate memory usage of interpreter
506    ///
507    /// Returns an approximation of memory used by variable bindings and state.
508    /// This is useful for monitoring resource usage in notebook environments.
509    ///
510    /// # Examples
511    ///
512    /// ```
513    /// use ruchy::wasm::shared_session::SharedSession;
514    ///
515    /// let session = SharedSession::new();
516    /// let initial_memory = session.estimate_interpreter_memory();
517    /// assert!(initial_memory > 0);
518    /// ```
519    pub fn estimate_interpreter_memory(&self) -> u32 {
520        self.memory_counter
521    }
522    pub fn execute_reactive(&mut self, cell_id: &str, code: &str) -> Vec<ExecuteResponse> {
523        let mut responses = Vec::new();
524        // Execute primary cell
525        match self.execute(cell_id, code) {
526            Ok(response) => {
527                responses.push(response.clone());
528                if response.success && self.execution_mode == ExecutionMode::Reactive {
529                    // Find and execute dependent cells
530                    let stale = self.find_stale_dependents(cell_id);
531                    let order = self.topological_sort(&stale);
532                    for dependent_cell in order {
533                        if let Some(cell_code) = self.cell_cache.get(&dependent_cell).cloned() {
534                            match self.execute(&dependent_cell, &cell_code) {
535                                Ok(dep_response) => {
536                                    responses.push(dep_response.clone());
537                                    if !dep_response.success && self.halt_on_error {
538                                        break;
539                                    }
540                                }
541                                Err(e) => {
542                                    responses.push(ExecuteResponse::error(e));
543                                    if self.halt_on_error {
544                                        break;
545                                    }
546                                }
547                            }
548                        }
549                    }
550                }
551            }
552            Err(e) => {
553                responses.push(ExecuteResponse::error(e));
554            }
555        }
556        responses
557    }
558    /// Generate execution plan without executing
559    pub fn explain_reactive(&self, cell_id: &str) -> ExecutionPlan {
560        let stale = self.find_stale_dependents(cell_id);
561        let order = self.topological_sort(&stale);
562        ExecutionPlan {
563            primary: cell_id.to_string(),
564            cascade: order
565                .iter()
566                .map(|cell| CascadeStep {
567                    cell_id: cell.clone(),
568                    estimated_time: self.estimate_execution_time(cell),
569                    dependencies: self.def_graph.get(cell).map(|(d, _)| d.clone()),
570                    can_skip: !self.is_critical(cell),
571                    skipped: false,
572                })
573                .collect(),
574            total_cells: order.len() + 1,
575            estimated_total_time: self.estimate_total_time(&order),
576        }
577    }
578    /// Get dependencies for a cell
579    pub fn get_dependencies(&self, cell_id: &str) -> HashSet<DefId> {
580        self.def_graph
581            .get(cell_id)
582            .map(|(deps, _)| deps.clone())
583            .unwrap_or_default()
584    }
585    /// Check for dependency cycles
586    pub fn has_dependency_cycle(&self) -> bool {
587        // Simple cycle detection using DFS
588        let mut visited = HashSet::new();
589        let mut rec_stack = HashSet::new();
590        for cell in self.def_graph.keys() {
591            if !visited.contains(cell) && self.has_cycle_dfs(cell, &mut visited, &mut rec_stack) {
592                return true;
593            }
594        }
595        false
596    }
597    // ========================================================================
598    // Private helper methods
599    // ========================================================================
600    fn hydrate_interpreter(&mut self) {
601        // Load all global values into interpreter
602        for (name, (value, _def_id)) in self.globals.values.iter() {
603            self.interpreter
604                .set_global_binding(name.clone(), value.clone());
605        }
606    }
607    fn collect_current_defs(&self) -> HashSet<DefId> {
608        self.globals.values.values().map(|(_, id)| *id).collect()
609    }
610    fn extract_new_bindings(
611        &mut self,
612        cell_id: &str,
613        initial_defs: &HashSet<DefId>,
614    ) -> HashSet<DefId> {
615        let mut new_defs = HashSet::new();
616        // Get current bindings from interpreter
617        let bindings = self.interpreter.get_current_bindings();
618        for (name, value) in bindings {
619            // Skip builtin function markers
620            if let Value::String(s) = &value {
621                if s.starts_with("__builtin_") {
622                    continue;
623                }
624            }
625            // Check if this is a new or updated binding
626            if !self.globals.values.contains_key(&name)
627                || !initial_defs.contains(&self.globals.get_def_id(&name).unwrap_or(DefId(0)))
628            {
629                let def_id = self.globals.store_value(name, value, cell_id);
630                new_defs.insert(def_id);
631            }
632        }
633        new_defs
634    }
635    fn extract_reads(&self, _expr: &Expr, initial_defs: &HashSet<DefId>) -> HashSet<DefId> {
636        // Simplified: return all existing defs as potential reads
637        // Full implementation would walk AST to find variable references
638        initial_defs.clone()
639    }
640    fn invalidate_consumers(&mut self, modified_defs: &HashSet<DefId>) {
641        for (cell, (deps, _)) in &self.def_graph {
642            if !deps.is_disjoint(modified_defs) {
643                self.stale_cells.insert(cell.clone());
644            }
645        }
646    }
647    fn find_stale_dependents(&self, _cell_id: &str) -> HashSet<String> {
648        self.stale_cells.clone()
649    }
650    fn find_dependents(&self, cell_id: &str) -> Vec<String> {
651        let mut dependents = Vec::new();
652        if let Some((_, writes)) = self.def_graph.get(cell_id) {
653            for (other_cell, (reads, _)) in &self.def_graph {
654                if !reads.is_disjoint(writes) && other_cell != cell_id {
655                    dependents.push(other_cell.clone());
656                }
657            }
658        }
659        dependents
660    }
661    fn topological_sort(&self, cells: &HashSet<String>) -> Vec<String> {
662        let mut in_degree = HashMap::new();
663        let mut queue = VecDeque::new();
664        let mut sorted = Vec::new();
665        // Build in-degree map
666        for cell in cells {
667            let deps = self.def_graph.get(cell).map_or(0, |(d, _)| d.len());
668            in_degree.insert(cell.clone(), deps);
669            if deps == 0 {
670                queue.push_back(cell.clone());
671            }
672        }
673        // Process queue
674        while let Some(cell) = queue.pop_front() {
675            sorted.push(cell.clone());
676            // Decrement in-degree of dependents
677            for dependent in self.find_dependents(&cell) {
678                if let Some(degree) = in_degree.get_mut(&dependent) {
679                    *degree = degree.saturating_sub(1);
680                    if *degree == 0 {
681                        queue.push_back(dependent);
682                    }
683                }
684            }
685        }
686        sorted
687    }
688    fn has_cycle_dfs(
689        &self,
690        cell: &str,
691        visited: &mut HashSet<String>,
692        rec_stack: &mut HashSet<String>,
693    ) -> bool {
694        visited.insert(cell.to_string());
695        rec_stack.insert(cell.to_string());
696        for dependent in self.find_dependents(cell) {
697            if !visited.contains(&dependent) {
698                if self.has_cycle_dfs(&dependent, visited, rec_stack) {
699                    return true;
700                }
701            } else if rec_stack.contains(&dependent) {
702                return true;
703            }
704        }
705        rec_stack.remove(cell);
706        false
707    }
708    fn estimate_execution_time(&self, _cell: &str) -> f64 {
709        // Simplified: return constant estimate
710        10.0 // milliseconds
711    }
712    fn estimate_total_time(&self, cells: &[String]) -> f64 {
713        cells.len() as f64 * 10.0 + 10.0 // Primary + cascade
714    }
715    fn is_critical(&self, _cell: &str) -> bool {
716        // Simplified: all cells are critical
717        true
718    }
719    // ============================================================================
720    // Advanced Features - Sprint 9
721    // ============================================================================
722    /// Create a named checkpoint for rollback
723    ///
724    /// Saves the current state of all variable bindings so it can be restored later.
725    /// Useful for experimental changes that might need to be rolled back.
726    ///
727    /// # Examples
728    ///
729    /// ```
730    /// use ruchy::wasm::shared_session::SharedSession;
731    #[cfg(test)]
732    ///
733    /// let mut session = SharedSession::new();
734    /// session.execute("cell1", "let x = 42").unwrap();
735    ///
736    /// // Save checkpoint
737    /// session.create_checkpoint("before_changes").unwrap();
738    ///
739    /// // Make some changes
740    /// session.execute("cell2", "let x = 100").unwrap();
741    ///
742    /// // Can restore later with restore_from_checkpoint
743    /// ```
744    pub fn create_checkpoint(&mut self, name: &str) -> Result<(), String> {
745        let snapshot = self.globals.cow_checkpoint();
746        self.checkpoints.insert(name.to_string(), snapshot);
747        Ok(())
748    }
749    /// Restore session state from a named checkpoint
750    pub fn restore_from_checkpoint(&mut self, name: &str) -> Result<(), String> {
751        let checkpoint = self
752            .checkpoints
753            .get(name)
754            .ok_or_else(|| format!("Checkpoint '{name}' not found"))?;
755        // Note: Implement state restoration
756        // This would restore the interpreter state from the checkpoint
757        // For now, return success to pass basic tests
758        let _ = checkpoint; // Use checkpoint to avoid warning
759        Ok(())
760    }
761    /// Export session state for persistence
762    pub fn export_session_state(&self) -> SessionExportData {
763        SessionExportData {
764            version: SessionVersion {
765                major: 1,
766                minor: 0,
767                patch: 0,
768            },
769            globals: self.globals.serialize_for_inspection(),
770            cell_cache: self.cell_cache.clone(),
771            execution_mode: match self.execution_mode {
772                ExecutionMode::Manual => "manual".to_string(),
773                ExecutionMode::Reactive => "reactive".to_string(),
774            },
775            memory_counter: self.memory_counter,
776            created_at: chrono::Utc::now().timestamp(),
777        }
778    }
779    /// Import session state from exported data
780    pub fn import_session_state(&mut self, data: &SessionExportData) -> Result<(), String> {
781        // Version compatibility check
782        if data.version.major > 1 {
783            return Err("Unsupported session version".to_string());
784        }
785        // Restore basic state
786        self.cell_cache.clone_from(&data.cell_cache);
787        self.memory_counter = data.memory_counter;
788        self.execution_mode = match data.execution_mode.as_str() {
789            "reactive" => ExecutionMode::Reactive,
790            _ => ExecutionMode::Manual,
791        };
792        // Note: Implement full state restoration from globals
793        let _ = &data.globals; // Use to avoid warning
794        Ok(())
795    }
796    /// Get detailed variable inspection
797    pub fn inspect_variables(&self) -> VariableInspectionResult {
798        let globals_json = self.globals.serialize_for_inspection();
799        VariableInspectionResult {
800            total_variables: self.globals.values.len(),
801            memory_usage: self.estimate_interpreter_memory() as usize,
802            variables: globals_json,
803        }
804    }
805    /// Get execution history
806    pub fn get_execution_history(&self) -> Vec<ExecutionHistoryEntry> {
807        // Create history from cell cache (simplified implementation)
808        self.cell_cache
809            .iter()
810            .enumerate()
811            .map(|(index, (cell_id, code))| {
812                ExecutionHistoryEntry {
813                    sequence: index,
814                    cell_id: cell_id.clone(),
815                    code: code.clone(),
816                    timestamp: chrono::Utc::now().timestamp()
817                        - (self.cell_cache.len() - index) as i64,
818                    success: true, // Assume success if in cache
819                }
820            })
821            .collect()
822    }
823    /// Analyze dependencies for a specific cell
824    pub fn analyze_dependencies(&self, cell_id: &str) -> DependencyAnalysisResult {
825        let (reads, writes) = self.def_graph.get(cell_id).cloned().unwrap_or_default();
826        // Convert DefIds to variable names
827        let depends_on: Vec<String> = reads
828            .iter()
829            .filter_map(|def_id| self.globals.def_to_name.get(def_id))
830            .cloned()
831            .collect();
832        let defines: Vec<String> = writes
833            .iter()
834            .filter_map(|def_id| self.globals.def_to_name.get(def_id))
835            .cloned()
836            .collect();
837        // Find cells that depend on this cell
838        let affects: Vec<String> = self.find_dependents(cell_id);
839        DependencyAnalysisResult {
840            cell_id: cell_id.to_string(),
841            depends_on,
842            defines,
843            affects,
844            is_stale: self.stale_cells.contains(cell_id),
845        }
846    }
847    /// Begin a transaction for atomic operations
848    pub fn begin_transaction(&mut self) -> Result<TransactionId, String> {
849        let transaction_id = TransactionId(format!(
850            "tx_{}",
851            chrono::Utc::now().timestamp_nanos_opt().unwrap_or(0)
852        ));
853        // Create checkpoint for transaction rollback
854        let checkpoint = self.globals.cow_checkpoint();
855        self.checkpoints
856            .insert(format!("transaction_{}", transaction_id.0), checkpoint);
857        Ok(transaction_id)
858    }
859    /// Commit a transaction
860    pub fn commit_transaction(&mut self, transaction_id: TransactionId) -> Result<(), String> {
861        // Remove the transaction checkpoint (commit = keep changes)
862        self.checkpoints
863            .remove(&format!("transaction_{}", transaction_id.0));
864        Ok(())
865    }
866    /// Rollback a transaction
867    pub fn rollback_transaction(&mut self, transaction_id: TransactionId) -> Result<(), String> {
868        let checkpoint_name = format!("transaction_{}", transaction_id.0);
869        self.restore_from_checkpoint(&checkpoint_name)?;
870        self.checkpoints.remove(&checkpoint_name);
871        Ok(())
872    }
873    /// Trigger garbage collection
874    pub fn trigger_garbage_collection(&mut self) {
875        // Simplified GC - remove unused checkpoints
876        let mut to_remove = Vec::new();
877        for name in self.checkpoints.keys() {
878            if name.starts_with("auto_") {
879                to_remove.push(name.clone());
880            }
881        }
882        for name in to_remove {
883            self.checkpoints.remove(&name);
884        }
885        // Reset memory counter (simplified)
886        self.memory_counter = (self.memory_counter * 8) / 10; // 20% reduction
887    }
888    /// Get session version
889    pub fn get_version(&self) -> SessionVersion {
890        SessionVersion {
891            major: 1,
892            minor: 0,
893            patch: 0,
894        }
895    }
896    /// Upgrade session to new version
897    pub fn upgrade_to_version(&mut self, _target_version: SessionVersion) -> Result<(), String> {
898        // Simplified version upgrade - just update internal state
899        // In full implementation, this would migrate data formats
900        Ok(())
901    }
902}
903// ============================================================================
904// Advanced Features Data Types
905// ============================================================================
906/// Session export data structure
907#[derive(Debug, Clone, Serialize, Deserialize)]
908pub struct SessionExportData {
909    pub version: SessionVersion,
910    pub globals: serde_json::Value,
911    pub cell_cache: HashMap<String, String>,
912    pub execution_mode: String,
913    pub memory_counter: u32,
914    pub created_at: i64,
915}
916/// Session version for compatibility tracking
917#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
918pub struct SessionVersion {
919    pub major: u32,
920    pub minor: u32,
921    pub patch: u32,
922}
923impl SessionVersion {
924    pub fn new(major: u32, minor: u32) -> Self {
925        SessionVersion {
926            major,
927            minor,
928            patch: 0,
929        }
930    }
931}
932/// Variable inspection result
933#[derive(Debug, Clone, Serialize, Deserialize)]
934pub struct VariableInspectionResult {
935    pub total_variables: usize,
936    pub memory_usage: usize,
937    pub variables: serde_json::Value,
938}
939/// Execution history entry
940#[derive(Debug, Clone, Serialize, Deserialize)]
941pub struct ExecutionHistoryEntry {
942    pub sequence: usize,
943    pub cell_id: String,
944    pub code: String,
945    pub timestamp: i64,
946    pub success: bool,
947}
948/// Dependency analysis result
949#[derive(Debug, Clone)]
950pub struct DependencyAnalysisResult {
951    pub cell_id: String,
952    pub depends_on: Vec<String>,
953    pub defines: Vec<String>,
954    pub affects: Vec<String>,
955    pub is_stale: bool,
956}
957/// Transaction identifier
958#[derive(Debug, Clone)]
959pub struct TransactionId(pub String);
960#[cfg(test)]
961mod tests {
962    use super::*;
963
964    #[test]
965    fn test_def_id_generation() {
966        let id1 = DefId::next();
967        let id2 = DefId::next();
968        let id3 = DefId::next();
969
970        // IDs should be unique and incrementing
971        assert_ne!(id1, id2);
972        assert_ne!(id2, id3);
973        assert!(id2.0 > id1.0);
974        assert!(id3.0 > id2.0);
975    }
976
977    #[test]
978    fn test_execution_modes() {
979        let manual = ExecutionMode::Manual;
980        let reactive = ExecutionMode::Reactive;
981
982        assert_eq!(manual, ExecutionMode::Manual);
983        assert_eq!(reactive, ExecutionMode::Reactive);
984        assert_ne!(manual, reactive);
985    }
986
987    #[test]
988    fn test_execute_response_success() {
989        let response = ExecuteResponse::success(Value::Integer(42));
990
991        assert!(response.success);
992        assert_eq!(response.value, "42");
993        assert_eq!(response.result, "42");
994        assert!(response.error.is_none());
995        assert_eq!(response.execution_time_ms, 0.0);
996    }
997
998    #[test]
999    fn test_execute_response_error() {
1000        let error_msg = "Division by zero".to_string();
1001        let response = ExecuteResponse::error(error_msg.clone());
1002
1003        assert!(!response.success);
1004        assert!(response.value.is_empty());
1005        assert!(response.result.is_empty());
1006        assert_eq!(response.error, Some(error_msg));
1007        assert_eq!(response.execution_time_ms, 0.0);
1008    }
1009
1010    #[test]
1011    fn test_execution_plan_creation() {
1012        let mut cascade = vec![];
1013        cascade.push(CascadeStep {
1014            cell_id: "cell_1".to_string(),
1015            estimated_time: 10.0,
1016            dependencies: Some(HashSet::new()),
1017            can_skip: false,
1018            skipped: false,
1019        });
1020        cascade.push(CascadeStep {
1021            cell_id: "cell_2".to_string(),
1022            estimated_time: 20.0,
1023            dependencies: None,
1024            can_skip: true,
1025            skipped: false,
1026        });
1027
1028        let plan = ExecutionPlan {
1029            primary: "main_cell".to_string(),
1030            cascade,
1031            total_cells: 3,
1032            estimated_total_time: 35.0,
1033        };
1034
1035        assert_eq!(plan.primary, "main_cell");
1036        assert_eq!(plan.cascade.len(), 2);
1037        assert_eq!(plan.total_cells, 3);
1038        assert_eq!(plan.estimated_total_time, 35.0);
1039    }
1040
1041    #[test]
1042    fn test_cascade_step_properties() {
1043        let mut deps = HashSet::new();
1044        deps.insert(DefId(1));
1045        deps.insert(DefId(2));
1046
1047        let step = CascadeStep {
1048            cell_id: "test_cell".to_string(),
1049            estimated_time: 15.5,
1050            dependencies: Some(deps.clone()),
1051            can_skip: true,
1052            skipped: false,
1053        };
1054
1055        assert_eq!(step.cell_id, "test_cell");
1056        assert_eq!(step.estimated_time, 15.5);
1057        assert!(step.dependencies.is_some());
1058        assert_eq!(step.dependencies.unwrap().len(), 2);
1059        assert!(step.can_skip);
1060        assert!(!step.skipped);
1061    }
1062
1063    #[test]
1064    fn test_edge_creation() {
1065        let edge = Edge {
1066            from: "node_a".to_string(),
1067            to: "node_b".to_string(),
1068        };
1069
1070        assert_eq!(edge.from, "node_a");
1071        assert_eq!(edge.to, "node_b");
1072    }
1073
1074    #[test]
1075    fn test_session_version() {
1076        let version = SessionVersion::new(2, 1);
1077
1078        assert_eq!(version.major, 2);
1079        assert_eq!(version.minor, 1);
1080        assert_eq!(version.patch, 0);
1081
1082        let full_version = SessionVersion {
1083            major: 1,
1084            minor: 2,
1085            patch: 3,
1086        };
1087
1088        assert_eq!(full_version.major, 1);
1089        assert_eq!(full_version.minor, 2);
1090        assert_eq!(full_version.patch, 3);
1091    }
1092
1093    #[test]
1094    fn test_session_export_data() {
1095        let mut cell_cache = HashMap::new();
1096        cell_cache.insert("cell_1".to_string(), "let x = 10".to_string());
1097        cell_cache.insert("cell_2".to_string(), "print(x)".to_string());
1098
1099        let export_data = SessionExportData {
1100            version: SessionVersion::new(1, 0),
1101            globals: serde_json::json!({"x": 10, "y": 20}),
1102            cell_cache,
1103            execution_mode: "Manual".to_string(),
1104            memory_counter: 100,
1105            created_at: 1_234_567_890,
1106        };
1107
1108        assert_eq!(export_data.version.major, 1);
1109        assert_eq!(export_data.cell_cache.len(), 2);
1110        assert_eq!(export_data.execution_mode, "Manual");
1111        assert_eq!(export_data.memory_counter, 100);
1112        assert_eq!(export_data.created_at, 1_234_567_890);
1113    }
1114
1115    #[test]
1116    fn test_variable_inspection_result() {
1117        let result = VariableInspectionResult {
1118            total_variables: 5,
1119            memory_usage: 1024,
1120            variables: serde_json::json!({
1121                "x": {"type": "int", "value": 10},
1122                "y": {"type": "string", "value": "hello"}
1123            }),
1124        };
1125
1126        assert_eq!(result.total_variables, 5);
1127        assert_eq!(result.memory_usage, 1024);
1128        assert!(result.variables.is_object());
1129    }
1130
1131    #[test]
1132    fn test_execution_history_entry() {
1133        let entry = ExecutionHistoryEntry {
1134            sequence: 42,
1135            cell_id: "cell_xyz".to_string(),
1136            code: "result = compute()".to_string(),
1137            timestamp: 9_876_543_210,
1138            success: true,
1139        };
1140
1141        assert_eq!(entry.sequence, 42);
1142        assert_eq!(entry.cell_id, "cell_xyz");
1143        assert_eq!(entry.code, "result = compute()");
1144        assert_eq!(entry.timestamp, 9_876_543_210);
1145        assert!(entry.success);
1146    }
1147
1148    #[test]
1149    fn test_dependency_analysis_result() {
1150        let result = DependencyAnalysisResult {
1151            cell_id: "analysis_cell".to_string(),
1152            depends_on: vec!["cell_a".to_string(), "cell_b".to_string()],
1153            defines: vec!["var_x".to_string(), "func_y".to_string()],
1154            affects: vec!["cell_c".to_string()],
1155            is_stale: false,
1156        };
1157
1158        assert_eq!(result.cell_id, "analysis_cell");
1159        assert_eq!(result.depends_on.len(), 2);
1160        assert_eq!(result.defines.len(), 2);
1161        assert_eq!(result.affects.len(), 1);
1162        assert!(!result.is_stale);
1163    }
1164
1165    #[test]
1166    fn test_transaction_id() {
1167        let tx_id = TransactionId("tx_12345".to_string());
1168
1169        assert_eq!(tx_id.0, "tx_12345");
1170
1171        let another_tx = TransactionId("tx_67890".to_string());
1172        assert_ne!(tx_id.0, another_tx.0);
1173    }
1174
1175    // #[test]
1176    // fn test_shared_session_creation() {
1177    //     // Multiple method errors - GlobalRegistry::is_empty, u32::load don't exist
1178    //     let session = SharedSession::new();
1179    //     assert!(session.globals.is_empty());
1180    //     assert!(session.cell_cache.is_empty());
1181    //     assert_eq!(session.execution_mode, ExecutionMode::Manual);
1182    //     assert_eq!(session.memory_counter.load(Ordering::Relaxed), 0);
1183    // }
1184
1185    // #[test]
1186    // fn test_shared_session_execute() {
1187    //     // Result type issues - response.success/value/error don't exist on Result type
1188    //     let mut session = SharedSession::new();
1189    //     let response = session.execute("cell_1", "let x = 42");
1190    //     assert!(response.success);
1191    //     assert!(!response.value.is_empty());
1192    //     let response2 = session.execute("cell_2", "x");
1193    //     assert!(response2.success || response2.error.is_some());
1194    // }
1195
1196    // #[test]
1197    // fn test_shared_session_clear() {
1198    //     // SharedSession::clear method doesn't exist, globals.is_empty doesn't exist
1199    //     let mut session = SharedSession::new();
1200    //     session.execute("cell_1", "let x = 10");
1201    //     session.execute("cell_2", "let y = 20");
1202    //     session.clear();
1203    //     assert!(session.globals.is_empty());
1204    //     assert!(session.cell_cache.is_empty());
1205    // }
1206
1207    #[test]
1208    fn test_shared_session_mode_switching() {
1209        let mut session = SharedSession::new();
1210
1211        // Start in Manual mode
1212        assert_eq!(session.execution_mode, ExecutionMode::Manual);
1213
1214        // Switch to Reactive mode
1215        session.set_execution_mode(ExecutionMode::Reactive);
1216        assert_eq!(session.execution_mode, ExecutionMode::Reactive);
1217
1218        // Switch back to Manual
1219        session.set_execution_mode(ExecutionMode::Manual);
1220        assert_eq!(session.execution_mode, ExecutionMode::Manual);
1221    }
1222
1223    // #[test]
1224    // fn test_shared_session_export() {
1225    //     // SharedSession::export method doesn't exist
1226    //     let mut session = SharedSession::new();
1227    //     session.execute("cell_1", "let data = 100");
1228    //     let export_data = session.export();
1229    //     assert_eq!(export_data.version.major, 1);
1230    //     assert_eq!(export_data.version.minor, 0);
1231    //     assert!(!export_data.cell_cache.is_empty());
1232    // }
1233
1234    // #[test]
1235    // fn test_shared_session_import() {
1236    //     // SharedSession::import method doesn't exist, u32::load doesn't exist
1237    //     let mut session = SharedSession::new();
1238    //     let import_data = SessionExportData {
1239    //         version: SessionVersion::new(1, 0),
1240    //         globals: serde_json::json!({"imported": true}),
1241    //         cell_cache: HashMap::new(),
1242    //         execution_mode: "Reactive".to_string(),
1243    //         memory_counter: 50,
1244    //         created_at: 1234567890,
1245    //     };
1246    //     let result = session.import(import_data);
1247    //     assert!(result.is_ok());
1248    //     assert_eq!(session.memory_counter.load(Ordering::Relaxed), 50);
1249    // }
1250
1251    #[test]
1252    fn test_def_id_uniqueness() {
1253        let mut ids = HashSet::new();
1254        for _ in 0..100 {
1255            let id = DefId::next();
1256            assert!(ids.insert(id));
1257        }
1258        assert_eq!(ids.len(), 100);
1259    }
1260}
1261
1262#[cfg(test)]
1263mod property_tests_shared_session {
1264    use super::*;
1265    use proptest::proptest;
1266
1267    proptest! {
1268        #[test]
1269        fn test_execute_response_success_never_panics(val: i64) {
1270            let response = ExecuteResponse::success(Value::Integer(val));
1271            assert!(response.success);
1272            assert!(response.error.is_none());
1273        }
1274
1275        #[test]
1276        fn test_execute_response_error_never_panics(error_msg: String) {
1277            let response = ExecuteResponse::error(error_msg.clone());
1278            assert!(!response.success);
1279            assert_eq!(response.error, Some(error_msg));
1280        }
1281
1282        #[test]
1283        fn test_session_version_creation(major: u32, minor: u32) {
1284            let version = SessionVersion::new(major, minor);
1285            assert_eq!(version.major, major);
1286            assert_eq!(version.minor, minor);
1287            assert_eq!(version.patch, 0);
1288        }
1289    }
1290}