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