Skip to main content

sublinear_solver/temporal_nexus/core/
identity.rs

1//! Identity Continuity Tracking for Temporal Consciousness
2//!
3//! This module tracks and preserves identity continuity across temporal boundaries
4//! to ensure consciousness coherence. It monitors identity state, detects breaks
5//! in continuity, and provides mechanisms for identity preservation.
6
7use std::collections::{HashMap, VecDeque};
8use super::{TemporalResult, TemporalError, TscTimestamp};
9
10/// Metrics for identity continuity analysis
11#[derive(Debug, Clone, Default)]
12pub struct ContinuityMetrics {
13    pub continuity_score: f64,
14    pub identity_stability: f64,
15    pub continuity_breaks: u64,
16    pub average_gap_duration_ns: f64,
17    pub max_gap_duration_ns: u64,
18    pub identity_coherence: f64,
19    pub temporal_consistency: f64,
20    pub preservation_efficiency: f64,
21}
22
23/// Identity state snapshot at a specific time
24#[derive(Debug, Clone)]
25struct IdentitySnapshot {
26    timestamp: TscTimestamp,
27    state_hash: u64,
28    feature_vector: Vec<f64>,
29    coherence_score: f64,
30    stability_metric: f64,
31    memory_fingerprint: Vec<u8>,
32}
33
34impl IdentitySnapshot {
35    /// Create a new identity snapshot
36    fn new(timestamp: TscTimestamp, state: &[u8]) -> Self {
37        let feature_vector = Self::extract_features(state);
38        let state_hash = Self::compute_hash(state);
39        let coherence_score = Self::calculate_coherence(&feature_vector);
40        
41        Self {
42            timestamp,
43            state_hash,
44            feature_vector,
45            coherence_score,
46            stability_metric: 1.0, // Will be updated during tracking
47            memory_fingerprint: state.to_vec(),
48        }
49    }
50    
51    /// Extract feature vector from state data
52    fn extract_features(state: &[u8]) -> Vec<f64> {
53        if state.is_empty() {
54            return vec![0.0; 16]; // Default feature size
55        }
56        
57        let mut features = Vec::with_capacity(16);
58        
59        // Statistical features
60        let mean = state.iter().map(|&x| x as f64).sum::<f64>() / state.len() as f64;
61        features.push(mean);
62        
63        let variance = state.iter()
64            .map(|&x| (x as f64 - mean).powi(2))
65            .sum::<f64>() / state.len() as f64;
66        features.push(variance.sqrt());
67        
68        // Entropy-like measure
69        let mut byte_counts = [0u32; 256];
70        for &byte in state {
71            byte_counts[byte as usize] += 1;
72        }
73        
74        let entropy = byte_counts.iter()
75            .filter(|&&count| count > 0)
76            .map(|&count| {
77                let p = count as f64 / state.len() as f64;
78                -p * p.ln()
79            })
80            .sum::<f64>();
81        features.push(entropy);
82        
83        // Spectral features (simple FFT-like)
84        for i in 0..8 {
85            let freq_component = state.iter()
86                .enumerate()
87                .map(|(j, &x)| {
88                    let phase = 2.0 * std::f64::consts::PI * (i + 1) as f64 * j as f64 / state.len() as f64;
89                    x as f64 * phase.cos()
90                })
91                .sum::<f64>();
92            features.push(freq_component / state.len() as f64);
93        }
94        
95        // Compression ratio estimate
96        let complexity = Self::estimate_complexity(state);
97        features.push(complexity);
98        
99        // Pattern density
100        let pattern_density = Self::calculate_pattern_density(state);
101        features.push(pattern_density);
102        
103        // Autocorrelation at lag 1
104        let autocorr = Self::calculate_autocorrelation(state, 1);
105        features.push(autocorr);
106        
107        // Trend measure
108        let trend = Self::calculate_trend(state);
109        features.push(trend);
110        
111        // Normalize features
112        for feature in &mut features {
113            *feature = feature.tanh(); // Bound between -1 and 1
114        }
115        
116        features
117    }
118    
119    /// Compute hash of state data
120    fn compute_hash(state: &[u8]) -> u64 {
121        use std::collections::hash_map::DefaultHasher;
122        use std::hash::{Hash, Hasher};
123        
124        let mut hasher = DefaultHasher::new();
125        state.hash(&mut hasher);
126        hasher.finish()
127    }
128    
129    /// Calculate coherence score from feature vector
130    fn calculate_coherence(features: &[f64]) -> f64 {
131        if features.is_empty() {
132            return 0.0;
133        }
134        
135        // Coherence based on feature consistency
136        let mean = features.iter().sum::<f64>() / features.len() as f64;
137        let variance = features.iter()
138            .map(|x| (x - mean).powi(2))
139            .sum::<f64>() / features.len() as f64;
140        
141        // Lower variance = higher coherence
142        (-variance).exp().min(1.0)
143    }
144    
145    /// Calculate similarity with another snapshot.
146    ///
147    /// Pure cosine similarity is scale-invariant and the feature
148    /// extractor tanh-normalises every component, so a small state and
149    /// a 10× larger one with the same shape both saturate near
150    /// `(1, 1, 1, …)` and look identical (similarity ≈ 1.0). That
151    /// silently swallows the "very different states" case the
152    /// continuity tracker is supposed to flag (test_continuity_break_detection).
153    ///
154    /// Combine cosine with a mean-L1 dissimilarity so component-wise
155    /// differences register even after saturation, then weight to keep
156    /// behavior similar for genuinely-similar states.
157    fn calculate_similarity(&self, other: &IdentitySnapshot) -> f64 {
158        if self.feature_vector.len() != other.feature_vector.len() || self.feature_vector.is_empty() {
159            return 0.0;
160        }
161
162        let dot_product: f64 = self.feature_vector.iter()
163            .zip(other.feature_vector.iter())
164            .map(|(a, b)| a * b)
165            .sum();
166        let magnitude_self: f64 = self.feature_vector.iter().map(|x| x * x).sum::<f64>().sqrt();
167        let magnitude_other: f64 = other.feature_vector.iter().map(|x| x * x).sum::<f64>().sqrt();
168
169        let cosine = if magnitude_self > 0.0 && magnitude_other > 0.0 {
170            dot_product / (magnitude_self * magnitude_other)
171        } else {
172            0.0
173        };
174
175        // Mean L1 + Chebyshev (max-per-component) over tanh-bounded
176        // features. Mean L1 alone isn't strong enough under tanh
177        // saturation: when one state is a 10× rescale of another, most
178        // features look identical near ±1 and the mean stays small.
179        // Chebyshev fires on the *worst* single component, which gives
180        // a clean break signal when any spectral/statistical feature
181        // genuinely differs. Both metrics are bounded to [0, 1].
182        let mut mean_l1 = 0.0;
183        let mut max_diff: f64 = 0.0;
184        for (a, b) in self.feature_vector.iter().zip(other.feature_vector.iter()) {
185            let d = (a - b).abs();
186            mean_l1 += d;
187            if d > max_diff {
188                max_diff = d;
189            }
190        }
191        mean_l1 /= self.feature_vector.len() as f64;
192        let l1_similarity = (1.0 - mean_l1 / 2.0).max(0.0);
193        // Features are tanh-bounded in [-1, 1] so max_diff is in [0, 2].
194        let chebyshev_similarity = (1.0 - max_diff / 2.0).max(0.0);
195
196        // Weighted blend: cosine 30%, mean-L1 30%, Chebyshev 40%. Identical
197        // states score 1.0; substantially different states (one large
198        // Chebyshev diff) drop well below 0.9 without breaking the
199        // "slightly different" test_continuity_tracker case.
200        0.3 * cosine + 0.3 * l1_similarity + 0.4 * chebyshev_similarity
201    }
202    
203    // Helper methods for feature extraction
204    
205    fn estimate_complexity(data: &[u8]) -> f64 {
206        if data.len() < 2 {
207            return 0.0;
208        }
209        
210        // Simple complexity estimate based on run lengths
211        let mut runs = 0;
212        let mut current_byte = data[0];
213        
214        for &byte in &data[1..] {
215            if byte != current_byte {
216                runs += 1;
217                current_byte = byte;
218            }
219        }
220        
221        runs as f64 / data.len() as f64
222    }
223    
224    fn calculate_pattern_density(data: &[u8]) -> f64 {
225        if data.len() < 4 {
226            return 0.0;
227        }
228        
229        let mut patterns = HashMap::new();
230        
231        // Count 2-byte patterns
232        for window in data.windows(2) {
233            *patterns.entry((window[0], window[1])).or_insert(0) += 1;
234        }
235        
236        patterns.len() as f64 / (data.len() - 1) as f64
237    }
238    
239    fn calculate_autocorrelation(data: &[u8], lag: usize) -> f64 {
240        if data.len() <= lag {
241            return 0.0;
242        }
243        
244        let mean = data.iter().map(|&x| x as f64).sum::<f64>() / data.len() as f64;
245        
246        let numerator: f64 = data.iter()
247            .take(data.len() - lag)
248            .zip(data.iter().skip(lag))
249            .map(|(&x, &y)| (x as f64 - mean) * (y as f64 - mean))
250            .sum();
251        
252        let denominator: f64 = data.iter()
253            .map(|&x| (x as f64 - mean).powi(2))
254            .sum();
255        
256        if denominator > 0.0 {
257            numerator / denominator
258        } else {
259            0.0
260        }
261    }
262    
263    fn calculate_trend(data: &[u8]) -> f64 {
264        if data.len() < 2 {
265            return 0.0;
266        }
267        
268        let n = data.len() as f64;
269        let sum_x = (0..data.len()).sum::<usize>() as f64;
270        let sum_y = data.iter().map(|&x| x as f64).sum::<f64>();
271        let sum_xy = data.iter()
272            .enumerate()
273            .map(|(i, &y)| i as f64 * y as f64)
274            .sum::<f64>();
275        let sum_x2 = (0..data.len())
276            .map(|i| (i as f64).powi(2))
277            .sum::<f64>();
278        
279        let denominator = n * sum_x2 - sum_x * sum_x;
280        if denominator > 0.0 {
281            (n * sum_xy - sum_x * sum_y) / denominator
282        } else {
283            0.0
284        }
285    }
286}
287
288/// Identity continuity tracker
289pub struct IdentityContinuityTracker {
290    snapshots: VecDeque<IdentitySnapshot>,
291    metrics: ContinuityMetrics,
292    max_snapshots: usize,
293    continuity_threshold: f64,
294    gap_tolerance_ns: u64,
295    identity_baseline: Option<IdentitySnapshot>,
296    last_validation_time: Option<TscTimestamp>,
297}
298
299impl IdentityContinuityTracker {
300    /// Create a new identity continuity tracker
301    pub fn new() -> Self {
302        Self {
303            snapshots: VecDeque::new(),
304            metrics: ContinuityMetrics::default(),
305            max_snapshots: 1000,
306            continuity_threshold: 0.7, // 70% similarity threshold
307            gap_tolerance_ns: 1_000_000, // 1ms tolerance
308            identity_baseline: None,
309            last_validation_time: None,
310        }
311    }
312    
313    /// Track identity continuity at a specific timestamp
314    pub fn track_continuity(&mut self, timestamp: TscTimestamp, state: &[u8]) -> TemporalResult<()> {
315        let snapshot = IdentitySnapshot::new(timestamp, state);
316        
317        // Establish baseline if this is the first snapshot
318        if self.identity_baseline.is_none() {
319            self.identity_baseline = Some(snapshot.clone());
320        }
321        
322        // Check for continuity breaks
323        let continuity_break_info = if let Some(prev_snapshot) = self.snapshots.back() {
324            Some((snapshot.clone(), prev_snapshot.clone()))
325        } else {
326            None
327        };
328
329        if let Some((current, previous)) = continuity_break_info {
330            self.check_continuity_break(&current, &previous)?;
331        }
332        
333        // Update stability metrics
334        self.update_stability_metrics(&snapshot);
335        
336        // Store the snapshot
337        self.store_snapshot(snapshot);
338        
339        // Update overall metrics
340        self.update_metrics();
341        
342        self.last_validation_time = Some(timestamp);
343        
344        Ok(())
345    }
346    
347    /// Validate current identity continuity.
348    ///
349    /// Returns Ok with too little history to judge (fewer than 2 snapshots) —
350    /// otherwise the very first scheduler tick that runs an
351    /// `IdentityPreservation { continuity_check: true }` task would fail
352    /// because `continuity_score` is still at its `0.0` initial value,
353    /// below any sensible threshold.
354    pub fn validate_continuity(&self) -> TemporalResult<()> {
355        if self.snapshots.len() < 2 {
356            return Ok(());
357        }
358        if self.metrics.continuity_score < self.continuity_threshold {
359            return Err(TemporalError::IdentityContinuityBreak {
360                gap_ns: self.metrics.max_gap_duration_ns,
361            });
362        }
363
364        Ok(())
365    }
366    
367    /// Get current continuity metrics
368    pub fn get_metrics(&self) -> TemporalResult<ContinuityMetrics> {
369        Ok(self.metrics.clone())
370    }
371    
372    /// Get identity stability score
373    pub fn get_identity_stability(&self) -> f64 {
374        self.metrics.identity_stability
375    }
376    
377    /// Get continuity score
378    pub fn get_continuity_score(&self) -> f64 {
379        self.metrics.continuity_score
380    }
381    
382    /// Reset tracking state
383    pub fn reset(&mut self) {
384        self.snapshots.clear();
385        self.metrics = ContinuityMetrics::default();
386        self.identity_baseline = None;
387        self.last_validation_time = None;
388    }
389    
390    /// Set continuity threshold
391    pub fn set_continuity_threshold(&mut self, threshold: f64) {
392        self.continuity_threshold = threshold.clamp(0.0, 1.0);
393    }
394    
395    /// Get recent identity trajectory
396    pub fn get_identity_trajectory(&self, window_size: usize) -> Vec<f64> {
397        self.snapshots.iter()
398            .rev()
399            .take(window_size)
400            .map(|s| s.coherence_score)
401            .collect()
402    }
403    
404    /// Calculate identity drift over time
405    pub fn calculate_identity_drift(&self) -> f64 {
406        if let Some(baseline) = &self.identity_baseline {
407            if let Some(current) = self.snapshots.back() {
408                return 1.0 - baseline.calculate_similarity(current);
409            }
410        }
411        0.0
412    }
413    
414    // Private helper methods
415    
416    fn check_continuity_break(
417        &mut self,
418        current: &IdentitySnapshot,
419        previous: &IdentitySnapshot,
420    ) -> TemporalResult<()> {
421        // Check temporal gap
422        let gap_ns = current.timestamp.nanos_since(previous.timestamp, 3_000_000_000);
423        if gap_ns > self.gap_tolerance_ns {
424            self.metrics.continuity_breaks += 1;
425            self.metrics.max_gap_duration_ns = self.metrics.max_gap_duration_ns.max(gap_ns);
426        }
427        
428        // Check similarity
429        let similarity = current.calculate_similarity(previous);
430        if similarity < self.continuity_threshold {
431            self.metrics.continuity_breaks += 1;
432        }
433        
434        Ok(())
435    }
436    
437    fn update_stability_metrics(&mut self, snapshot: &IdentitySnapshot) {
438        if self.snapshots.len() < 2 {
439            return;
440        }
441        
442        // Calculate stability based on recent snapshots
443        let recent_snapshots: Vec<_> = self.snapshots.iter().rev().take(10).collect();
444        
445        if recent_snapshots.len() >= 2 {
446            let mut similarities = Vec::new();
447            
448            for i in 0..recent_snapshots.len() - 1 {
449                let sim = recent_snapshots[i].calculate_similarity(recent_snapshots[i + 1]);
450                similarities.push(sim);
451            }
452            
453            // Current similarity with latest snapshot
454            let current_sim = snapshot.calculate_similarity(recent_snapshots[0]);
455            similarities.push(current_sim);
456            
457            // Stability is average similarity over recent window
458            let avg_similarity = similarities.iter().sum::<f64>() / similarities.len() as f64;
459            
460            // Update stability metric with exponential moving average
461            let alpha = 0.1;
462            self.metrics.identity_stability = (1.0 - alpha) * self.metrics.identity_stability + alpha * avg_similarity;
463        }
464    }
465    
466    fn store_snapshot(&mut self, snapshot: IdentitySnapshot) {
467        self.snapshots.push_back(snapshot);
468        
469        // Keep history bounded
470        while self.snapshots.len() > self.max_snapshots {
471            self.snapshots.pop_front();
472        }
473    }
474    
475    fn update_metrics(&mut self) {
476        if self.snapshots.len() < 2 {
477            return;
478        }
479        
480        // Calculate continuity score
481        let mut total_similarity = 0.0;
482        let mut similarity_count = 0;
483        
484        for window in self.snapshots.iter().collect::<Vec<_>>().windows(2) {
485            let sim = window[1].calculate_similarity(window[0]);
486            total_similarity += sim;
487            similarity_count += 1;
488        }
489        
490        if similarity_count > 0 {
491            self.metrics.continuity_score = total_similarity / similarity_count as f64;
492        }
493        
494        // Calculate coherence
495        let coherence_scores: Vec<f64> = self.snapshots.iter()
496            .map(|s| s.coherence_score)
497            .collect();
498        
499        if !coherence_scores.is_empty() {
500            self.metrics.identity_coherence = coherence_scores.iter().sum::<f64>() / coherence_scores.len() as f64;
501        }
502        
503        // Calculate temporal consistency
504        self.calculate_temporal_consistency();
505        
506        // Calculate preservation efficiency
507        self.calculate_preservation_efficiency();
508        
509        // Update gap metrics
510        self.update_gap_metrics();
511    }
512    
513    fn calculate_temporal_consistency(&mut self) {
514        if self.snapshots.len() < 3 {
515            return;
516        }
517        
518        // Measure how consistently identity features change over time
519        let mut consistency_scores = Vec::new();
520        
521        for i in 2..self.snapshots.len() {
522            let s1 = &self.snapshots[i - 2];
523            let s2 = &self.snapshots[i - 1];
524            let s3 = &self.snapshots[i];
525            
526            // Calculate velocity vectors
527            let vel1: Vec<f64> = s2.feature_vector.iter()
528                .zip(s1.feature_vector.iter())
529                .map(|(a, b)| a - b)
530                .collect();
531            
532            let vel2: Vec<f64> = s3.feature_vector.iter()
533                .zip(s2.feature_vector.iter())
534                .map(|(a, b)| a - b)
535                .collect();
536            
537            // Calculate consistency as cosine similarity of velocity vectors
538            let dot_product: f64 = vel1.iter().zip(vel2.iter()).map(|(a, b)| a * b).sum();
539            let mag1: f64 = vel1.iter().map(|x| x * x).sum::<f64>().sqrt();
540            let mag2: f64 = vel2.iter().map(|x| x * x).sum::<f64>().sqrt();
541            
542            if mag1 > 0.0 && mag2 > 0.0 {
543                consistency_scores.push(dot_product / (mag1 * mag2));
544            }
545        }
546        
547        if !consistency_scores.is_empty() {
548            self.metrics.temporal_consistency = consistency_scores.iter().sum::<f64>() / consistency_scores.len() as f64;
549        }
550    }
551    
552    fn calculate_preservation_efficiency(&mut self) {
553        if let Some(baseline) = &self.identity_baseline {
554            if let Some(current) = self.snapshots.back() {
555                // Efficiency based on how well identity is preserved relative to baseline
556                let similarity_to_baseline = current.calculate_similarity(baseline);
557                let time_factor = 1.0 / (1.0 + self.snapshots.len() as f64 / 1000.0); // Decay over time
558                
559                self.metrics.preservation_efficiency = similarity_to_baseline * time_factor;
560            }
561        }
562    }
563    
564    fn update_gap_metrics(&mut self) {
565        if self.snapshots.len() < 2 {
566            return;
567        }
568        
569        let mut gaps = Vec::new();
570        
571        for window in self.snapshots.iter().collect::<Vec<_>>().windows(2) {
572            let gap_ns = window[1].timestamp.nanos_since(window[0].timestamp, 3_000_000_000);
573            gaps.push(gap_ns);
574        }
575        
576        if !gaps.is_empty() {
577            self.metrics.average_gap_duration_ns = gaps.iter().sum::<u64>() as f64 / gaps.len() as f64;
578            self.metrics.max_gap_duration_ns = *gaps.iter().max().unwrap_or(&0);
579        }
580    }
581}
582
583impl Default for IdentityContinuityTracker {
584    fn default() -> Self {
585        Self::new()
586    }
587}
588
589#[cfg(test)]
590mod tests {
591    use super::*;
592    
593    #[test]
594    fn test_identity_snapshot_creation() {
595        let timestamp = TscTimestamp::now();
596        let state = vec![1, 2, 3, 4, 5];
597        
598        let snapshot = IdentitySnapshot::new(timestamp, &state);
599        assert_eq!(snapshot.timestamp, timestamp);
600        assert!(!snapshot.feature_vector.is_empty());
601        assert!(snapshot.coherence_score >= 0.0 && snapshot.coherence_score <= 1.0);
602    }
603    
604    #[test]
605    fn test_feature_extraction() {
606        let state = vec![1, 2, 3, 4, 5, 4, 3, 2, 1];
607        let features = IdentitySnapshot::extract_features(&state);
608        
609        assert!(!features.is_empty());
610        // Features should be normalized
611        for &feature in &features {
612            assert!(feature >= -1.0 && feature <= 1.0);
613        }
614    }
615    
616    #[test]
617    fn test_similarity_calculation() {
618        let timestamp = TscTimestamp::now();
619        let state1 = vec![1, 2, 3, 4, 5];
620        let state2 = vec![1, 2, 3, 4, 5]; // Identical
621        let state3 = vec![5, 4, 3, 2, 1]; // Different
622        
623        let snapshot1 = IdentitySnapshot::new(timestamp, &state1);
624        let snapshot2 = IdentitySnapshot::new(timestamp, &state2);
625        let snapshot3 = IdentitySnapshot::new(timestamp, &state3);
626        
627        let sim12 = snapshot1.calculate_similarity(&snapshot2);
628        let sim13 = snapshot1.calculate_similarity(&snapshot3);
629        
630        assert!(sim12 > sim13); // Identical states should be more similar
631        assert!(sim12 >= 0.0 && sim12 <= 1.0);
632        assert!(sim13 >= 0.0 && sim13 <= 1.0);
633    }
634    
635    #[test]
636    fn test_continuity_tracker() {
637        let mut tracker = IdentityContinuityTracker::new();
638        let timestamp = TscTimestamp::now();
639        let state = vec![1, 2, 3, 4, 5];
640        
641        tracker.track_continuity(timestamp, &state).unwrap();
642        assert_eq!(tracker.snapshots.len(), 1);
643        
644        // Track another similar state
645        let timestamp2 = timestamp.add_nanos(1000, 3_000_000_000);
646        let state2 = vec![1, 2, 3, 4, 6]; // Slightly different
647        
648        tracker.track_continuity(timestamp2, &state2).unwrap();
649        assert_eq!(tracker.snapshots.len(), 2);
650        
651        let metrics = tracker.get_metrics().unwrap();
652        assert!(metrics.continuity_score > 0.0);
653    }
654    
655    #[test]
656    fn test_continuity_break_detection() {
657        let mut tracker = IdentityContinuityTracker::new();
658        tracker.set_continuity_threshold(0.9); // High threshold
659        
660        let timestamp = TscTimestamp::now();
661        let state1 = vec![1, 2, 3, 4, 5];
662        let state2 = vec![10, 20, 30, 40, 50]; // Very different
663        
664        tracker.track_continuity(timestamp, &state1).unwrap();
665        
666        let timestamp2 = timestamp.add_nanos(1000, 3_000_000_000);
667        tracker.track_continuity(timestamp2, &state2).unwrap();
668        
669        // Should detect continuity break
670        assert!(tracker.metrics.continuity_breaks > 0);
671    }
672    
673    #[test]
674    fn test_identity_drift_calculation() {
675        let mut tracker = IdentityContinuityTracker::new();
676        let timestamp = TscTimestamp::now();
677        
678        // Start with baseline
679        let baseline_state = vec![1, 2, 3, 4, 5];
680        tracker.track_continuity(timestamp, &baseline_state).unwrap();
681        
682        // Gradual drift
683        for i in 1..=10 {
684            let timestamp_i = timestamp.add_nanos(i * 1000, 3_000_000_000);
685            let drifted_state = vec![1 + i as u8, 2, 3, 4, 5];
686            tracker.track_continuity(timestamp_i, &drifted_state).unwrap();
687        }
688        
689        let drift = tracker.calculate_identity_drift();
690        assert!(drift > 0.0); // Should detect some drift
691        assert!(drift <= 1.0); // Should be bounded
692    }
693    
694    #[test]
695    fn test_stability_metrics() {
696        let mut tracker = IdentityContinuityTracker::new();
697        let timestamp = TscTimestamp::now();
698        
699        // Track several stable states
700        for i in 0..10 {
701            let timestamp_i = timestamp.add_nanos(i * 1000, 3_000_000_000);
702            let stable_state = vec![1, 2, 3, 4, 5]; // Same state
703            tracker.track_continuity(timestamp_i, &stable_state).unwrap();
704        }
705        
706        assert!(tracker.get_identity_stability() > 0.5); // Should be stable
707    }
708}