@feltdb/core 0.4.12 → 0.4.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/dist/cli/commands.js +42 -25
  2. package/dist/cli/index.js +1 -1
  3. package/dist/create/cli.js +39 -4
  4. package/dist/create/create.js +30 -18
  5. package/dist/create/docker-compose-generator.js +68 -11
  6. package/dist/create/package-versions.js +1 -1
  7. package/dist/create/server-source/Cargo.lock +2238 -0
  8. package/dist/create/server-source/Cargo.toml +7 -0
  9. package/dist/create/server-source/crates/feltdb/Cargo.lock +175 -0
  10. package/dist/create/server-source/crates/feltdb/Cargo.toml +79 -0
  11. package/dist/create/server-source/crates/feltdb/benches/baselines/gate-13-redux.json +370 -0
  12. package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +589 -0
  13. package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +259 -0
  14. package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +446 -0
  15. package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +272 -0
  16. package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +378 -0
  17. package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +414 -0
  18. package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +486 -0
  19. package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +273 -0
  20. package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +587 -0
  21. package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +513 -0
  22. package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +395 -0
  23. package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +469 -0
  24. package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +409 -0
  25. package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +627 -0
  26. package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +383 -0
  27. package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +298 -0
  28. package/dist/create/server-source/crates/feltdb/src/acceptance_tests.rs +1698 -0
  29. package/dist/create/server-source/crates/feltdb/src/acquisition.rs +286 -0
  30. package/dist/create/server-source/crates/feltdb/src/admission.rs +192 -0
  31. package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +477 -0
  32. package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +566 -0
  33. package/dist/create/server-source/crates/feltdb/src/analytics.rs +475 -0
  34. package/dist/create/server-source/crates/feltdb/src/application.rs +2244 -0
  35. package/dist/create/server-source/crates/feltdb/src/application_runtime.rs +1070 -0
  36. package/dist/create/server-source/crates/feltdb/src/authorization.rs +1030 -0
  37. package/dist/create/server-source/crates/feltdb/src/bin/feltdb_node.rs +266 -0
  38. package/dist/create/server-source/crates/feltdb/src/capabilities/mod.rs +8 -0
  39. package/dist/create/server-source/crates/feltdb/src/capabilities/search.rs +418 -0
  40. package/dist/create/server-source/crates/feltdb/src/capabilities/vector.rs +482 -0
  41. package/dist/create/server-source/crates/feltdb/src/capability.rs +903 -0
  42. package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +261 -0
  43. package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +78 -0
  44. package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +2214 -0
  45. package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +194 -0
  46. package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +427 -0
  47. package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +453 -0
  48. package/dist/create/server-source/crates/feltdb/src/content_distribution.rs +465 -0
  49. package/dist/create/server-source/crates/feltdb/src/convergence.rs +618 -0
  50. package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +418 -0
  51. package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +380 -0
  52. package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +362 -0
  53. package/dist/create/server-source/crates/feltdb/src/cron.rs +294 -0
  54. package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +474 -0
  55. package/dist/create/server-source/crates/feltdb/src/distributed_tests.rs +1003 -0
  56. package/dist/create/server-source/crates/feltdb/src/distributed_transactions.rs +536 -0
  57. package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +364 -0
  58. package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +235 -0
  59. package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +207 -0
  60. package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +316 -0
  61. package/dist/create/server-source/crates/feltdb/src/execution.rs +426 -0
  62. package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +219 -0
  63. package/dist/create/server-source/crates/feltdb/src/indexing.rs +779 -0
  64. package/dist/create/server-source/crates/feltdb/src/lib.rs +2838 -0
  65. package/dist/create/server-source/crates/feltdb/src/materialization.rs +184 -0
  66. package/dist/create/server-source/crates/feltdb/src/metrics.rs +267 -0
  67. package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +311 -0
  68. package/dist/create/server-source/crates/feltdb/src/observability.rs +366 -0
  69. package/dist/create/server-source/crates/feltdb/src/operation.rs +251 -0
  70. package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +438 -0
  71. package/dist/create/server-source/crates/feltdb/src/operation_log.rs +344 -0
  72. package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +477 -0
  73. package/dist/create/server-source/crates/feltdb/src/peer_registry.rs +166 -0
  74. package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +261 -0
  75. package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +560 -0
  76. package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +3226 -0
  77. package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +201 -0
  78. package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +263 -0
  79. package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +484 -0
  80. package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +216 -0
  81. package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +281 -0
  82. package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +323 -0
  83. package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +573 -0
  84. package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +404 -0
  85. package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +418 -0
  86. package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +381 -0
  87. package/dist/create/server-source/crates/feltdb/src/phase_1c_three_node.rs +523 -0
  88. package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +334 -0
  89. package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +306 -0
  90. package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +355 -0
  91. package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +395 -0
  92. package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +346 -0
  93. package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +430 -0
  94. package/dist/create/server-source/crates/feltdb/src/production_api.rs +400 -0
  95. package/dist/create/server-source/crates/feltdb/src/provenance.rs +207 -0
  96. package/dist/create/server-source/crates/feltdb/src/query_performance.rs +217 -0
  97. package/dist/create/server-source/crates/feltdb/src/references.rs +404 -0
  98. package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +401 -0
  99. package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +160 -0
  100. package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +132 -0
  101. package/dist/create/server-source/crates/feltdb/src/routing.rs +409 -0
  102. package/dist/create/server-source/crates/feltdb/src/sharding.rs +523 -0
  103. package/dist/create/server-source/crates/feltdb/src/state_contract.rs +3118 -0
  104. package/dist/create/server-source/crates/feltdb/src/state_hash.rs +291 -0
  105. package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +376 -0
  106. package/dist/create/server-source/crates/feltdb/src/storage.rs +267 -0
  107. package/dist/create/server-source/crates/feltdb/src/submission.rs +477 -0
  108. package/dist/create/server-source/crates/feltdb/src/sync.rs +483 -0
  109. package/dist/create/server-source/crates/feltdb/src/sync_contract.rs +822 -0
  110. package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +366 -0
  111. package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +473 -0
  112. package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +949 -0
  113. package/dist/create/server-source/crates/feltdb/src/transactions.rs +646 -0
  114. package/dist/create/server-source/crates/feltdb/src/trigger.rs +390 -0
  115. package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +928 -0
  116. package/dist/create/server-source/crates/feltdb/src/workflow.rs +713 -0
  117. package/dist/create/server-source/crates/feltdb/src/workflow_acceptance_tests.rs +510 -0
  118. package/dist/create/server-source/crates/feltdb/src/workflow_integration.rs +354 -0
  119. package/dist/create/server-source/crates/feltdb/src/workflow_runtime.rs +594 -0
  120. package/dist/create/server-source/crates/feltdb/src/workload.rs +1310 -0
  121. package/dist/create/server-source/crates/feltdb-server/Cargo.toml +23 -0
  122. package/dist/create/server-source/crates/feltdb-server/README.md +79 -0
  123. package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +73 -0
  124. package/dist/create/server-source/crates/feltdb-server/src/application_contract.rs +425 -0
  125. package/dist/create/server-source/crates/feltdb-server/src/artifacts.rs +449 -0
  126. package/dist/create/server-source/crates/feltdb-server/src/audit.rs +59 -0
  127. package/dist/create/server-source/crates/feltdb-server/src/auth.rs +308 -0
  128. package/dist/create/server-source/crates/feltdb-server/src/authorization.rs +228 -0
  129. package/dist/create/server-source/crates/feltdb-server/src/backup.rs +416 -0
  130. package/dist/create/server-source/crates/feltdb-server/src/causal.rs +218 -0
  131. package/dist/create/server-source/crates/feltdb-server/src/certification.rs +223 -0
  132. package/dist/create/server-source/crates/feltdb-server/src/clock.rs +132 -0
  133. package/dist/create/server-source/crates/feltdb-server/src/cluster.rs +165 -0
  134. package/dist/create/server-source/crates/feltdb-server/src/connections.rs +1044 -0
  135. package/dist/create/server-source/crates/feltdb-server/src/content.rs +111 -0
  136. package/dist/create/server-source/crates/feltdb-server/src/identity.rs +250 -0
  137. package/dist/create/server-source/crates/feltdb-server/src/key_management.rs +78 -0
  138. package/dist/create/server-source/crates/feltdb-server/src/key_provider.rs +247 -0
  139. package/dist/create/server-source/crates/feltdb-server/src/leases.rs +123 -0
  140. package/dist/create/server-source/crates/feltdb-server/src/lib.rs +25 -0
  141. package/dist/create/server-source/crates/feltdb-server/src/main.rs +8715 -0
  142. package/dist/create/server-source/crates/feltdb-server/src/metrics.rs +97 -0
  143. package/dist/create/server-source/crates/feltdb-server/src/portable_bundle.rs +350 -0
  144. package/dist/create/server-source/crates/feltdb-server/src/principals.rs +510 -0
  145. package/dist/create/server-source/crates/feltdb-server/src/providers.rs +269 -0
  146. package/dist/create/server-source/crates/feltdb-server/src/releases.rs +2563 -0
  147. package/dist/create/server-source/crates/feltdb-server/src/sessions.rs +156 -0
  148. package/dist/create/server-source/crates/feltdb-server/src/tenancy.rs +1097 -0
  149. package/dist/create/server-source/crates/feltdb-server/src/versions.rs +264 -0
  150. package/dist/create/server-source/crates/feltdb-wasm/Cargo.toml +31 -0
  151. package/dist/create/server-source/crates/feltdb-wasm/src/lib.rs +1086 -0
  152. package/dist/create/server-source/crates/feltdb-wasm/test.db +0 -0
  153. package/dist/flowspec.d.ts +2 -0
  154. package/dist/flowspec.d.ts.map +1 -1
  155. package/dist/flowspec.js +28 -4
  156. package/dist/state-contract.d.ts +7 -1
  157. package/dist/state-contract.d.ts.map +1 -1
  158. package/dist/studio/components/ApplicationDesigner.d.ts.map +1 -1
  159. package/dist/studio/components/ConflictExplorer.d.ts.map +1 -1
  160. package/dist/studio/components/index.js +1 -1
  161. package/dist/studio/{components-Q0Nx6gLE.js → components-9kDSWiGL.js} +163 -47
  162. package/dist/studio/index.js +31 -28
  163. package/dist/studio-app/assets/{index-BHOmLZF-.js → index-DVdvmf69.js} +9 -9
  164. package/dist/studio-app/index.html +1 -1
  165. package/package.json +1 -1
@@ -0,0 +1,2214 @@
1
+ /// Causal Consistency Dependency Barrier (Gate 4)
2
+ ///
3
+ /// Implements the critical invariant: No operation becomes visible at a replica
4
+ /// until all causal dependencies encoded by its StateVersion are satisfied.
5
+ ///
6
+ /// Explicit state machine:
7
+ ///
8
+ /// received operation O
9
+ /// ↓
10
+ /// check_dependencies(O)
11
+ /// ↓
12
+ /// dependencies_satisfied?
13
+ /// / \
14
+ /// yes no
15
+ /// ↓ ↓
16
+ /// READY WAITING_FOR_CAUSAL_DEPENDENCIES
17
+ /// ↓ ↓
18
+ /// APPLIED (buffered, waiting)
19
+ /// ↓
20
+ /// (when new op arrives that unblocks O)
21
+ /// ↓
22
+ /// re-check_dependencies(O)
23
+ /// ↓
24
+ /// READY
25
+ /// ↓
26
+ /// APPLIED
27
+
28
+ use crate::convergence::VectorClock;
29
+ use std::collections::HashMap;
30
+
31
+ /// Metrics for the causal dependency barrier (Phase 7.0.1 audit instrumentation)
32
+ #[derive(Clone, Default, Debug)]
33
+ pub struct BarrierMetrics {
34
+ /// Number of times frontier was checked for readiness
35
+ pub frontier_checks: u64,
36
+ /// Number of times pending buffer was scanned
37
+ pub pending_scans: u64,
38
+ /// Total entries examined during scans
39
+ pub pending_entries_examined: u64,
40
+ /// Operations released due to frontier advancement
41
+ pub operations_released: u64,
42
+ }
43
+
44
+ /// Phase 7.1-opt: Dependency index for selective candidate discovery
45
+ /// Maps (node, timestamp) → list of operation IDs waiting for that dependency
46
+ #[derive(Clone, Default, Debug)]
47
+ pub struct DependencyIndex {
48
+ /// For each (node, timestamp) in a required vector clock,
49
+ /// track which pending operations depend on it
50
+ pub waiters: HashMap<(String, u64), Vec<String>>,
51
+ }
52
+
53
+ impl DependencyIndex {
54
+ /// Get the number of index entries
55
+ pub fn entry_count(&self) -> usize {
56
+ self.waiters.len()
57
+ }
58
+ }
59
+
60
+ impl BarrierMetrics {
61
+ pub fn reset(&mut self) {
62
+ self.frontier_checks = 0;
63
+ self.pending_scans = 0;
64
+ self.pending_entries_examined = 0;
65
+ self.operations_released = 0;
66
+ }
67
+ }
68
+
69
+ /// State of an operation in the dependency barrier
70
+ #[derive(Clone, Debug, PartialEq, Eq)]
71
+ pub enum OperationState {
72
+ /// Operation has been received
73
+ Received,
74
+ /// Operation is waiting for causal dependencies to be satisfied
75
+ WaitingForDependencies {
76
+ required_clock: VectorClock,
77
+ missing_dependencies: Vec<String>, // human-readable list
78
+ },
79
+ /// All dependencies satisfied, ready to apply
80
+ Ready,
81
+ /// Operation has been applied
82
+ Applied,
83
+ }
84
+
85
+ /// Represents a pending operation waiting for dependencies
86
+ #[derive(Clone, Debug)]
87
+ pub struct PendingOperation {
88
+ pub operation_id: String,
89
+ pub required_dependencies: VectorClock,
90
+ pub state: OperationState,
91
+ }
92
+
93
+ impl PendingOperation {
94
+ pub fn new(operation_id: String, required_dependencies: VectorClock) -> Self {
95
+ Self {
96
+ operation_id,
97
+ required_dependencies,
98
+ state: OperationState::Received,
99
+ }
100
+ }
101
+ }
102
+
103
+ /// Causal dependency barrier manager
104
+ pub struct CausalDependencyBarrier {
105
+ /// Current replica's vector clock frontier
106
+ local_frontier: VectorClock,
107
+ /// Previous frontier (used to detect which dimensions advanced)
108
+ prev_frontier: VectorClock,
109
+ /// Operations waiting for dependencies
110
+ pending: HashMap<String, PendingOperation>,
111
+ /// Operations that have been applied
112
+ applied: Vec<String>,
113
+ /// Metrics for Phase 7.0.1 audit and benchmarking
114
+ metrics: BarrierMetrics,
115
+ /// Phase 7.1-opt: Dependency index for selective candidate discovery
116
+ dependency_index: DependencyIndex,
117
+ }
118
+
119
+ impl CausalDependencyBarrier {
120
+ pub fn new() -> Self {
121
+ Self {
122
+ local_frontier: VectorClock::new(),
123
+ prev_frontier: VectorClock::new(),
124
+ pending: HashMap::new(),
125
+ applied: Vec::new(),
126
+ metrics: BarrierMetrics::default(),
127
+ dependency_index: DependencyIndex::default(),
128
+ }
129
+ }
130
+
131
+ /// Get current metrics (for testing and benchmarking)
132
+ pub fn metrics(&self) -> BarrierMetrics {
133
+ self.metrics.clone()
134
+ }
135
+
136
+ /// Reset metrics (for testing and benchmarking)
137
+ pub fn clear_metrics(&mut self) {
138
+ self.metrics.reset();
139
+ }
140
+
141
+ /// Get current metrics snapshot (for testing and benchmarking)
142
+ pub fn get_metrics(&self) -> BarrierMetrics {
143
+ self.metrics.clone()
144
+ }
145
+
146
+ /// Get reference to dependency index (for testing and benchmarking)
147
+ pub fn get_dependency_index(&self) -> &DependencyIndex {
148
+ &self.dependency_index
149
+ }
150
+
151
+ /// Get previous frontier (for benchmarking frontier management)
152
+ pub fn get_prev_frontier_mut(&mut self) -> &mut VectorClock {
153
+ &mut self.prev_frontier
154
+ }
155
+
156
+ /// Get current frontier (for testing)
157
+ pub fn get_frontier_mut(&mut self) -> &mut VectorClock {
158
+ &mut self.local_frontier
159
+ }
160
+
161
+ /// Receive an operation and check if its dependencies are satisfied
162
+ pub fn receive_operation(
163
+ &mut self,
164
+ operation_id: String,
165
+ required_dependencies: VectorClock,
166
+ ) -> ReceiveResult {
167
+ // Track frontier check (Phase 7.0.1 instrumentation)
168
+ #[cfg(test)]
169
+ {
170
+ self.metrics.frontier_checks += 1;
171
+ }
172
+
173
+ // Check if dependencies are already satisfied
174
+ if self.local_frontier.happens_before(&required_dependencies) {
175
+ // Dependencies not yet satisfied, buffer the operation
176
+ let missing = self.compute_missing_dependencies(&required_dependencies);
177
+ let pending_op = PendingOperation {
178
+ operation_id: operation_id.clone(),
179
+ required_dependencies: required_dependencies.clone(),
180
+ state: OperationState::WaitingForDependencies {
181
+ required_clock: required_dependencies.clone(),
182
+ missing_dependencies: missing,
183
+ },
184
+ };
185
+ self.pending.insert(operation_id.clone(), pending_op);
186
+
187
+ // Phase 7.1-opt: Index this operation for later candidate discovery
188
+ #[cfg(test)]
189
+ self.index_operation(&operation_id, &required_dependencies);
190
+
191
+ ReceiveResult::BufferedWaitingForDependencies { operation_id }
192
+ } else {
193
+ // Dependencies are satisfied, operation can be applied immediately
194
+ ReceiveResult::ReadyToApply { operation_id }
195
+ }
196
+ }
197
+
198
+ /// Apply a ready operation and advance the frontier
199
+ pub fn apply_operation(
200
+ &mut self,
201
+ operation_id: &str,
202
+ operation_clock: &VectorClock,
203
+ ) -> ApplyResult {
204
+ // Remove from pending if present
205
+ if let Some(pending_op) = self.pending.remove(operation_id) {
206
+ // Phase 7.1-opt: Unindex when operation is removed
207
+ #[cfg(test)]
208
+ self.unindex_operation(operation_id, &pending_op.required_dependencies);
209
+ }
210
+
211
+ // Advance the frontier
212
+ #[cfg(test)]
213
+ {
214
+ self.prev_frontier.clone_from(&self.local_frontier);
215
+ }
216
+ self.local_frontier.merge(operation_clock);
217
+
218
+ // Record as applied
219
+ self.applied.push(operation_id.to_string());
220
+
221
+ // Check if this unblocks any waiting operations
222
+ let newly_ready = self.check_unblocked_operations();
223
+
224
+ ApplyResult {
225
+ operation_id: operation_id.to_string(),
226
+ newly_unblocked: newly_ready,
227
+ }
228
+ }
229
+
230
+ /// Get current state of an operation
231
+ pub fn get_operation_state(&self, operation_id: &str) -> Option<OperationState> {
232
+ if self.applied.contains(&operation_id.to_string()) {
233
+ Some(OperationState::Applied)
234
+ } else if let Some(pending) = self.pending.get(operation_id) {
235
+ Some(pending.state.clone())
236
+ } else {
237
+ None
238
+ }
239
+ }
240
+
241
+ /// Get all pending operations
242
+ pub fn get_pending_operations(&self) -> Vec<PendingOperation> {
243
+ self.pending.values().cloned().collect()
244
+ }
245
+
246
+ /// Get count of operations in each state
247
+ pub fn get_state_counts(&self) -> StateCounters {
248
+ let waiting_count = self.pending.len();
249
+ let applied_count = self.applied.len();
250
+
251
+ StateCounters {
252
+ received: 0,
253
+ waiting_for_dependencies: waiting_count,
254
+ ready: 0,
255
+ applied: applied_count,
256
+ }
257
+ }
258
+
259
+ /// Reference implementation: Check all pending operations (Phase 7.1 baseline)
260
+ /// Full-scan implementation: examines every pending operation
261
+ /// Used as correctness oracle for differential testing
262
+ pub fn check_unblocked_operations_full_scan(&mut self) -> Vec<String> {
263
+ // Track pending scan (Phase 7.0.1 instrumentation)
264
+ if !self.pending.is_empty() {
265
+ self.metrics.pending_scans += 1;
266
+ self.metrics.pending_entries_examined += self.pending.len() as u64;
267
+ }
268
+
269
+ let mut newly_ready = Vec::new();
270
+ let mut to_ready = Vec::new();
271
+
272
+ for (op_id, pending_op) in self.pending.iter() {
273
+ if !self.local_frontier.happens_before(&pending_op.required_dependencies) {
274
+ to_ready.push(op_id.clone());
275
+ newly_ready.push(op_id.clone());
276
+ }
277
+ }
278
+
279
+ // Track operations released (Phase 7.0.1 instrumentation)
280
+ self.metrics.operations_released += newly_ready.len() as u64;
281
+
282
+ for op_id in to_ready {
283
+ if let Some(pending) = self.pending.get_mut(&op_id) {
284
+ pending.state = OperationState::Ready;
285
+ }
286
+ }
287
+
288
+ newly_ready
289
+ }
290
+
291
+ /// Indexed implementation: selective candidate discovery via dependency index
292
+ /// Phase 7.1-opt: Only examines operations whose dependencies changed
293
+ /// Production release path (validated faster in all tested scenarios)
294
+ pub fn check_unblocked_operations_indexed(&mut self) -> Vec<String> {
295
+ let mut newly_ready = Vec::new();
296
+ let mut candidates = std::collections::HashSet::new();
297
+
298
+ // Find operations affected by frontier advancement
299
+ for (node, old_ts) in &self.prev_frontier.clocks {
300
+ let new_ts = self.local_frontier.get(node);
301
+ if new_ts > *old_ts {
302
+ // Frontier advanced for this node
303
+ // Operations waiting for timestamps in (old_ts, new_ts] are candidates
304
+ for ts in (*old_ts + 1)..=new_ts {
305
+ if let Some(waiters) = self.dependency_index.waiters.get(&(node.clone(), ts)) {
306
+ for op_id in waiters {
307
+ candidates.insert(op_id.clone());
308
+ }
309
+ }
310
+ }
311
+ }
312
+ }
313
+
314
+ // Also check for new nodes that appeared in frontier
315
+ for (node, new_ts) in &self.local_frontier.clocks {
316
+ if !self.prev_frontier.clocks.contains_key(node) {
317
+ // New node appeared, check all dependencies on it
318
+ for ts in 1..=*new_ts {
319
+ if let Some(waiters) = self.dependency_index.waiters.get(&(node.clone(), ts)) {
320
+ for op_id in waiters {
321
+ candidates.insert(op_id.clone());
322
+ }
323
+ }
324
+ }
325
+ }
326
+ }
327
+
328
+ // Track candidate count for instrumentation
329
+ let candidate_count = candidates.len();
330
+
331
+ // Test candidates using readiness theorem
332
+ let mut to_ready = Vec::new();
333
+ for op_id in candidates {
334
+ if let Some(pending_op) = self.pending.get(&op_id) {
335
+ if !self.local_frontier.happens_before(&pending_op.required_dependencies) {
336
+ to_ready.push(op_id.clone());
337
+ newly_ready.push(op_id);
338
+ }
339
+ }
340
+ }
341
+
342
+ // Track operations released (Phase 7.0.1 instrumentation)
343
+ self.metrics.operations_released += newly_ready.len() as u64;
344
+ if candidate_count > 0 {
345
+ self.metrics.pending_scans += 1;
346
+ self.metrics.pending_entries_examined += candidate_count as u64;
347
+ }
348
+
349
+ // Update state (keep in index for discovery purposes)
350
+ for op_id in to_ready {
351
+ if let Some(pending) = self.pending.get_mut(&op_id) {
352
+ pending.state = OperationState::Ready;
353
+ // Note: Don't unindex here - operation remains in index for potential re-discovery
354
+ // Unindex only happens when operation is actually removed from pending via apply_operation
355
+ }
356
+ }
357
+
358
+ newly_ready
359
+ }
360
+
361
+ /// Production dispatch: Check which pending operations have had their dependencies satisfied
362
+ /// Phase 7.1.3 crossover analysis shows indexed is faster in all tested scenarios
363
+ /// Routes to indexed implementation for selective candidate discovery
364
+ pub fn check_unblocked_operations(&mut self) -> Vec<String> {
365
+ self.check_unblocked_operations_indexed()
366
+ }
367
+
368
+ /// Compute which nodes/clocks are missing to satisfy dependencies
369
+ fn compute_missing_dependencies(&self, required: &VectorClock) -> Vec<String> {
370
+ let mut missing = Vec::new();
371
+
372
+ for (node, required_ts) in &required.clocks {
373
+ let current_ts = self.local_frontier.get(node);
374
+ if current_ts < *required_ts {
375
+ missing.push(format!(
376
+ "{}:{} (have {})",
377
+ node, required_ts, current_ts
378
+ ));
379
+ }
380
+ }
381
+
382
+ missing
383
+ }
384
+
385
+ /// Get the current frontier
386
+ pub fn get_frontier(&self) -> &VectorClock {
387
+ &self.local_frontier
388
+ }
389
+
390
+ /// Manually advance frontier (for testing or external coordination)
391
+ pub fn advance_frontier(&mut self, clock: &VectorClock) {
392
+ #[cfg(test)]
393
+ {
394
+ self.prev_frontier.clone_from(&self.local_frontier);
395
+ }
396
+ self.local_frontier.merge(clock);
397
+ self.check_unblocked_operations();
398
+ }
399
+
400
+ /// Phase 7.1-opt: Index an operation by its required dependencies
401
+ #[cfg(test)]
402
+ fn index_operation(&mut self, op_id: &str, required: &VectorClock) {
403
+ for (node, timestamp) in &required.clocks {
404
+ self.dependency_index.waiters
405
+ .entry((node.clone(), *timestamp))
406
+ .or_insert_with(Vec::new)
407
+ .push(op_id.to_string());
408
+ }
409
+ }
410
+
411
+ /// Phase 7.1-opt: Remove an operation from the dependency index
412
+ #[cfg(test)]
413
+ fn unindex_operation(&mut self, op_id: &str, required: &VectorClock) {
414
+ for (node, timestamp) in &required.clocks {
415
+ if let Some(waiters) = self.dependency_index.waiters.get_mut(&(node.clone(), *timestamp)) {
416
+ waiters.retain(|id| id != op_id);
417
+ }
418
+ }
419
+ }
420
+
421
+ /// Check if operation is ready based on frontier alone (Phase 7.0.1: readiness theorem)
422
+ /// Returns true if required_vc <= applied_frontier
423
+ pub fn is_ready_frontier(&self, required: &VectorClock) -> bool {
424
+ // Operation is ready if its required dependencies don't happen-before our frontier
425
+ !self.local_frontier.happens_before(required)
426
+ }
427
+
428
+ /// Check if operation is ready using current buffer-based logic (Phase 7.0.1: readiness theorem)
429
+ /// This is the reference implementation to compare against frontier check
430
+ pub fn is_ready_buffer(&self, required: &VectorClock) -> bool {
431
+ // Current implementation: same as is_ready_frontier
432
+ // This method exists to allow comparing future optimizations
433
+ !self.local_frontier.happens_before(required)
434
+ }
435
+ }
436
+
437
+ impl Default for CausalDependencyBarrier {
438
+ fn default() -> Self {
439
+ Self::new()
440
+ }
441
+ }
442
+
443
+ /// Result of receiving an operation
444
+ #[derive(Clone, Debug)]
445
+ pub enum ReceiveResult {
446
+ /// Operation is buffered, waiting for dependencies
447
+ BufferedWaitingForDependencies { operation_id: String },
448
+ /// Operation can be applied immediately
449
+ ReadyToApply { operation_id: String },
450
+ }
451
+
452
+ /// Result of applying an operation
453
+ #[derive(Clone, Debug)]
454
+ pub struct ApplyResult {
455
+ pub operation_id: String,
456
+ pub newly_unblocked: Vec<String>,
457
+ }
458
+
459
+ /// Counters for operation states
460
+ #[derive(Clone, Debug, PartialEq, Eq)]
461
+ pub struct StateCounters {
462
+ pub received: usize,
463
+ pub waiting_for_dependencies: usize,
464
+ pub ready: usize,
465
+ pub applied: usize,
466
+ }
467
+
468
+ #[cfg(test)]
469
+ mod tests {
470
+ use super::*;
471
+
472
+ #[test]
473
+ fn test_barrier_creation() {
474
+ let barrier = CausalDependencyBarrier::new();
475
+ assert_eq!(barrier.get_pending_operations().len(), 0);
476
+ }
477
+
478
+ /// Phase 7.0.2: Verify readiness theorem and buffer independence
479
+ /// This test confirms that receive_operation on a satisfied dependency
480
+ /// produces consistent latency regardless of pending buffer size.
481
+ #[test]
482
+ fn phase_7_0_2_readiness_independent_of_pending_buffer() {
483
+ // Theorem: ready(op) ⟺ required_vc <= applied_frontier
484
+ // Should be true regardless of buffer contents
485
+
486
+ let required = {
487
+ let mut vc = VectorClock::new();
488
+ vc.increment("node_a");
489
+ vc.increment("node_b");
490
+ vc
491
+ };
492
+
493
+ // Test with different buffer sizes
494
+ for buffer_size in [0, 10, 100] {
495
+ let mut barrier = CausalDependencyBarrier::new();
496
+ barrier.clear_metrics();
497
+
498
+ // Advance frontier to satisfy requirement
499
+ barrier.advance_frontier(&required);
500
+ barrier.clear_metrics(); // Reset metrics after setup
501
+
502
+ // Pre-fill buffer with unrelated operations
503
+ for i in 0..buffer_size {
504
+ let mut unrelated = VectorClock::new();
505
+ unrelated.increment(&format!("other_{}", i));
506
+ let _ = barrier.receive_operation(format!("op_buffer_{}", i), unrelated);
507
+ }
508
+ barrier.clear_metrics(); // Reset metrics after filling
509
+
510
+ // Critical test: receive satisfied operation
511
+ // Expected: Should be ready, metrics should show frontier check not buffer scan
512
+ let result = barrier.receive_operation(
513
+ "op_test".to_string(),
514
+ required.clone(),
515
+ );
516
+
517
+ let metrics = barrier.metrics();
518
+
519
+ // Check result
520
+ match result {
521
+ ReceiveResult::ReadyToApply { .. } => {
522
+ // Correct: dependencies satisfied
523
+ assert!(
524
+ true,
525
+ "Operation should be ReadyToApply when frontier satisfies"
526
+ );
527
+ }
528
+ ReceiveResult::BufferedWaitingForDependencies { .. } => {
529
+ panic!(
530
+ "Operation should be ready! Frontier was advanced with requirement: {:?}",
531
+ required
532
+ );
533
+ }
534
+ }
535
+
536
+ // Check instrumentation
537
+ // Should have 1 frontier check, 0 pending scans (on receive path)
538
+ println!("Buffer size: {}", buffer_size);
539
+ println!(
540
+ " frontier_checks: {} (expected 1)",
541
+ metrics.frontier_checks
542
+ );
543
+ println!(
544
+ " pending_scans: {} (expected 0 on receive path)",
545
+ metrics.pending_scans
546
+ );
547
+
548
+ // The key invariant: pending_scans should be 0 on receive_operation
549
+ // If this fails, it means receive_operation is scanning the buffer
550
+ assert_eq!(
551
+ metrics.pending_scans, 0,
552
+ "receive_operation should not scan pending buffer; \
553
+ buffer-size-dependent cost would indicate buffer scanning on readiness path"
554
+ );
555
+ }
556
+ }
557
+
558
+ #[test]
559
+ fn test_operation_with_no_dependencies() {
560
+ let mut barrier = CausalDependencyBarrier::new();
561
+ let op_clock = VectorClock::new();
562
+
563
+ let result = barrier.receive_operation("op1".to_string(), op_clock);
564
+ assert!(matches!(result, ReceiveResult::ReadyToApply { .. }));
565
+ }
566
+
567
+ #[test]
568
+ fn test_operation_buffered_when_dependencies_missing() {
569
+ let mut barrier = CausalDependencyBarrier::new();
570
+
571
+ let mut required = VectorClock::new();
572
+ required.increment("node_a");
573
+ required.increment("node_a"); // {node_a: 2}
574
+
575
+ let result = barrier.receive_operation("op1".to_string(), required);
576
+ assert!(matches!(result, ReceiveResult::BufferedWaitingForDependencies { .. }));
577
+ assert_eq!(barrier.get_pending_operations().len(), 1);
578
+ }
579
+
580
+ #[test]
581
+ fn test_unblocking_when_dependency_satisfied() {
582
+ let mut barrier = CausalDependencyBarrier::new();
583
+
584
+ // Operation requires {node_a: 2}
585
+ let mut required = VectorClock::new();
586
+ required.increment("node_a");
587
+ required.increment("node_a"); // {node_a: 2}
588
+
589
+ let result = barrier.receive_operation("op1".to_string(), required);
590
+ assert!(matches!(result, ReceiveResult::BufferedWaitingForDependencies { .. }));
591
+ assert_eq!(barrier.get_pending_operations().len(), 1);
592
+
593
+ // Advance frontier to satisfy dependency
594
+ let mut satisfying_clock = VectorClock::new();
595
+ satisfying_clock.increment("node_a");
596
+ satisfying_clock.increment("node_a");
597
+
598
+ barrier.advance_frontier(&satisfying_clock);
599
+
600
+ // Now op1 should be ready
601
+ let pending = barrier.get_pending_operations();
602
+ assert_eq!(pending.len(), 1);
603
+ assert_eq!(pending[0].state, OperationState::Ready);
604
+ }
605
+
606
+ #[test]
607
+ fn test_out_of_order_arrival_buffering() {
608
+ let mut barrier = CausalDependencyBarrier::new();
609
+
610
+ // Op2 depends on Op1 having been applied
611
+ // Op1 has no dependencies
612
+ let op1_required = VectorClock::new(); // Op1 requires nothing
613
+
614
+ let mut op2_required = VectorClock::new();
615
+ op2_required.increment("node_a"); // Op2 requires {node_a: 1}
616
+
617
+ // Receive Op2 before Op1
618
+ let result = barrier.receive_operation("op2".to_string(), op2_required.clone());
619
+ assert!(matches!(result, ReceiveResult::BufferedWaitingForDependencies { .. }));
620
+
621
+ // Receive Op1 (which has no dependencies)
622
+ let result = barrier.receive_operation("op1".to_string(), op1_required.clone());
623
+ assert!(matches!(result, ReceiveResult::ReadyToApply { .. }));
624
+
625
+ // Apply Op1, which contributes {node_a: 1} to the frontier
626
+ let mut op1_contribution = VectorClock::new();
627
+ op1_contribution.increment("node_a");
628
+ barrier.apply_operation("op1", &op1_contribution);
629
+
630
+ // Now Op2 should be unblocked because frontier is {node_a: 1}
631
+ let pending = barrier.get_pending_operations();
632
+ assert_eq!(pending.len(), 1);
633
+ assert_eq!(pending[0].operation_id, "op2");
634
+ assert_eq!(pending[0].state, OperationState::Ready);
635
+ }
636
+
637
+ #[test]
638
+ fn test_dependency_cascade() {
639
+ let mut barrier = CausalDependencyBarrier::new();
640
+
641
+ // Op1 has no dependencies
642
+ let op1_clock = VectorClock::new();
643
+
644
+ // Op2 depends on Op1: {node_a: 1}
645
+ let mut op2_required = VectorClock::new();
646
+ op2_required.increment("node_a");
647
+
648
+ // Op3 depends on Op2: {node_a: 2}
649
+ let mut op3_required = VectorClock::new();
650
+ op3_required.increment("node_a");
651
+ op3_required.increment("node_a");
652
+
653
+ // Receive in order: Op3, Op2, Op1
654
+ barrier.receive_operation("op3".to_string(), op3_required);
655
+ barrier.receive_operation("op2".to_string(), op2_required);
656
+ barrier.receive_operation("op1".to_string(), op1_clock.clone());
657
+
658
+ // All should be buffered except Op1 (which can apply immediately)
659
+ assert_eq!(barrier.get_pending_operations().len(), 2); // Op2 and Op3
660
+
661
+ // Apply Op1
662
+ let mut apply_clock = VectorClock::new();
663
+ apply_clock.increment("node_a");
664
+ barrier.apply_operation("op1", &apply_clock);
665
+
666
+ // Now Op2 should be ready
667
+ let pending = barrier.get_pending_operations();
668
+ let op2_state = pending.iter().find(|p| p.operation_id == "op2").unwrap();
669
+ assert_eq!(op2_state.state, OperationState::Ready);
670
+
671
+ // Apply Op2
672
+ let mut apply_clock2 = VectorClock::new();
673
+ apply_clock2.increment("node_a");
674
+ apply_clock2.increment("node_a");
675
+ barrier.apply_operation("op2", &apply_clock2);
676
+
677
+ // Now Op3 should be ready
678
+ let pending = barrier.get_pending_operations();
679
+ let op3_state = pending.iter().find(|p| p.operation_id == "op3").unwrap();
680
+ assert_eq!(op3_state.state, OperationState::Ready);
681
+ }
682
+
683
+ #[test]
684
+ fn test_state_counts() {
685
+ let mut barrier = CausalDependencyBarrier::new();
686
+
687
+ let mut required = VectorClock::new();
688
+ required.increment("node_a");
689
+
690
+ barrier.receive_operation("op1".to_string(), required.clone());
691
+ barrier.receive_operation("op2".to_string(), required);
692
+
693
+ let counts = barrier.get_state_counts();
694
+ assert_eq!(counts.waiting_for_dependencies, 2);
695
+ assert_eq!(counts.applied, 0);
696
+ }
697
+
698
+ #[test]
699
+ fn test_causal_invariant_preserved() {
700
+ // Verify that operations are applied in causal order
701
+ let mut barrier = CausalDependencyBarrier::new();
702
+
703
+ // Op1 has no dependencies
704
+ let op1_required = VectorClock::new();
705
+
706
+ // Op2 requires {a: 2}
707
+ let mut op2_required = VectorClock::new();
708
+ op2_required.increment("a");
709
+ op2_required.increment("a");
710
+
711
+ // Receive Op2 before Op1 (out of order)
712
+ barrier.receive_operation("op2".to_string(), op2_required.clone());
713
+
714
+ // Op1 should be ready immediately (no dependencies)
715
+ let result = barrier.receive_operation("op1".to_string(), op1_required);
716
+ assert!(matches!(result, ReceiveResult::ReadyToApply { .. }));
717
+
718
+ // After applying Op1, it contributes {a: 1} to frontier
719
+ let mut op1_contribution = VectorClock::new();
720
+ op1_contribution.increment("a");
721
+ barrier.apply_operation("op1", &op1_contribution);
722
+
723
+ // Op2 still waiting (needs a:2, but frontier is only a:1)
724
+ let pending = barrier.get_pending_operations();
725
+ let op2_state = pending.iter().find(|p| p.operation_id == "op2").unwrap();
726
+ assert!(matches!(
727
+ op2_state.state,
728
+ OperationState::WaitingForDependencies { .. }
729
+ ));
730
+ }
731
+
732
+ #[test]
733
+ fn test_readiness_theorem_equivalence() {
734
+ // Phase 7.0.1: Prove that frontier-based and buffer-based readiness checks are equivalent
735
+ let mut barrier = CausalDependencyBarrier::new();
736
+
737
+ // Create a required vector clock
738
+ let mut required = VectorClock::new();
739
+ required.increment("node_a");
740
+ required.increment("node_b");
741
+
742
+ // Before frontier is advanced, operation should NOT be ready
743
+ assert!(!barrier.is_ready_frontier(&required));
744
+ assert!(!barrier.is_ready_buffer(&required));
745
+
746
+ // Advance frontier to satisfy dependencies
747
+ let mut satisfying = VectorClock::new();
748
+ satisfying.increment("node_a");
749
+ satisfying.increment("node_b");
750
+ barrier.advance_frontier(&satisfying);
751
+
752
+ // Now both methods should agree: operation IS ready
753
+ assert!(barrier.is_ready_frontier(&required));
754
+ assert!(barrier.is_ready_buffer(&required));
755
+ }
756
+
757
+ #[test]
758
+ fn test_instrumentation_frontier_checks() {
759
+ // Phase 7.0.1: Verify frontier_checks counter is incremented
760
+ let mut barrier = CausalDependencyBarrier::new();
761
+ assert_eq!(barrier.metrics().frontier_checks, 0);
762
+
763
+ let required = VectorClock::new();
764
+ barrier.receive_operation("op1".to_string(), required.clone());
765
+ assert_eq!(barrier.metrics().frontier_checks, 1);
766
+
767
+ barrier.receive_operation("op2".to_string(), required);
768
+ assert_eq!(barrier.metrics().frontier_checks, 2);
769
+ }
770
+
771
+ #[test]
772
+ fn test_instrumentation_pending_scans() {
773
+ // Phase 7.0.1: Verify pending_scans counter when checking unblocked operations
774
+ let mut barrier = CausalDependencyBarrier::new();
775
+ barrier.clear_metrics();
776
+
777
+ // Add buffered operation
778
+ let mut required = VectorClock::new();
779
+ required.increment("node_a");
780
+ barrier.receive_operation("op_buffered".to_string(), required);
781
+
782
+ assert_eq!(barrier.metrics().pending_scans, 0);
783
+ assert_eq!(barrier.metrics().pending_entries_examined, 0);
784
+
785
+ // Advance frontier to trigger check_unblocked_operations
786
+ let mut satisfying = VectorClock::new();
787
+ satisfying.increment("node_a");
788
+ barrier.advance_frontier(&satisfying);
789
+
790
+ // Should have scanned pending buffer
791
+ assert_eq!(barrier.metrics().pending_scans, 1);
792
+ assert_eq!(barrier.metrics().pending_entries_examined, 1);
793
+ }
794
+
795
+ #[test]
796
+ fn test_instrumentation_operations_released() {
797
+ // Phase 7.0.1: Verify operations_released counter
798
+ let mut barrier = CausalDependencyBarrier::new();
799
+ barrier.clear_metrics();
800
+
801
+ // Add multiple buffered operations
802
+ let mut required = VectorClock::new();
803
+ required.increment("node_a");
804
+
805
+ for i in 0..3 {
806
+ barrier.receive_operation(format!("op_{}", i), required.clone());
807
+ }
808
+
809
+ assert_eq!(barrier.metrics().operations_released, 0);
810
+
811
+ // Advance frontier to release operations
812
+ let mut satisfying = VectorClock::new();
813
+ satisfying.increment("node_a");
814
+ barrier.advance_frontier(&satisfying);
815
+
816
+ // All 3 should be released
817
+ assert_eq!(barrier.metrics().operations_released, 3);
818
+ }
819
+
820
+ #[test]
821
+ fn test_instrumentation_buffer_scanning_scales_with_size() {
822
+ // Phase 7.0.1: Demonstrate that buffer scanning scales with buffer size
823
+ // This test uses full-scan oracle to validate full-scan instrumentation behavior
824
+ // Production dispatch uses indexed path; this tests the oracle implementation
825
+ let mut barrier = CausalDependencyBarrier::new();
826
+ barrier.clear_metrics();
827
+
828
+ // Create a requirement for {node_a: 5}
829
+ let mut required = VectorClock::new();
830
+ required.increment("node_a");
831
+ required.increment("node_a");
832
+ required.increment("node_a");
833
+ required.increment("node_a");
834
+ required.increment("node_a");
835
+
836
+ // Add 10 operations that can't be satisfied yet
837
+ for i in 0..10 {
838
+ barrier.receive_operation(format!("op_{}", i), required.clone());
839
+ }
840
+
841
+ barrier.clear_metrics();
842
+
843
+ // Trigger full-scan directly: should see all 10 items
844
+ let mut partial = VectorClock::new();
845
+ partial.increment("node_a");
846
+ partial.increment("node_a"); // Only {node_a: 2}, not enough for requirement
847
+ {
848
+ let current = barrier.get_frontier().clone();
849
+ barrier.get_prev_frontier_mut().clone_from(&current);
850
+ }
851
+ barrier.get_frontier_mut().merge(&partial);
852
+ let _ = barrier.check_unblocked_operations_full_scan();
853
+
854
+ let first_scan_entries = barrier.metrics().pending_entries_examined;
855
+ assert_eq!(first_scan_entries, 10);
856
+
857
+ barrier.clear_metrics();
858
+
859
+ // Add 5 more operations
860
+ for i in 10..15 {
861
+ barrier.receive_operation(format!("op_{}", i), required.clone());
862
+ }
863
+
864
+ // Now there should be 15 items in pending
865
+ let pending_count = barrier.get_pending_operations().len();
866
+ assert_eq!(pending_count, 15);
867
+
868
+ // Trigger full-scan again: should see all 15 items
869
+ let mut more_partial = VectorClock::new();
870
+ more_partial.increment("node_a");
871
+ more_partial.increment("node_a");
872
+ more_partial.increment("node_a"); // Only {node_a: 3}, still not enough
873
+ {
874
+ let current = barrier.get_frontier().clone();
875
+ barrier.get_prev_frontier_mut().clone_from(&current);
876
+ }
877
+ barrier.get_frontier_mut().merge(&more_partial);
878
+ let _ = barrier.check_unblocked_operations_full_scan();
879
+
880
+ let second_scan_entries = barrier.metrics().pending_entries_examined;
881
+ assert_eq!(second_scan_entries, 15);
882
+
883
+ // Verify that entries_examined scales with buffer size
884
+ assert!(second_scan_entries > first_scan_entries);
885
+ }
886
+
887
+ // ============================================================================
888
+ // LIFECYCLE INVARIANT TESTS (Phase 7.0.1: Prove theorem over actual states)
889
+ // ============================================================================
890
+
891
+ #[test]
892
+ fn lifecycle_invariant_out_of_order_operations() {
893
+ // Phase 7.0.1: Proof that frontier theorem holds with out-of-order arrivals
894
+ // Sequence: A1 → A3 → A2
895
+ // Expected: A1 applies, A3 buffers, then when A2 applies, A3 can be checked
896
+ let mut barrier = CausalDependencyBarrier::new();
897
+
898
+ // A1: no dependencies (ready immediately)
899
+ let a1_required = VectorClock::new();
900
+ let result_a1 = barrier.receive_operation("A1".to_string(), a1_required.clone());
901
+ assert!(matches!(result_a1, ReceiveResult::ReadyToApply { .. }));
902
+
903
+ // Apply A1 with {node_a: 1}
904
+ let mut a1_contribution = VectorClock::new();
905
+ a1_contribution.increment("node_a");
906
+ barrier.apply_operation("A1", &a1_contribution);
907
+ assert_eq!(barrier.get_frontier().get("node_a"), 1);
908
+
909
+ // A3: requires {node_a: 3}
910
+ let mut a3_required = VectorClock::new();
911
+ a3_required.increment("node_a");
912
+ a3_required.increment("node_a");
913
+ a3_required.increment("node_a");
914
+ let result_a3 = barrier.receive_operation("A3".to_string(), a3_required.clone());
915
+ assert!(matches!(result_a3, ReceiveResult::BufferedWaitingForDependencies { .. }));
916
+
917
+ // A2: requires {node_a: 2}
918
+ let mut a2_required = VectorClock::new();
919
+ a2_required.increment("node_a");
920
+ a2_required.increment("node_a");
921
+ let result_a2 = barrier.receive_operation("A2".to_string(), a2_required.clone());
922
+ assert!(matches!(result_a2, ReceiveResult::BufferedWaitingForDependencies { .. }));
923
+
924
+ // Now frontier is only {node_a: 1}, both A2 and A3 are buffered
925
+ assert_eq!(barrier.get_pending_operations().len(), 2);
926
+
927
+ // Apply A2 with {node_a: 2}
928
+ let mut a2_contribution = VectorClock::new();
929
+ a2_contribution.increment("node_a");
930
+ a2_contribution.increment("node_a");
931
+ barrier.apply_operation("A2", &a2_contribution);
932
+
933
+ // Now frontier is {node_a: 2}, A2 should be applied, A3 still waiting
934
+ assert_eq!(barrier.get_frontier().get("node_a"), 2);
935
+ let pending = barrier.get_pending_operations();
936
+ assert_eq!(pending.len(), 1);
937
+ assert_eq!(pending[0].operation_id, "A3");
938
+ assert!(matches!(pending[0].state, OperationState::WaitingForDependencies { .. }));
939
+
940
+ // Apply A3 with {node_a: 3}
941
+ let mut a3_contribution = VectorClock::new();
942
+ a3_contribution.increment("node_a");
943
+ a3_contribution.increment("node_a");
944
+ a3_contribution.increment("node_a");
945
+ barrier.apply_operation("A3", &a3_contribution);
946
+
947
+ // Now all should be applied
948
+ assert_eq!(barrier.get_frontier().get("node_a"), 3);
949
+ assert_eq!(barrier.get_pending_operations().len(), 0);
950
+ }
951
+
952
+ #[test]
953
+ fn lifecycle_invariant_multiple_dependencies() {
954
+ // Phase 7.0.1: Proof with multiple independent dependency chains
955
+ // A5 depends on: B3, C7, D2
956
+ let mut barrier = CausalDependencyBarrier::new();
957
+
958
+ // Build the requirement: {B: 3, C: 7, D: 2}
959
+ let mut a5_required = VectorClock::new();
960
+ a5_required.increment("B");
961
+ a5_required.increment("B");
962
+ a5_required.increment("B"); // {B: 3}
963
+ for _ in 0..7 {
964
+ a5_required.increment("C");
965
+ } // {C: 7}
966
+ a5_required.increment("D");
967
+ a5_required.increment("D"); // {D: 2}
968
+
969
+ // Try to receive A5 before any dependencies
970
+ let result = barrier.receive_operation("A5".to_string(), a5_required.clone());
971
+ assert!(matches!(result, ReceiveResult::BufferedWaitingForDependencies { .. }));
972
+
973
+ // Partially satisfy: apply B3
974
+ let mut b3_clock = VectorClock::new();
975
+ b3_clock.increment("B");
976
+ b3_clock.increment("B");
977
+ b3_clock.increment("B");
978
+ barrier.receive_operation("B3".to_string(), b3_clock.clone());
979
+ barrier.apply_operation("B3", &b3_clock);
980
+ assert_eq!(barrier.get_frontier().get("B"), 3);
981
+
982
+ // A5 still waiting (needs C7 and D2)
983
+ assert!(matches!(
984
+ barrier.get_operation_state("A5"),
985
+ Some(OperationState::WaitingForDependencies { .. })
986
+ ));
987
+
988
+ // Satisfy C7
989
+ let mut c7_clock = VectorClock::new();
990
+ for _ in 0..7 {
991
+ c7_clock.increment("C");
992
+ }
993
+ barrier.receive_operation("C7".to_string(), c7_clock.clone());
994
+ barrier.apply_operation("C7", &c7_clock);
995
+ assert_eq!(barrier.get_frontier().get("C"), 7);
996
+
997
+ // A5 still waiting (needs D2)
998
+ assert!(matches!(
999
+ barrier.get_operation_state("A5"),
1000
+ Some(OperationState::WaitingForDependencies { .. })
1001
+ ));
1002
+
1003
+ // Satisfy D2
1004
+ let mut d2_clock = VectorClock::new();
1005
+ d2_clock.increment("D");
1006
+ d2_clock.increment("D");
1007
+ barrier.receive_operation("D2".to_string(), d2_clock.clone());
1008
+ barrier.apply_operation("D2", &d2_clock);
1009
+ assert_eq!(barrier.get_frontier().get("D"), 2);
1010
+
1011
+ // Now A5 should be ready (frontier has all dependencies)
1012
+ let pending = barrier.get_pending_operations();
1013
+ let a5_state = pending.iter().find(|p| p.operation_id == "A5");
1014
+ assert!(a5_state.is_some());
1015
+ assert!(matches!(a5_state.unwrap().state, OperationState::Ready));
1016
+ }
1017
+
1018
+ #[test]
1019
+ fn lifecycle_invariant_buffered_but_unapplied() {
1020
+ // Phase 7.0.1: Critical case - operation received but not yet applied
1021
+ // This tests the distinction between observed_frontier and applied_frontier
1022
+ // Scenario: B1 is applied first (frontier = {B: 1}),
1023
+ // then we receive B3 (but don't apply it),
1024
+ // then A5 that requires {B: 3}
1025
+ let mut barrier = CausalDependencyBarrier::new();
1026
+
1027
+ // First apply B1 to establish frontier
1028
+ let mut b1_clock = VectorClock::new();
1029
+ b1_clock.increment("B");
1030
+ barrier.receive_operation("B1".to_string(), b1_clock.clone());
1031
+ barrier.apply_operation("B1", &b1_clock);
1032
+ assert_eq!(barrier.get_frontier().get("B"), 1);
1033
+
1034
+ // Receive B3 (requires {B: 3} but frontier only has {B: 1}, so it gets buffered)
1035
+ let mut b3_required = VectorClock::new();
1036
+ b3_required.increment("B");
1037
+ b3_required.increment("B");
1038
+ b3_required.increment("B");
1039
+ let result_b3 = barrier.receive_operation("B3".to_string(), b3_required.clone());
1040
+ assert!(matches!(result_b3, ReceiveResult::BufferedWaitingForDependencies { .. }));
1041
+
1042
+ // At this point:
1043
+ // - applied_frontier[B] = 1 (only B1 applied)
1044
+ // - We have B3 in the buffer (received but not applied)
1045
+ // - A5 requires {B: 3}
1046
+
1047
+ // A5 should be WAITING because applied_frontier < required
1048
+ let mut a5_required = VectorClock::new();
1049
+ a5_required.increment("B");
1050
+ a5_required.increment("B");
1051
+ a5_required.increment("B");
1052
+ let result_a5 = barrier.receive_operation("A5".to_string(), a5_required.clone());
1053
+ assert!(matches!(result_a5, ReceiveResult::BufferedWaitingForDependencies { .. }));
1054
+
1055
+ // NOW apply B3
1056
+ barrier.apply_operation("B3", &b3_required);
1057
+ assert_eq!(barrier.get_frontier().get("B"), 3);
1058
+
1059
+ // THEN A5 can be ready
1060
+ let pending = barrier.get_pending_operations();
1061
+ let a5_state = pending.iter().find(|p| p.operation_id == "A5");
1062
+ assert!(a5_state.is_some());
1063
+ assert!(matches!(a5_state.unwrap().state, OperationState::Ready));
1064
+ }
1065
+
1066
+ #[test]
1067
+ fn lifecycle_invariant_multiple_buffered_become_ready() {
1068
+ // Phase 7.0.1: Release mechanism test
1069
+ // Multiple operations in buffer all become ready when frontier advances
1070
+ let mut barrier = CausalDependencyBarrier::new();
1071
+
1072
+ // Buffer 5 operations, all requiring {node_a: 3}
1073
+ let mut required = VectorClock::new();
1074
+ required.increment("node_a");
1075
+ required.increment("node_a");
1076
+ required.increment("node_a");
1077
+
1078
+ for i in 0..5 {
1079
+ let result = barrier.receive_operation(format!("op_{}", i), required.clone());
1080
+ assert!(matches!(result, ReceiveResult::BufferedWaitingForDependencies { .. }));
1081
+ }
1082
+
1083
+ assert_eq!(barrier.get_pending_operations().len(), 5);
1084
+
1085
+ // Clear metrics and check that no operations are marked Ready yet
1086
+ barrier.clear_metrics();
1087
+ for i in 0..5 {
1088
+ let state = barrier.get_operation_state(&format!("op_{}", i));
1089
+ assert!(matches!(
1090
+ state,
1091
+ Some(OperationState::WaitingForDependencies { .. })
1092
+ ));
1093
+ }
1094
+
1095
+ // Advance frontier to {node_a: 3}
1096
+ let mut satisfying = VectorClock::new();
1097
+ satisfying.increment("node_a");
1098
+ satisfying.increment("node_a");
1099
+ satisfying.increment("node_a");
1100
+ barrier.advance_frontier(&satisfying);
1101
+
1102
+ // Check that:
1103
+ // 1. All 5 operations were examined in the scan
1104
+ // 2. All 5 operations were released
1105
+ assert_eq!(barrier.metrics().pending_entries_examined, 5);
1106
+ assert_eq!(barrier.metrics().operations_released, 5);
1107
+
1108
+ // 3. All 5 operations now marked Ready
1109
+ for i in 0..5 {
1110
+ let state = barrier.get_operation_state(&format!("op_{}", i));
1111
+ assert!(matches!(state, Some(OperationState::Ready)));
1112
+ }
1113
+ }
1114
+
1115
+ #[test]
1116
+ fn phase_7_0_2_adversarial_satisfied_with_arbitrary_pending() {
1117
+ // Phase 7.0.2: Readiness Theorem Adversarial Test
1118
+ // Hypothesis: ready(op) ⟺ required_vc(op) <= applied_frontier
1119
+ // This should be true REGARDLESS of what's in the pending buffer
1120
+ //
1121
+ // Test: Apply frontier -> fill pending -> check clearly satisfied op
1122
+ let mut barrier = CausalDependencyBarrier::new();
1123
+
1124
+ // 1. Set applied frontier to {A: 10, B: 10, C: 10}
1125
+ let mut frontier = VectorClock::new();
1126
+ frontier.increment("node_a");
1127
+ for _ in 0..9 {
1128
+ frontier.increment("node_a");
1129
+ }
1130
+ frontier.increment("node_b");
1131
+ for _ in 0..9 {
1132
+ frontier.increment("node_b");
1133
+ }
1134
+ frontier.increment("node_c");
1135
+ for _ in 0..9 {
1136
+ frontier.increment("node_c");
1137
+ }
1138
+ barrier.advance_frontier(&frontier);
1139
+
1140
+ // 2. Fill pending with high-timestamp operations
1141
+ // These are NOT applied yet, just buffered
1142
+ for i in 0..1000 {
1143
+ let mut high_ts = VectorClock::new();
1144
+ for j in 0..10 {
1145
+ high_ts.increment(&format!("node_a"));
1146
+ }
1147
+ high_ts.increment(&format!("pending_{}", i));
1148
+ let _ = barrier.receive_operation(format!("pending_{}", i), high_ts);
1149
+ }
1150
+
1151
+ // 3. Check a CLEARLY satisfied operation
1152
+ // Required: {A: 5, B: 7, C: 3} which is clearly less than frontier {A:10, B:10, C:10}
1153
+ let mut required = VectorClock::new();
1154
+ for _ in 0..5 {
1155
+ required.increment("node_a");
1156
+ }
1157
+ for _ in 0..7 {
1158
+ required.increment("node_b");
1159
+ }
1160
+ for _ in 0..3 {
1161
+ required.increment("node_c");
1162
+ }
1163
+
1164
+ // Expected: READY, because frontier >= required
1165
+ let result = barrier.receive_operation("clearly_satisfied".to_string(), required);
1166
+
1167
+ // Verify it's ready
1168
+ assert!(
1169
+ matches!(result, ReceiveResult::ReadyToApply { .. }),
1170
+ "Operation with satisfied frontier should be ready even with 1000 pending"
1171
+ );
1172
+
1173
+ // Verify no buffer scan happened
1174
+ let metrics = barrier.metrics();
1175
+ assert_eq!(
1176
+ metrics.pending_scans, 0,
1177
+ "readiness check should not scan pending buffer; frontier alone is sufficient"
1178
+ );
1179
+ }
1180
+
1181
+ #[test]
1182
+ fn phase_7_0_2_adversarial_pending_not_applied() {
1183
+ // Phase 7.0.2: Critical Distinction Test
1184
+ // Ensure we distinguish between:
1185
+ // - observed_frontier (max timestamp we've SEEN)
1186
+ // - applied_frontier (max timestamp we've APPLIED)
1187
+ //
1188
+ // Test: Buffer a high-timestamp operation, then check a requirement
1189
+ // that depends on that timestamp. Should NOT be ready until it's actually applied.
1190
+ let mut barrier = CausalDependencyBarrier::new();
1191
+
1192
+ // 1. Start with applied frontier at {A: 5}
1193
+ let mut frontier = VectorClock::new();
1194
+ for _ in 0..5 {
1195
+ frontier.increment("node_a");
1196
+ }
1197
+ barrier.advance_frontier(&frontier);
1198
+
1199
+ // 2. Receive operation A:10 (not yet applied)
1200
+ // This increments observed_frontier but not applied_frontier
1201
+ let mut high_ts = VectorClock::new();
1202
+ for _ in 0..10 {
1203
+ high_ts.increment("node_a");
1204
+ }
1205
+ let result = barrier.receive_operation("unmet_op".to_string(), high_ts.clone());
1206
+
1207
+ // Should be buffered (A:10 not yet applied)
1208
+ assert!(
1209
+ matches!(result, ReceiveResult::BufferedWaitingForDependencies { .. }),
1210
+ "Operation A:10 should be buffered since frontier is only at A:5"
1211
+ );
1212
+
1213
+ // 3. Now try to apply a NEW operation that requires A:10
1214
+ // This new operation's dependencies are NOT satisfied
1215
+ let result2 = barrier.receive_operation("test_op".to_string(), high_ts.clone());
1216
+
1217
+ assert!(
1218
+ matches!(result2, ReceiveResult::BufferedWaitingForDependencies { .. }),
1219
+ "Operation requiring A:10 should NOT be ready even though A:10 exists in pending buffer; \
1220
+ applied_frontier is still at A:5"
1221
+ );
1222
+
1223
+ // Verify instrumentation shows the semantic logic is working
1224
+ let metrics = barrier.metrics();
1225
+ println!(
1226
+ "Metrics: frontier_checks={}, pending_scans={}",
1227
+ metrics.frontier_checks, metrics.pending_scans
1228
+ );
1229
+
1230
+ // Should have done 2 frontier checks (for the two receive_operation calls)
1231
+ // Should NOT have done any scans (readiness is frontier-only)
1232
+ assert!(
1233
+ metrics.frontier_checks >= 2,
1234
+ "Should have checked frontier at least twice"
1235
+ );
1236
+ assert_eq!(
1237
+ metrics.pending_scans, 0,
1238
+ "Should not need buffer scans for readiness - frontier is sufficient"
1239
+ );
1240
+ }
1241
+
1242
+ // ============================================================================
1243
+ // PHASE 7.1-OPT: DIFFERENTIAL TESTING (Index vs Reference)
1244
+ // ============================================================================
1245
+
1246
+ #[test]
1247
+ fn phase_7_1_opt_index_correctness_candidate_discovery() {
1248
+ // Test that index correctly identifies which operations are affected by frontier advancement
1249
+ let mut barrier = CausalDependencyBarrier::new();
1250
+
1251
+ // Create operations with clear dependency patterns on same node
1252
+ let mut req_a1 = VectorClock::new();
1253
+ req_a1.clocks.insert("node_a".to_string(), 1);
1254
+ barrier.receive_operation("op_a1".to_string(), req_a1);
1255
+
1256
+ let mut req_a2 = VectorClock::new();
1257
+ req_a2.clocks.insert("node_a".to_string(), 2);
1258
+ barrier.receive_operation("op_a2".to_string(), req_a2);
1259
+
1260
+ let mut req_a3 = VectorClock::new();
1261
+ req_a3.clocks.insert("node_a".to_string(), 3);
1262
+ barrier.receive_operation("op_a3".to_string(), req_a3);
1263
+
1264
+ // Verify index is correct
1265
+ let a1_waiters = barrier.dependency_index.waiters.get(&("node_a".to_string(), 1))
1266
+ .map(|w| w.clone())
1267
+ .unwrap_or_default();
1268
+ assert!(a1_waiters.contains(&"op_a1".to_string()));
1269
+
1270
+ let a2_waiters = barrier.dependency_index.waiters.get(&("node_a".to_string(), 2))
1271
+ .map(|w| w.clone())
1272
+ .unwrap_or_default();
1273
+ assert!(a2_waiters.contains(&"op_a2".to_string()));
1274
+
1275
+ // Advance frontier to A:2
1276
+ let mut frontier = VectorClock::new();
1277
+ frontier.clocks.insert("node_a".to_string(), 2);
1278
+ barrier.advance_frontier(&frontier);
1279
+
1280
+ // Check which operations are Ready
1281
+ let pending = barrier.get_pending_operations();
1282
+ let ready: Vec<_> = pending.iter()
1283
+ .filter(|p| matches!(p.state, OperationState::Ready))
1284
+ .map(|p| p.operation_id.clone())
1285
+ .collect();
1286
+
1287
+ // op_a1 and op_a2 should be Ready
1288
+ assert_eq!(ready.len(), 2);
1289
+ assert!(ready.contains(&"op_a1".to_string()));
1290
+ assert!(ready.contains(&"op_a2".to_string()));
1291
+
1292
+ // op_a3 should still be Waiting (needs A:3)
1293
+ let waiting: Vec<_> = pending.iter()
1294
+ .filter(|p| matches!(p.state, OperationState::WaitingForDependencies { .. }))
1295
+ .map(|p| p.operation_id.clone())
1296
+ .collect();
1297
+ assert_eq!(waiting.len(), 1);
1298
+ assert!(waiting.contains(&"op_a3".to_string()));
1299
+ }
1300
+
1301
+ // ============================================================================
1302
+ // PHASE 7.1-OPT: INDEX INVARIANT TESTS
1303
+ // ============================================================================
1304
+
1305
+ #[test]
1306
+ fn phase_7_1_opt_index_invariant_all_operations_discoverable() {
1307
+ // Invariant: Every pending operation must be discoverable through index
1308
+ let mut barrier = CausalDependencyBarrier::new();
1309
+
1310
+ // Add 20 operations with varied dependencies
1311
+ for i in 0..20 {
1312
+ let mut required = VectorClock::new();
1313
+ required.clocks.insert("node_a".to_string(), ((i % 5) + 1) as u64);
1314
+ if i % 3 == 0 {
1315
+ required.clocks.insert("node_b".to_string(), ((i / 3) + 1) as u64);
1316
+ }
1317
+ barrier.receive_operation(format!("op_{}", i), required);
1318
+ }
1319
+
1320
+ // Verify each pending operation is in index for all its dependencies
1321
+ for (op_id, pending_op) in &barrier.pending {
1322
+ for (node, ts) in &pending_op.required_dependencies.clocks {
1323
+ let key = (node.clone(), *ts);
1324
+ assert!(
1325
+ barrier.dependency_index.waiters.get(&key)
1326
+ .map(|waiters| waiters.contains(op_id))
1327
+ .unwrap_or(false),
1328
+ "Operation {} not found in index for ({}, {})",
1329
+ op_id, node, ts
1330
+ );
1331
+ }
1332
+ }
1333
+ }
1334
+
1335
+ #[test]
1336
+ fn phase_7_1_opt_index_invariant_removed_on_apply() {
1337
+ // Invariant: When operation is applied, it must be removed from index
1338
+ let mut barrier = CausalDependencyBarrier::new();
1339
+
1340
+ // Add operation
1341
+ let mut required = VectorClock::new();
1342
+ required.clocks.insert("node_a".to_string(), 1);
1343
+ let result = barrier.receive_operation("op_1".to_string(), required.clone());
1344
+
1345
+ // Verify it's indexed
1346
+ assert!(
1347
+ barrier.dependency_index.waiters.get(&("node_a".to_string(), 1))
1348
+ .map(|w| w.contains(&"op_1".to_string()))
1349
+ .unwrap_or(false)
1350
+ );
1351
+
1352
+ // Apply operation (moving it from pending to applied)
1353
+ barrier.apply_operation("op_1", &required);
1354
+
1355
+ // Verify it's removed from index (because apply calls unindex when removing from pending)
1356
+ for waiters in barrier.dependency_index.waiters.values() {
1357
+ assert!(
1358
+ !waiters.contains(&"op_1".to_string()),
1359
+ "Applied operation should be removed from all index entries"
1360
+ );
1361
+ }
1362
+ }
1363
+
1364
+ #[test]
1365
+ fn phase_7_1_opt_index_invariant_duplicate_dependencies() {
1366
+ // Invariant: Handle operations with duplicate node dependencies
1367
+ let mut barrier = CausalDependencyBarrier::new();
1368
+
1369
+ // Create operation with same node in required_vc (vector clock deduplicates)
1370
+ let mut required = VectorClock::new();
1371
+ required.clocks.insert("node_a".to_string(), 5);
1372
+ barrier.receive_operation("op_dup".to_string(), required.clone());
1373
+
1374
+ // Should be indexed exactly once for (node_a, 5)
1375
+ let waiters_at_5 = barrier.dependency_index.waiters.get(&("node_a".to_string(), 5))
1376
+ .map(|w| w.iter().filter(|id| id == &"op_dup").count())
1377
+ .unwrap_or(0);
1378
+
1379
+ // VectorClock naturally deduplicates, so should only appear once
1380
+ assert_eq!(waiters_at_5, 1, "Operation should appear once per unique dependency");
1381
+ }
1382
+
1383
+ #[test]
1384
+ fn phase_7_1_opt_index_invariant_multiple_nodes() {
1385
+ // Invariant: Correctly handle multi-node dependencies
1386
+ let mut barrier = CausalDependencyBarrier::new();
1387
+
1388
+ let mut required = VectorClock::new();
1389
+ required.clocks.insert("node_a".to_string(), 2);
1390
+ required.clocks.insert("node_b".to_string(), 3);
1391
+ required.clocks.insert("node_c".to_string(), 1);
1392
+
1393
+ barrier.receive_operation("op_multi".to_string(), required);
1394
+
1395
+ // Should be findable via any dependency
1396
+ assert!(barrier.dependency_index.waiters.get(&("node_a".to_string(), 2))
1397
+ .map(|w| w.contains(&"op_multi".to_string()))
1398
+ .unwrap_or(false));
1399
+
1400
+ assert!(barrier.dependency_index.waiters.get(&("node_b".to_string(), 3))
1401
+ .map(|w| w.contains(&"op_multi".to_string()))
1402
+ .unwrap_or(false));
1403
+
1404
+ assert!(barrier.dependency_index.waiters.get(&("node_c".to_string(), 1))
1405
+ .map(|w| w.contains(&"op_multi".to_string()))
1406
+ .unwrap_or(false));
1407
+ }
1408
+
1409
+ // ============================================================================
1410
+ // PHASE 7.1-OPT: CORE FUNCTIONALITY TESTS
1411
+ // ============================================================================
1412
+
1413
+ #[test]
1414
+ fn phase_7_1_opt_core_sequential_release() {
1415
+ // Core test: Sequential operations released (marked Ready) in correct order
1416
+ let mut barrier = CausalDependencyBarrier::new();
1417
+
1418
+ // Add 5 operations with sequential dependencies
1419
+ for i in 1..=5 {
1420
+ let mut required = VectorClock::new();
1421
+ required.clocks.insert("node_a".to_string(), i as u64);
1422
+ barrier.receive_operation(format!("op_{}", i), required);
1423
+ }
1424
+
1425
+ assert_eq!(barrier.get_pending_operations().len(), 5);
1426
+
1427
+ // Advance to 3
1428
+ let mut frontier = VectorClock::new();
1429
+ frontier.clocks.insert("node_a".to_string(), 3);
1430
+ barrier.advance_frontier(&frontier);
1431
+
1432
+ // Check which operations are Ready vs still Waiting
1433
+ let pending = barrier.get_pending_operations();
1434
+ let ready_ops: Vec<_> = pending.iter()
1435
+ .filter(|p| matches!(p.state, OperationState::Ready))
1436
+ .map(|p| p.operation_id.clone())
1437
+ .collect();
1438
+ let waiting_ops: Vec<_> = pending.iter()
1439
+ .filter(|p| matches!(p.state, OperationState::WaitingForDependencies { .. }))
1440
+ .map(|p| p.operation_id.clone())
1441
+ .collect();
1442
+
1443
+ // op_1, op_2, op_3 should be Ready
1444
+ assert_eq!(ready_ops.len(), 3);
1445
+ assert!(ready_ops.contains(&"op_1".to_string()));
1446
+ assert!(ready_ops.contains(&"op_2".to_string()));
1447
+ assert!(ready_ops.contains(&"op_3".to_string()));
1448
+
1449
+ // op_4, op_5 should still be Waiting
1450
+ assert_eq!(waiting_ops.len(), 2);
1451
+ assert!(waiting_ops.contains(&"op_4".to_string()));
1452
+ assert!(waiting_ops.contains(&"op_5".to_string()));
1453
+ }
1454
+
1455
+ #[test]
1456
+ fn phase_7_1_opt_core_index_maintained_on_release() {
1457
+ // Core test: Index is properly maintained when operations are released
1458
+ let mut barrier = CausalDependencyBarrier::new();
1459
+
1460
+ // Add operations
1461
+ let mut req_1 = VectorClock::new();
1462
+ req_1.clocks.insert("node_a".to_string(), 1);
1463
+ barrier.receive_operation("op_1".to_string(), req_1);
1464
+
1465
+ let mut req_2 = VectorClock::new();
1466
+ req_2.clocks.insert("node_a".to_string(), 2);
1467
+ barrier.receive_operation("op_2".to_string(), req_2);
1468
+
1469
+ // Index should have entries
1470
+ assert!(barrier.dependency_index.waiters.get(&("node_a".to_string(), 1))
1471
+ .map(|w| w.contains(&"op_1".to_string()))
1472
+ .unwrap_or(false));
1473
+
1474
+ // Advance frontier
1475
+ let mut frontier = VectorClock::new();
1476
+ frontier.clocks.insert("node_a".to_string(), 1);
1477
+ barrier.advance_frontier(&frontier);
1478
+
1479
+ // After releasing op_1, it should still be in pending but marked Ready
1480
+ // and it should be removed from the index if using optimized version
1481
+ // (but for now, just verify it's marked as Ready)
1482
+ let pending = barrier.get_pending_operations();
1483
+ let op_1 = pending.iter().find(|p| p.operation_id == "op_1").unwrap();
1484
+ assert!(matches!(op_1.state, OperationState::Ready));
1485
+ }
1486
+
1487
+ #[test]
1488
+ fn phase_7_1_opt_core_index_maintains_consistency() {
1489
+ // Core test: Index stays consistent with pending operations
1490
+ let mut barrier = CausalDependencyBarrier::new();
1491
+
1492
+ // Add mix of operations
1493
+ for i in 0..5 {
1494
+ let mut required = VectorClock::new();
1495
+ required.clocks.insert("node_a".to_string(), ((i % 3) + 1) as u64);
1496
+ barrier.receive_operation(format!("op_{}", i), required);
1497
+ }
1498
+
1499
+ // Verify all pending ops are in index
1500
+ for (op_id, pending_op) in &barrier.pending {
1501
+ for (node, ts) in &pending_op.required_dependencies.clocks {
1502
+ assert!(
1503
+ barrier.dependency_index.waiters.get(&(node.clone(), *ts))
1504
+ .map(|w| w.contains(op_id))
1505
+ .unwrap_or(false),
1506
+ "Operation {} not indexed for ({}, {})",
1507
+ op_id, node, ts
1508
+ );
1509
+ }
1510
+ }
1511
+
1512
+ // Advance and check that released ops are removed from index
1513
+ let mut frontier = VectorClock::new();
1514
+ frontier.clocks.insert("node_a".to_string(), 2);
1515
+ barrier.advance_frontier(&frontier);
1516
+
1517
+ // Verify remaining pending ops are still in index
1518
+ for (op_id, pending_op) in &barrier.pending {
1519
+ for (node, ts) in &pending_op.required_dependencies.clocks {
1520
+ assert!(
1521
+ barrier.dependency_index.waiters.get(&(node.clone(), *ts))
1522
+ .map(|w| w.contains(op_id))
1523
+ .unwrap_or(false),
1524
+ "Remaining operation {} should still be in index",
1525
+ op_id
1526
+ );
1527
+ }
1528
+ }
1529
+ }
1530
+
1531
+ // ============================================================================
1532
+ // PHASE 7.1.1: COMPREHENSIVE DIFFERENTIAL VALIDATION
1533
+ // ============================================================================
1534
+ // Permanent infrastructure for regression testing.
1535
+ // Ensures optimized and reference implementations remain semantically equivalent.
1536
+
1537
+ /// Scenario generator for differential testing
1538
+ #[derive(Clone, Debug)]
1539
+ struct DifferentialScenario {
1540
+ seed: u64,
1541
+ initial_frontier: VectorClock,
1542
+ pending_operations: Vec<(String, VectorClock)>,
1543
+ frontier_advances: Vec<VectorClock>,
1544
+ }
1545
+
1546
+ impl DifferentialScenario {
1547
+ fn random_with_seed(seed: u64) -> Self {
1548
+ use std::collections::hash_map::DefaultHasher;
1549
+ use std::hash::{Hash, Hasher};
1550
+
1551
+ let mut hasher = DefaultHasher::new();
1552
+ seed.hash(&mut hasher);
1553
+ let mut rng_seed = hasher.finish();
1554
+
1555
+ fn next_rng(seed: &mut u64) -> u64 {
1556
+ *seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
1557
+ *seed
1558
+ }
1559
+
1560
+ // Generate random frontier
1561
+ let mut initial_frontier = VectorClock::new();
1562
+ let node_count = (next_rng(&mut rng_seed) % 5) as usize + 2; // 2-6 nodes
1563
+ for i in 0..node_count {
1564
+ let ts = (next_rng(&mut rng_seed) % 20) as u64;
1565
+ if ts > 0 {
1566
+ initial_frontier.clocks.insert(format!("node_{}", i), ts);
1567
+ }
1568
+ }
1569
+
1570
+ // Generate random pending operations
1571
+ let pending_count = (next_rng(&mut rng_seed) % 100) as usize + 10; // 10-109
1572
+ let mut pending_operations = Vec::new();
1573
+ for i in 0..pending_count {
1574
+ let mut required = VectorClock::new();
1575
+ let req_node_count = (next_rng(&mut rng_seed) % node_count as u64) as usize + 1;
1576
+ for j in 0..req_node_count {
1577
+ let node_idx = next_rng(&mut rng_seed) % node_count as u64;
1578
+ let ts = (next_rng(&mut rng_seed) % 100) as u64 + 1;
1579
+ required.clocks.insert(format!("node_{}", node_idx), ts);
1580
+ }
1581
+ pending_operations.push((format!("op_{}", i), required));
1582
+ }
1583
+
1584
+ // Generate random frontier advances
1585
+ let advance_count = (next_rng(&mut rng_seed) % 20) as usize + 5; // 5-24
1586
+ let mut frontier_advances = Vec::new();
1587
+ for _ in 0..advance_count {
1588
+ let mut advance = initial_frontier.clone();
1589
+ let node_to_advance = next_rng(&mut rng_seed) % node_count as u64;
1590
+ let node_key = format!("node_{}", node_to_advance);
1591
+ let new_ts = (next_rng(&mut rng_seed) % 100) as u64 + 1;
1592
+ advance.clocks.insert(node_key, new_ts);
1593
+ frontier_advances.push(advance);
1594
+ }
1595
+
1596
+ Self {
1597
+ seed,
1598
+ initial_frontier,
1599
+ pending_operations,
1600
+ frontier_advances,
1601
+ }
1602
+ }
1603
+ }
1604
+
1605
+ #[test]
1606
+ fn phase_7_1_1_differential_random_scenarios_seed_based() {
1607
+ // Test 1: Random scenarios with deterministic seed
1608
+ // This ensures reproducibility and comprehensive coverage
1609
+
1610
+ for seed in 0..50 {
1611
+ let scenario = DifferentialScenario::random_with_seed(seed);
1612
+
1613
+ // Reference implementation
1614
+ let mut barrier_ref = CausalDependencyBarrier::new();
1615
+ for (op_id, required) in &scenario.pending_operations {
1616
+ barrier_ref.receive_operation(op_id.clone(), required.clone());
1617
+ }
1618
+
1619
+ // Optimized implementation
1620
+ let mut barrier_opt = CausalDependencyBarrier::new();
1621
+ for (op_id, required) in &scenario.pending_operations {
1622
+ barrier_opt.receive_operation(op_id.clone(), required.clone());
1623
+ }
1624
+
1625
+ // Run through advances
1626
+ for advance in &scenario.frontier_advances {
1627
+ barrier_ref.clear_metrics();
1628
+ barrier_opt.clear_metrics();
1629
+
1630
+ // Reference
1631
+ barrier_ref.prev_frontier.clone_from(&barrier_ref.local_frontier);
1632
+ barrier_ref.local_frontier.merge(advance);
1633
+ let _ref_ready = barrier_ref.check_unblocked_operations_full_scan();
1634
+
1635
+ // Optimized
1636
+ barrier_opt.prev_frontier.clone_from(&barrier_opt.local_frontier);
1637
+ barrier_opt.local_frontier.merge(advance);
1638
+ let _opt_ready = barrier_opt.check_unblocked_operations_indexed();
1639
+
1640
+ // Compare results
1641
+ let ref_pending_count = barrier_ref.get_pending_operations().len();
1642
+ let opt_pending_count = barrier_opt.get_pending_operations().len();
1643
+
1644
+ assert_eq!(
1645
+ ref_pending_count, opt_pending_count,
1646
+ "Seed {}: Pending count mismatch after advance",
1647
+ seed
1648
+ );
1649
+
1650
+ // Verify index consistency in optimized version
1651
+ for (op_id, pending_op) in &barrier_opt.pending {
1652
+ for (node, ts) in &pending_op.required_dependencies.clocks {
1653
+ let key = (node.clone(), *ts);
1654
+ if let Some(waiters) = barrier_opt.dependency_index.waiters.get(&key) {
1655
+ assert!(
1656
+ waiters.contains(op_id),
1657
+ "Seed {}: Operation {} not in index for {:?}",
1658
+ seed, op_id, key
1659
+ );
1660
+ }
1661
+ }
1662
+ }
1663
+ }
1664
+ }
1665
+ }
1666
+
1667
+ #[test]
1668
+ fn phase_7_1_1_differential_adversarial_multiple_ops_same_dependency() {
1669
+ // Adversarial: Multiple operations depending on same (node, ts)
1670
+ let mut barrier_ref = CausalDependencyBarrier::new();
1671
+ let mut barrier_opt = CausalDependencyBarrier::new();
1672
+
1673
+ // Create 10 operations all requiring A:5
1674
+ for i in 0..10 {
1675
+ let mut required = VectorClock::new();
1676
+ required.clocks.insert("node_a".to_string(), 5);
1677
+ barrier_ref.receive_operation(format!("op_{}", i), required.clone());
1678
+ barrier_opt.receive_operation(format!("op_{}", i), required);
1679
+ }
1680
+
1681
+ // Advance to A:5
1682
+ let mut frontier = VectorClock::new();
1683
+ frontier.clocks.insert("node_a".to_string(), 5);
1684
+
1685
+ barrier_ref.prev_frontier.clone_from(&barrier_ref.local_frontier);
1686
+ barrier_ref.local_frontier.merge(&frontier);
1687
+ barrier_ref.check_unblocked_operations_full_scan();
1688
+
1689
+ barrier_opt.prev_frontier.clone_from(&barrier_opt.local_frontier);
1690
+ barrier_opt.local_frontier.merge(&frontier);
1691
+ barrier_opt.check_unblocked_operations_indexed();
1692
+
1693
+ // Both should have released all 10
1694
+ let ref_ready = barrier_ref.get_pending_operations()
1695
+ .iter()
1696
+ .filter(|p| matches!(p.state, OperationState::Ready))
1697
+ .count();
1698
+ let opt_ready = barrier_opt.get_pending_operations()
1699
+ .iter()
1700
+ .filter(|p| matches!(p.state, OperationState::Ready))
1701
+ .count();
1702
+
1703
+ assert_eq!(ref_ready, opt_ready);
1704
+ assert_eq!(ref_ready, 10);
1705
+ }
1706
+
1707
+ #[test]
1708
+ fn phase_7_1_1_differential_adversarial_frontier_jump() {
1709
+ // Adversarial: Large frontier jump
1710
+ let mut barrier_ref = CausalDependencyBarrier::new();
1711
+ let mut barrier_opt = CausalDependencyBarrier::new();
1712
+
1713
+ for i in 1..=10 {
1714
+ let mut req = VectorClock::new();
1715
+ req.clocks.insert("node_a".to_string(), i as u64);
1716
+ barrier_ref.receive_operation(format!("op_{}", i), req.clone());
1717
+ barrier_opt.receive_operation(format!("op_{}", i), req);
1718
+ }
1719
+
1720
+ // Jump to A:7
1721
+ let mut frontier = VectorClock::new();
1722
+ frontier.clocks.insert("node_a".to_string(), 7);
1723
+
1724
+ barrier_ref.prev_frontier.clone_from(&barrier_ref.local_frontier);
1725
+ barrier_ref.local_frontier.merge(&frontier);
1726
+ barrier_ref.check_unblocked_operations_full_scan();
1727
+
1728
+ barrier_opt.prev_frontier.clone_from(&barrier_opt.local_frontier);
1729
+ barrier_opt.local_frontier.merge(&frontier);
1730
+ barrier_opt.check_unblocked_operations_indexed();
1731
+
1732
+ // Both should have released op_1 through op_7
1733
+ let ref_ready = barrier_ref.get_pending_operations()
1734
+ .iter()
1735
+ .filter(|p| matches!(p.state, OperationState::Ready))
1736
+ .count();
1737
+ let opt_ready = barrier_opt.get_pending_operations()
1738
+ .iter()
1739
+ .filter(|p| matches!(p.state, OperationState::Ready))
1740
+ .count();
1741
+
1742
+ assert_eq!(ref_ready, opt_ready);
1743
+ assert_eq!(ref_ready, 7);
1744
+ }
1745
+
1746
+ #[test]
1747
+ fn phase_7_1_1_differential_adversarial_simultaneous_release() {
1748
+ // Adversarial: All operations ready at same frontier advance
1749
+ let mut barrier_ref = CausalDependencyBarrier::new();
1750
+ let mut barrier_opt = CausalDependencyBarrier::new();
1751
+
1752
+ for i in 0..20 {
1753
+ let mut req = VectorClock::new();
1754
+ req.clocks.insert("node_a".to_string(), 5);
1755
+ barrier_ref.receive_operation(format!("op_{}", i), req.clone());
1756
+ barrier_opt.receive_operation(format!("op_{}", i), req);
1757
+ }
1758
+
1759
+ let mut frontier = VectorClock::new();
1760
+ frontier.clocks.insert("node_a".to_string(), 5);
1761
+
1762
+ barrier_ref.prev_frontier.clone_from(&barrier_ref.local_frontier);
1763
+ barrier_ref.local_frontier.merge(&frontier);
1764
+ barrier_ref.check_unblocked_operations_full_scan();
1765
+
1766
+ barrier_opt.prev_frontier.clone_from(&barrier_opt.local_frontier);
1767
+ barrier_opt.local_frontier.merge(&frontier);
1768
+ barrier_opt.check_unblocked_operations_indexed();
1769
+
1770
+ let ref_ready = barrier_ref.get_pending_operations()
1771
+ .iter()
1772
+ .filter(|p| matches!(p.state, OperationState::Ready))
1773
+ .count();
1774
+ let opt_ready = barrier_opt.get_pending_operations()
1775
+ .iter()
1776
+ .filter(|p| matches!(p.state, OperationState::Ready))
1777
+ .count();
1778
+
1779
+ assert_eq!(ref_ready, opt_ready);
1780
+ assert_eq!(ref_ready, 20);
1781
+ }
1782
+
1783
+ #[test]
1784
+ fn phase_7_1_1_differential_adversarial_empty_buffer() {
1785
+ // Adversarial: Empty pending buffer
1786
+ let mut barrier_ref = CausalDependencyBarrier::new();
1787
+ let mut barrier_opt = CausalDependencyBarrier::new();
1788
+
1789
+ let mut frontier = VectorClock::new();
1790
+ frontier.clocks.insert("node_a".to_string(), 10);
1791
+
1792
+ barrier_ref.prev_frontier.clone_from(&barrier_ref.local_frontier);
1793
+ barrier_ref.local_frontier.merge(&frontier);
1794
+ let ref_ready = barrier_ref.check_unblocked_operations_full_scan();
1795
+
1796
+ barrier_opt.prev_frontier.clone_from(&barrier_opt.local_frontier);
1797
+ barrier_opt.local_frontier.merge(&frontier);
1798
+ let opt_ready = barrier_opt.check_unblocked_operations_indexed();
1799
+
1800
+ assert_eq!(ref_ready.len(), 0);
1801
+ assert_eq!(opt_ready.len(), 0);
1802
+ }
1803
+
1804
+ #[test]
1805
+ fn phase_7_1_1_index_invariant_no_orphaned_entries() {
1806
+ // Verify index never references non-existent operations
1807
+ let mut barrier = CausalDependencyBarrier::new();
1808
+
1809
+ for i in 0..50 {
1810
+ let mut req = VectorClock::new();
1811
+ req.clocks.insert("node_a".to_string(), ((i % 10) + 1) as u64);
1812
+ barrier.receive_operation(format!("op_{}", i), req);
1813
+ }
1814
+
1815
+ // Release some operations
1816
+ let mut frontier = VectorClock::new();
1817
+ frontier.clocks.insert("node_a".to_string(), 5);
1818
+ barrier.prev_frontier.clone_from(&barrier.local_frontier);
1819
+ barrier.local_frontier.merge(&frontier);
1820
+ barrier.check_unblocked_operations();
1821
+
1822
+ // Verify all index entries reference operations in pending
1823
+ for waiters in barrier.dependency_index.waiters.values() {
1824
+ for op_id in waiters {
1825
+ assert!(
1826
+ barrier.pending.contains_key(op_id),
1827
+ "Index entry references non-existent operation {}",
1828
+ op_id
1829
+ );
1830
+ }
1831
+ }
1832
+ }
1833
+
1834
+ #[test]
1835
+ fn phase_7_1_1_recovery_index_reconstruction() {
1836
+ // Verify index can be reconstructed from pending operations
1837
+ let mut barrier1 = CausalDependencyBarrier::new();
1838
+
1839
+ // Build up some state
1840
+ for i in 0..20 {
1841
+ let mut req = VectorClock::new();
1842
+ req.clocks.insert("node_a".to_string(), ((i % 5) + 1) as u64);
1843
+ barrier1.receive_operation(format!("op_{}", i), req);
1844
+ }
1845
+
1846
+ let mut frontier = VectorClock::new();
1847
+ frontier.clocks.insert("node_a".to_string(), 2);
1848
+ barrier1.advance_frontier(&frontier);
1849
+
1850
+ // Simulate recovery: extract pending, rebuild index
1851
+ let pending_snapshot: Vec<_> = barrier1.pending.values().cloned().collect();
1852
+
1853
+ let mut barrier2 = CausalDependencyBarrier::new();
1854
+ for pending_op in pending_snapshot {
1855
+ barrier2.pending.insert(
1856
+ pending_op.operation_id.clone(),
1857
+ pending_op.clone(),
1858
+ );
1859
+ barrier2.index_operation(
1860
+ &pending_op.operation_id,
1861
+ &pending_op.required_dependencies,
1862
+ );
1863
+ }
1864
+
1865
+ // Verify index is equivalent
1866
+ assert_eq!(
1867
+ barrier1.dependency_index.waiters.len(),
1868
+ barrier2.dependency_index.waiters.len()
1869
+ );
1870
+
1871
+ for (key, waiters1) in &barrier1.dependency_index.waiters {
1872
+ let waiters2 = barrier2.dependency_index.waiters.get(key);
1873
+ assert!(waiters2.is_some(), "Recovered index missing key {:?}", key);
1874
+ let mut w1 = waiters1.clone();
1875
+ let mut w2 = waiters2.unwrap().clone();
1876
+ w1.sort();
1877
+ w2.sort();
1878
+ assert_eq!(w1, w2, "Index mismatch for key {:?}", key);
1879
+ }
1880
+ }
1881
+
1882
+ #[test]
1883
+ fn phase_7_1_1_state_hash_equivalence_simple() {
1884
+ // Simpler state equivalence test: verify both paths mark same ops as Ready
1885
+ let mut barrier_ref = CausalDependencyBarrier::new();
1886
+ let mut barrier_opt = CausalDependencyBarrier::new();
1887
+
1888
+ // Add 10 operations with varied dependencies
1889
+ for i in 0..10 {
1890
+ let mut req = VectorClock::new();
1891
+ req.clocks.insert("node_a".to_string(), (i + 1) as u64);
1892
+ barrier_ref.receive_operation(format!("op_{}", i), req.clone());
1893
+ barrier_opt.receive_operation(format!("op_{}", i), req);
1894
+ }
1895
+
1896
+ // Advance frontier to release first 5 operations
1897
+ let mut frontier = VectorClock::new();
1898
+ frontier.clocks.insert("node_a".to_string(), 5);
1899
+
1900
+ barrier_ref.prev_frontier.clone_from(&barrier_ref.local_frontier);
1901
+ barrier_ref.local_frontier.merge(&frontier);
1902
+ let _ref_ready = barrier_ref.check_unblocked_operations_full_scan();
1903
+
1904
+ barrier_opt.prev_frontier.clone_from(&barrier_opt.local_frontier);
1905
+ barrier_opt.local_frontier.merge(&frontier);
1906
+ let _opt_ready = barrier_opt.check_unblocked_operations_indexed();
1907
+
1908
+ // Compare operation states
1909
+ let ref_pending = barrier_ref.get_pending_operations();
1910
+ let opt_pending = barrier_opt.get_pending_operations();
1911
+
1912
+ assert_eq!(ref_pending.len(), opt_pending.len());
1913
+
1914
+ for ref_op in ref_pending.iter() {
1915
+ let opt_op = opt_pending.iter().find(|op| op.operation_id == ref_op.operation_id)
1916
+ .expect(&format!("Operation {} missing in optimized", ref_op.operation_id));
1917
+
1918
+ assert_eq!(
1919
+ std::mem::discriminant(&ref_op.state),
1920
+ std::mem::discriminant(&opt_op.state),
1921
+ "State mismatch for operation {}",
1922
+ ref_op.operation_id
1923
+ );
1924
+ }
1925
+ }
1926
+
1927
+ #[test]
1928
+ fn phase_7_1_1_property_multidimensional_frontier_satisfaction() {
1929
+ // Property test: Multi-node frontier satisfaction
1930
+ // Operations requiring multiple nodes should only release when ALL dimensions satisfied
1931
+ let mut barrier_ref = CausalDependencyBarrier::new();
1932
+ let mut barrier_opt = CausalDependencyBarrier::new();
1933
+
1934
+ // Create operations with multi-node dependencies
1935
+ // op_all_three: requires A:5, B:3, C:2
1936
+ let mut req_all = VectorClock::new();
1937
+ req_all.clocks.insert("node_a".to_string(), 5);
1938
+ req_all.clocks.insert("node_b".to_string(), 3);
1939
+ req_all.clocks.insert("node_c".to_string(), 2);
1940
+
1941
+ barrier_ref.receive_operation("op_all_three".to_string(), req_all.clone());
1942
+ barrier_opt.receive_operation("op_all_three".to_string(), req_all);
1943
+
1944
+ // Partial advance: only A:5 satisfied
1945
+ let mut partial = VectorClock::new();
1946
+ partial.clocks.insert("node_a".to_string(), 5);
1947
+ partial.clocks.insert("node_b".to_string(), 1);
1948
+ partial.clocks.insert("node_c".to_string(), 1);
1949
+
1950
+ barrier_ref.prev_frontier.clone_from(&barrier_ref.local_frontier);
1951
+ barrier_ref.local_frontier.merge(&partial);
1952
+ barrier_ref.check_unblocked_operations_full_scan();
1953
+
1954
+ barrier_opt.prev_frontier.clone_from(&barrier_opt.local_frontier);
1955
+ barrier_opt.local_frontier.merge(&partial);
1956
+ barrier_opt.check_unblocked_operations_indexed();
1957
+
1958
+ // op_all_three should still be waiting
1959
+ let ref_waiting = barrier_ref.get_pending_operations()
1960
+ .iter()
1961
+ .filter(|p| matches!(p.state, OperationState::WaitingForDependencies { .. }))
1962
+ .count();
1963
+ let opt_waiting = barrier_opt.get_pending_operations()
1964
+ .iter()
1965
+ .filter(|p| matches!(p.state, OperationState::WaitingForDependencies { .. }))
1966
+ .count();
1967
+
1968
+ assert_eq!(ref_waiting, 1);
1969
+ assert_eq!(opt_waiting, 1);
1970
+
1971
+ // Full advance: all dimensions satisfied
1972
+ let mut full = VectorClock::new();
1973
+ full.clocks.insert("node_a".to_string(), 5);
1974
+ full.clocks.insert("node_b".to_string(), 3);
1975
+ full.clocks.insert("node_c".to_string(), 2);
1976
+
1977
+ barrier_ref.prev_frontier.clone_from(&barrier_ref.local_frontier);
1978
+ barrier_ref.local_frontier.merge(&full);
1979
+ barrier_ref.check_unblocked_operations_full_scan();
1980
+
1981
+ barrier_opt.prev_frontier.clone_from(&barrier_opt.local_frontier);
1982
+ barrier_opt.local_frontier.merge(&full);
1983
+ barrier_opt.check_unblocked_operations_indexed();
1984
+
1985
+ // Now op_all_three should be ready
1986
+ let ref_ready = barrier_ref.get_pending_operations()
1987
+ .iter()
1988
+ .filter(|p| matches!(p.state, OperationState::Ready))
1989
+ .count();
1990
+ let opt_ready = barrier_opt.get_pending_operations()
1991
+ .iter()
1992
+ .filter(|p| matches!(p.state, OperationState::Ready))
1993
+ .count();
1994
+
1995
+ assert_eq!(ref_ready, 1);
1996
+ assert_eq!(opt_ready, 1);
1997
+ }
1998
+
1999
+ #[test]
2000
+ fn phase_7_1_1_adversarial_coordinate_by_coordinate_satisfaction() {
2001
+ // Adversarial: Dependencies satisfied one coordinate at a time
2002
+ let mut barrier_ref = CausalDependencyBarrier::new();
2003
+ let mut barrier_opt = CausalDependencyBarrier::new();
2004
+
2005
+ for i in 0..5 {
2006
+ let mut req = VectorClock::new();
2007
+ req.clocks.insert("node_a".to_string(), 10 + i as u64);
2008
+ req.clocks.insert("node_b".to_string(), 20 + i as u64);
2009
+ req.clocks.insert("node_c".to_string(), 30 + i as u64);
2010
+ barrier_ref.receive_operation(format!("op_{}", i), req.clone());
2011
+ barrier_opt.receive_operation(format!("op_{}", i), req);
2012
+ }
2013
+
2014
+ // Satisfy one coordinate at a time for each operation
2015
+ for coord in vec![("node_a", 14), ("node_b", 24), ("node_c", 34)] {
2016
+ let mut frontier = VectorClock::new();
2017
+ for (n, ts) in vec![("node_a", 14u64), ("node_b", 24u64), ("node_c", 34u64)] {
2018
+ frontier.clocks.insert(n.to_string(), ts);
2019
+ }
2020
+
2021
+ barrier_ref.prev_frontier.clone_from(&barrier_ref.local_frontier);
2022
+ barrier_ref.local_frontier.merge(&frontier);
2023
+ barrier_ref.check_unblocked_operations_full_scan();
2024
+
2025
+ barrier_opt.prev_frontier.clone_from(&barrier_opt.local_frontier);
2026
+ barrier_opt.local_frontier.merge(&frontier);
2027
+ barrier_opt.check_unblocked_operations_indexed();
2028
+ }
2029
+
2030
+ // Both should have released all 5 operations
2031
+ let ref_ready = barrier_ref.get_pending_operations()
2032
+ .iter()
2033
+ .filter(|p| matches!(p.state, OperationState::Ready))
2034
+ .count();
2035
+ let opt_ready = barrier_opt.get_pending_operations()
2036
+ .iter()
2037
+ .filter(|p| matches!(p.state, OperationState::Ready))
2038
+ .count();
2039
+
2040
+ assert_eq!(ref_ready, 5);
2041
+ assert_eq!(opt_ready, 5);
2042
+ }
2043
+
2044
+ #[test]
2045
+ fn phase_7_1_1_adversarial_large_pending_buffer() {
2046
+ // Adversarial: Large pending buffer with low release selectivity
2047
+ // 50 operations, only some will be released
2048
+ let mut barrier_ref = CausalDependencyBarrier::new();
2049
+ let mut barrier_opt = CausalDependencyBarrier::new();
2050
+
2051
+ // Add 50 operations with escalating dependencies on single node
2052
+ for i in 0..50 {
2053
+ let mut req = VectorClock::new();
2054
+ req.clocks.insert("node_a".to_string(), (i + 1) as u64);
2055
+ barrier_ref.receive_operation(format!("op_{}", i), req.clone());
2056
+ barrier_opt.receive_operation(format!("op_{}", i), req);
2057
+ }
2058
+
2059
+ // Advance frontier to release only first 25 (those requiring node_a <= 25)
2060
+ let mut frontier = VectorClock::new();
2061
+ frontier.clocks.insert("node_a".to_string(), 25);
2062
+
2063
+ barrier_ref.prev_frontier.clone_from(&barrier_ref.local_frontier);
2064
+ barrier_ref.local_frontier.merge(&frontier);
2065
+ barrier_ref.check_unblocked_operations_full_scan();
2066
+
2067
+ barrier_opt.prev_frontier.clone_from(&barrier_opt.local_frontier);
2068
+ barrier_opt.local_frontier.merge(&frontier);
2069
+ barrier_opt.check_unblocked_operations_indexed();
2070
+
2071
+ // Both should release exactly 25 operations
2072
+ let ref_ready = barrier_ref.get_pending_operations()
2073
+ .iter()
2074
+ .filter(|p| matches!(p.state, OperationState::Ready))
2075
+ .count();
2076
+ let opt_ready = barrier_opt.get_pending_operations()
2077
+ .iter()
2078
+ .filter(|p| matches!(p.state, OperationState::Ready))
2079
+ .count();
2080
+
2081
+ assert_eq!(ref_ready, 25);
2082
+ assert_eq!(opt_ready, 25);
2083
+ }
2084
+
2085
+ #[test]
2086
+ fn phase_7_1_1_adversarial_independent_node_dependencies() {
2087
+ // Adversarial: Operations depending on different independent nodes
2088
+ let mut barrier_ref = CausalDependencyBarrier::new();
2089
+ let mut barrier_opt = CausalDependencyBarrier::new();
2090
+
2091
+ // Create enough operations with dependencies on different nodes
2092
+ for i in 0..5 {
2093
+ let mut req = VectorClock::new();
2094
+ req.clocks.insert(format!("node_{}", i), 10);
2095
+ barrier_ref.receive_operation(format!("op_{}", i), req.clone());
2096
+ barrier_opt.receive_operation(format!("op_{}", i), req);
2097
+ }
2098
+
2099
+ // Advance all nodes to their required values
2100
+ let mut frontier = VectorClock::new();
2101
+ for i in 0..5 {
2102
+ frontier.clocks.insert(format!("node_{}", i), 10);
2103
+ }
2104
+
2105
+ barrier_ref.advance_frontier(&frontier);
2106
+ barrier_opt.advance_frontier(&frontier);
2107
+
2108
+ // All operations should be ready
2109
+ let ref_ready = barrier_ref.get_pending_operations()
2110
+ .iter()
2111
+ .filter(|p| matches!(p.state, OperationState::Ready))
2112
+ .count();
2113
+ let opt_ready = barrier_opt.get_pending_operations()
2114
+ .iter()
2115
+ .filter(|p| matches!(p.state, OperationState::Ready))
2116
+ .count();
2117
+
2118
+ assert_eq!(ref_ready, 5);
2119
+ assert_eq!(opt_ready, 5);
2120
+ }
2121
+
2122
+ #[test]
2123
+ fn phase_7_1_1_property_index_completeness() {
2124
+ // Property test: Index must contain all pending operations
2125
+ for seed in 0..10 {
2126
+ let scenario = DifferentialScenario::random_with_seed(seed);
2127
+ let mut barrier = CausalDependencyBarrier::new();
2128
+
2129
+ for (op_id, required) in &scenario.pending_operations {
2130
+ barrier.receive_operation(op_id.clone(), required.clone());
2131
+ }
2132
+
2133
+ // Every pending operation must be discoverable through index
2134
+ for (op_id, pending_op) in &barrier.pending {
2135
+ let mut found = false;
2136
+ for (node, ts) in &pending_op.required_dependencies.clocks {
2137
+ if let Some(waiters) = barrier.dependency_index.waiters.get(&(node.clone(), *ts)) {
2138
+ if waiters.contains(op_id) {
2139
+ found = true;
2140
+ break;
2141
+ }
2142
+ }
2143
+ }
2144
+ assert!(found, "Operation {} not found in any index entry", op_id);
2145
+ }
2146
+ }
2147
+ }
2148
+
2149
+ #[test]
2150
+ fn phase_7_1_1_property_frontier_only_advances() {
2151
+ // Property test: Frontier never regresses in any dimension
2152
+ for seed in 0..10 {
2153
+ let scenario = DifferentialScenario::random_with_seed(seed);
2154
+ let mut barrier = CausalDependencyBarrier::new();
2155
+
2156
+ for (op_id, required) in &scenario.pending_operations {
2157
+ barrier.receive_operation(op_id.clone(), required.clone());
2158
+ }
2159
+
2160
+ let mut prev_frontier = barrier.local_frontier.clone();
2161
+
2162
+ for advance in &scenario.frontier_advances {
2163
+ barrier.prev_frontier.clone_from(&barrier.local_frontier);
2164
+ barrier.local_frontier.merge(advance);
2165
+ barrier.check_unblocked_operations();
2166
+
2167
+ // Check frontier only advanced (never regressed)
2168
+ for (node, prev_ts) in &prev_frontier.clocks {
2169
+ let new_ts = barrier.local_frontier.get(node);
2170
+ assert!(
2171
+ new_ts >= *prev_ts,
2172
+ "Frontier regressed for {} from {} to {}",
2173
+ node, prev_ts, new_ts
2174
+ );
2175
+ }
2176
+
2177
+ prev_frontier = barrier.local_frontier.clone();
2178
+ }
2179
+ }
2180
+ }
2181
+
2182
+ #[test]
2183
+ fn phase_7_1_1_property_ready_operations_never_waiting() {
2184
+ // Property test: Once an operation is marked Ready, it never regresses to Waiting
2185
+ for seed in 0..10 {
2186
+ let scenario = DifferentialScenario::random_with_seed(seed);
2187
+ let mut barrier = CausalDependencyBarrier::new();
2188
+
2189
+ for (op_id, required) in &scenario.pending_operations {
2190
+ barrier.receive_operation(op_id.clone(), required.clone());
2191
+ }
2192
+
2193
+ let mut seen_ready = std::collections::HashSet::new();
2194
+
2195
+ for advance in &scenario.frontier_advances {
2196
+ barrier.prev_frontier.clone_from(&barrier.local_frontier);
2197
+ barrier.local_frontier.merge(advance);
2198
+ barrier.check_unblocked_operations();
2199
+
2200
+ for pending_op in barrier.get_pending_operations() {
2201
+ if matches!(pending_op.state, OperationState::Ready) {
2202
+ seen_ready.insert(pending_op.operation_id.clone());
2203
+ } else if matches!(pending_op.state, OperationState::WaitingForDependencies { .. }) {
2204
+ assert!(
2205
+ !seen_ready.contains(&pending_op.operation_id),
2206
+ "Operation {} regressed from Ready to Waiting",
2207
+ pending_op.operation_id
2208
+ );
2209
+ }
2210
+ }
2211
+ }
2212
+ }
2213
+ }
2214
+ }