@feltdb/core 0.4.12 → 0.4.13

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 (164) hide show
  1. package/dist/cli/commands.js +5 -2
  2. package/dist/cli/index.js +1 -1
  3. package/dist/create/cli.js +16 -1
  4. package/dist/create/create.js +30 -16
  5. package/dist/create/docker-compose-generator.js +48 -9
  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/index.js +1 -1
  160. package/dist/studio/{components-Q0Nx6gLE.js → components-bHTARBin.js} +156 -46
  161. package/dist/studio/index.js +31 -28
  162. package/dist/studio-app/assets/{index-BHOmLZF-.js → index-BqKquLuU.js} +8 -8
  163. package/dist/studio-app/index.html +1 -1
  164. package/package.json +1 -1
@@ -0,0 +1,618 @@
1
+ /// Convergence layer module for distributed state consistency
2
+ /// Provides causal state versioning, conflict resolution, and deterministic merge
3
+ use serde::{Deserialize, Serialize};
4
+ use std::cmp::Ordering;
5
+ use std::collections::HashMap;
6
+
7
+ /// Vector clock for tracking causality in distributed systems
8
+ /// Each instance maintains a logical clock value
9
+ #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
10
+ pub struct VectorClock {
11
+ /// Map of instance_id to logical clock value
12
+ pub clocks: HashMap<String, u64>,
13
+ }
14
+
15
+ impl VectorClock {
16
+ /// Create a new vector clock
17
+ pub fn new() -> Self {
18
+ Self {
19
+ clocks: HashMap::new(),
20
+ }
21
+ }
22
+
23
+ /// Create a vector clock from a HashMap
24
+ pub fn from_clocks(clocks: HashMap<String, u64>) -> Self {
25
+ Self { clocks }
26
+ }
27
+
28
+ /// Increment the clock for a specific instance
29
+ pub fn increment(&mut self, instance_id: &str) {
30
+ let entry = self.clocks.entry(instance_id.to_string()).or_insert(0);
31
+ *entry += 1;
32
+ }
33
+
34
+ /// Get the clock value for an instance
35
+ pub fn get(&self, instance_id: &str) -> u64 {
36
+ *self.clocks.get(instance_id).unwrap_or(&0)
37
+ }
38
+
39
+ /// Merge with another vector clock (takes maximum for each instance)
40
+ pub fn merge(&mut self, other: &VectorClock) {
41
+ for (instance_id, other_clock) in &other.clocks {
42
+ let entry = self.clocks.entry(instance_id.clone()).or_insert(0);
43
+ *entry = (*entry).max(*other_clock);
44
+ }
45
+ }
46
+
47
+ /// Check if this clock causally precedes another
48
+ pub fn happens_before(&self, other: &VectorClock) -> bool {
49
+ let mut at_least_one_less = false;
50
+
51
+ for (instance_id, other_value) in &other.clocks {
52
+ let self_value = self.get(instance_id);
53
+ if self_value > *other_value {
54
+ return false; // This clock is ahead in some dimension
55
+ }
56
+ if self_value < *other_value {
57
+ at_least_one_less = true;
58
+ }
59
+ }
60
+
61
+ // Also check if self has clocks that other doesn't (or other is behind on them)
62
+ for (instance_id, self_value) in &self.clocks {
63
+ let other_value = other.get(instance_id);
64
+ if self_value > &other_value {
65
+ return false;
66
+ }
67
+ }
68
+
69
+ at_least_one_less
70
+ }
71
+
72
+ /// Check if two clocks are concurrent (neither happens before the other)
73
+ pub fn is_concurrent(&self, other: &VectorClock) -> bool {
74
+ !self.happens_before(other) && !other.happens_before(self)
75
+ }
76
+
77
+ /// Compare two vector clocks for causal ordering
78
+ pub fn causal_cmp(&self, other: &VectorClock) -> Ordering {
79
+ if self == other {
80
+ return Ordering::Equal;
81
+ }
82
+
83
+ if self.happens_before(other) {
84
+ Ordering::Less
85
+ } else if other.happens_before(self) {
86
+ Ordering::Greater
87
+ } else {
88
+ // Concurrent - use lexicographic comparison of instance IDs as tiebreaker
89
+ let all_instances: std::collections::BTreeSet<_> =
90
+ self.clocks.keys().chain(other.clocks.keys()).collect();
91
+
92
+ for instance_id in all_instances {
93
+ let self_val = self.get(instance_id);
94
+ let other_val = other.get(instance_id);
95
+ if self_val != other_val {
96
+ return if self_val < other_val {
97
+ Ordering::Less
98
+ } else {
99
+ Ordering::Greater
100
+ };
101
+ }
102
+ }
103
+ Ordering::Equal
104
+ }
105
+ }
106
+ }
107
+
108
+ impl Default for VectorClock {
109
+ fn default() -> Self {
110
+ Self::new()
111
+ }
112
+ }
113
+
114
+ /// State version representing a causal snapshot of the system
115
+ /// Tracks which instance is at which logical clock value
116
+ #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
117
+ pub struct StateVersion {
118
+ /// Vector clock representing this state
119
+ pub vector_clock: VectorClock,
120
+ /// Timestamp when this version was created (ms since UNIX epoch)
121
+ pub timestamp_ms: u128,
122
+ }
123
+
124
+ impl StateVersion {
125
+ /// Create a new state version
126
+ pub fn new(vector_clock: VectorClock, timestamp_ms: u128) -> Self {
127
+ Self {
128
+ vector_clock,
129
+ timestamp_ms,
130
+ }
131
+ }
132
+
133
+ /// Create a state version from clocks
134
+ pub fn from_clocks(clocks: HashMap<String, u64>, timestamp_ms: u128) -> Self {
135
+ Self {
136
+ vector_clock: VectorClock::from_clocks(clocks),
137
+ timestamp_ms,
138
+ }
139
+ }
140
+
141
+ /// Check if this state version causally precedes another
142
+ pub fn happens_before(&self, other: &StateVersion) -> bool {
143
+ self.vector_clock.happens_before(&other.vector_clock)
144
+ }
145
+
146
+ /// Check if two state versions are concurrent
147
+ pub fn is_concurrent(&self, other: &StateVersion) -> bool {
148
+ self.vector_clock.is_concurrent(&other.vector_clock)
149
+ }
150
+
151
+ /// Get causal ordering between two state versions
152
+ pub fn causal_cmp(&self, other: &StateVersion) -> Ordering {
153
+ self.vector_clock.causal_cmp(&other.vector_clock)
154
+ }
155
+ }
156
+
157
+ /// Tracks the causal history of an operation
158
+ #[derive(Debug, Clone, Serialize, Deserialize)]
159
+ pub struct CausalHistory {
160
+ /// The vector clock when this operation occurred
161
+ pub state_version: StateVersion,
162
+ /// Instance that originated this operation
163
+ pub instance_id: String,
164
+ /// Sequence number from that instance
165
+ pub sequence: u64,
166
+ /// Any dependencies on other operations (by their vector clocks)
167
+ pub dependencies: Vec<VectorClock>,
168
+ }
169
+
170
+ impl CausalHistory {
171
+ /// Create a new causal history entry
172
+ pub fn new(state_version: StateVersion, instance_id: String, sequence: u64) -> Self {
173
+ Self {
174
+ state_version,
175
+ instance_id,
176
+ sequence,
177
+ dependencies: Vec::new(),
178
+ }
179
+ }
180
+
181
+ /// Add a dependency on another operation
182
+ pub fn add_dependency(&mut self, dep_clock: VectorClock) {
183
+ if !self.dependencies.contains(&dep_clock) {
184
+ self.dependencies.push(dep_clock);
185
+ }
186
+ }
187
+
188
+ /// Check if this operation depends on another (transitively)
189
+ pub fn depends_on(&self, other: &CausalHistory) -> bool {
190
+ // Direct dependency
191
+ if self
192
+ .dependencies
193
+ .contains(&other.state_version.vector_clock)
194
+ {
195
+ return true;
196
+ }
197
+
198
+ // Indirect dependency would require full history traversal
199
+ // For now, check if other happens before self
200
+ other.state_version.happens_before(&self.state_version)
201
+ }
202
+ }
203
+
204
+ /// Conflict resolution strategy for concurrent operations
205
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
206
+ pub enum ConflictResolutionStrategy {
207
+ /// Last-Write-Wins based on timestamp with instance_id as tiebreaker
208
+ LastWriteWins,
209
+ /// Causally consistent - apply in causal order, keep concurrent
210
+ CausalConsistent,
211
+ /// Application-defined (returned as unresolved for app to handle)
212
+ ApplicationDefined,
213
+ }
214
+
215
+ /// Result of conflict resolution
216
+ #[derive(Debug, Clone, Serialize, Deserialize)]
217
+ pub struct ConflictResolution {
218
+ /// Which operation "won" (Some(index)) or if unresolved (None)
219
+ pub winner_index: Option<usize>,
220
+ /// The resolution strategy used
221
+ pub strategy: ConflictResolutionStrategy,
222
+ /// Whether operations are concurrent
223
+ pub is_concurrent: bool,
224
+ /// Whether operations have causal ordering
225
+ pub is_causal: bool,
226
+ }
227
+
228
+ impl ConflictResolution {
229
+ /// Resolve concurrent operations using Last-Write-Wins strategy
230
+ pub fn resolve_lww(histories: &[CausalHistory]) -> Self {
231
+ if histories.is_empty() {
232
+ return Self {
233
+ winner_index: None,
234
+ strategy: ConflictResolutionStrategy::LastWriteWins,
235
+ is_concurrent: false,
236
+ is_causal: false,
237
+ };
238
+ }
239
+
240
+ if histories.len() == 1 {
241
+ return Self {
242
+ winner_index: Some(0),
243
+ strategy: ConflictResolutionStrategy::LastWriteWins,
244
+ is_concurrent: false,
245
+ is_causal: false,
246
+ };
247
+ }
248
+
249
+ // Check for causal ordering
250
+ let mut is_causal = false;
251
+ for i in 0..histories.len() {
252
+ for j in 0..histories.len() {
253
+ if i != j
254
+ && histories[i]
255
+ .state_version
256
+ .happens_before(&histories[j].state_version)
257
+ {
258
+ is_causal = true;
259
+ break;
260
+ }
261
+ }
262
+ if is_causal {
263
+ break;
264
+ }
265
+ }
266
+
267
+ // If there's causal ordering, return the one that happens after all others
268
+ if is_causal {
269
+ let mut winner_idx = 0;
270
+ for i in 1..histories.len() {
271
+ // If winner happens before i, then i is later
272
+ if histories[winner_idx]
273
+ .state_version
274
+ .happens_before(&histories[i].state_version)
275
+ {
276
+ winner_idx = i;
277
+ }
278
+ }
279
+ return Self {
280
+ winner_index: Some(winner_idx),
281
+ strategy: ConflictResolutionStrategy::LastWriteWins,
282
+ is_concurrent: false,
283
+ is_causal: true,
284
+ };
285
+ }
286
+
287
+ // Concurrent operations - use LWW with tiebreaker
288
+ let mut winner_idx = 0;
289
+ for i in 1..histories.len() {
290
+ let ts_cmp = histories[i]
291
+ .state_version
292
+ .timestamp_ms
293
+ .cmp(&histories[winner_idx].state_version.timestamp_ms);
294
+
295
+ if ts_cmp == std::cmp::Ordering::Greater {
296
+ winner_idx = i;
297
+ } else if ts_cmp == std::cmp::Ordering::Equal {
298
+ // Tiebreaker: lexicographic comparison of instance_id
299
+ if histories[i].instance_id > histories[winner_idx].instance_id {
300
+ winner_idx = i;
301
+ }
302
+ }
303
+ }
304
+
305
+ Self {
306
+ winner_index: Some(winner_idx),
307
+ strategy: ConflictResolutionStrategy::LastWriteWins,
308
+ is_concurrent: true,
309
+ is_causal: false,
310
+ }
311
+ }
312
+ }
313
+
314
+ /// Deterministic merge result for reference graph convergence
315
+ #[derive(Debug, Clone, Serialize, Deserialize)]
316
+ pub struct MergeResult {
317
+ /// Merged vector clock representing the combined state
318
+ pub merged_clock: VectorClock,
319
+ /// Conflicts detected during merge
320
+ pub conflicts: Vec<(usize, usize, ConflictResolution)>,
321
+ /// Whether merge resulted in a new state or kept existing
322
+ pub changed: bool,
323
+ }
324
+
325
+ impl MergeResult {
326
+ /// Create a new merge result
327
+ pub fn new(merged_clock: VectorClock) -> Self {
328
+ Self {
329
+ merged_clock,
330
+ conflicts: Vec::new(),
331
+ changed: false,
332
+ }
333
+ }
334
+ }
335
+
336
+ #[cfg(test)]
337
+ mod tests {
338
+ use super::*;
339
+
340
+ #[test]
341
+ fn vector_clock_increment() {
342
+ let mut clock = VectorClock::new();
343
+ clock.increment("inst1");
344
+ assert_eq!(clock.get("inst1"), 1);
345
+ assert_eq!(clock.get("inst2"), 0);
346
+
347
+ clock.increment("inst1");
348
+ assert_eq!(clock.get("inst1"), 2);
349
+ }
350
+
351
+ #[test]
352
+ fn vector_clock_merge() {
353
+ let mut clock1 = VectorClock::new();
354
+ clock1.increment("inst1");
355
+ clock1.increment("inst1");
356
+
357
+ let mut clock2 = VectorClock::new();
358
+ clock2.increment("inst2");
359
+ clock2.increment("inst2");
360
+ clock2.increment("inst2");
361
+
362
+ clock1.merge(&clock2);
363
+ assert_eq!(clock1.get("inst1"), 2);
364
+ assert_eq!(clock1.get("inst2"), 3);
365
+ }
366
+
367
+ #[test]
368
+ fn vector_clock_happens_before() {
369
+ let mut clock1 = VectorClock::new();
370
+ clock1.increment("inst1");
371
+
372
+ let mut clock2 = VectorClock::new();
373
+ clock2.increment("inst1");
374
+ clock2.increment("inst1");
375
+
376
+ assert!(clock1.happens_before(&clock2));
377
+ assert!(!clock2.happens_before(&clock1));
378
+ }
379
+
380
+ #[test]
381
+ fn vector_clock_concurrent() {
382
+ let mut clock1 = VectorClock::new();
383
+ clock1.increment("inst1");
384
+
385
+ let mut clock2 = VectorClock::new();
386
+ clock2.increment("inst2");
387
+
388
+ assert!(clock1.is_concurrent(&clock2));
389
+ assert!(clock2.is_concurrent(&clock1));
390
+ }
391
+
392
+ #[test]
393
+ fn state_version_causal_ordering() {
394
+ let mut clock1 = VectorClock::new();
395
+ clock1.increment("inst1");
396
+ let version1 = StateVersion::new(clock1, 1000);
397
+
398
+ let mut clock2 = VectorClock::new();
399
+ clock2.increment("inst1");
400
+ clock2.increment("inst1");
401
+ let version2 = StateVersion::new(clock2, 2000);
402
+
403
+ assert!(version1.happens_before(&version2));
404
+ assert!(version1.causal_cmp(&version2) == Ordering::Less);
405
+ }
406
+
407
+ #[test]
408
+ fn conflict_resolution_lww_causal() {
409
+ let mut clock1 = VectorClock::new();
410
+ clock1.increment("inst1");
411
+ let version1 = StateVersion::new(clock1, 1000);
412
+ let history1 = CausalHistory::new(version1, "inst1".to_string(), 1);
413
+
414
+ let mut clock2 = VectorClock::new();
415
+ clock2.increment("inst1");
416
+ clock2.increment("inst1");
417
+ let version2 = StateVersion::new(clock2, 2000);
418
+ let history2 = CausalHistory::new(version2, "inst1".to_string(), 2);
419
+
420
+ let resolution = ConflictResolution::resolve_lww(&[history1, history2]);
421
+ assert_eq!(resolution.winner_index, Some(1));
422
+ assert!(resolution.is_causal);
423
+ assert!(!resolution.is_concurrent);
424
+ }
425
+
426
+ #[test]
427
+ fn conflict_resolution_lww_concurrent() {
428
+ let mut clock1 = VectorClock::new();
429
+ clock1.increment("inst1");
430
+ let version1 = StateVersion::new(clock1, 1000);
431
+ let history1 = CausalHistory::new(version1, "inst1".to_string(), 1);
432
+
433
+ let mut clock2 = VectorClock::new();
434
+ clock2.increment("inst2");
435
+ let version2 = StateVersion::new(clock2, 2000);
436
+ let history2 = CausalHistory::new(version2, "inst2".to_string(), 1);
437
+
438
+ let resolution = ConflictResolution::resolve_lww(&[history1, history2]);
439
+ assert!(resolution.winner_index.is_some());
440
+ assert!(!resolution.is_causal);
441
+ assert!(resolution.is_concurrent);
442
+ }
443
+
444
+ #[test]
445
+ fn causal_history_dependency() {
446
+ let mut clock1 = VectorClock::new();
447
+ clock1.increment("inst1");
448
+ let version1 = StateVersion::new(clock1.clone(), 1000);
449
+ let history1 = CausalHistory::new(version1, "inst1".to_string(), 1);
450
+
451
+ let mut clock2 = VectorClock::new();
452
+ clock2.clone_from(&clock1);
453
+ clock2.increment("inst2");
454
+ let version2 = StateVersion::new(clock2, 2000);
455
+ let mut history2 = CausalHistory::new(version2, "inst2".to_string(), 1);
456
+
457
+ history2.add_dependency(clock1.clone());
458
+ assert!(history2.depends_on(&history1));
459
+ }
460
+
461
+ #[test]
462
+ fn vector_clock_lexicographic_tiebreaker() {
463
+ let mut clock1 = VectorClock::new();
464
+ clock1.clocks.insert("inst-a".to_string(), 1);
465
+ clock1.clocks.insert("inst-b".to_string(), 1);
466
+
467
+ let clock2 = VectorClock::from_clocks(clock1.clocks.clone());
468
+
469
+ // Same clocks - should use lexicographic tiebreaker
470
+ let cmp_result = clock1.causal_cmp(&clock2);
471
+ assert_eq!(cmp_result, Ordering::Equal);
472
+ }
473
+
474
+ #[test]
475
+ fn concurrent_mutations_deterministic_resolution() {
476
+ // Scenario: Two instances concurrently modify the same entity
477
+ // Result: Deterministic resolution based on LWW + instance_id tiebreaker
478
+
479
+ // Instance A modifies at timestamp 1000
480
+ let mut clock_a = VectorClock::new();
481
+ clock_a.increment("inst-a");
482
+ let version_a = StateVersion::new(clock_a, 1000);
483
+ let history_a = CausalHistory::new(version_a, "inst-a".to_string(), 1);
484
+
485
+ // Instance B modifies at timestamp 1000 (same time, but different instance)
486
+ let mut clock_b = VectorClock::new();
487
+ clock_b.increment("inst-b");
488
+ let version_b = StateVersion::new(clock_b, 1000);
489
+ let history_b = CausalHistory::new(version_b, "inst-b".to_string(), 1);
490
+
491
+ // Resolve conflict
492
+ let resolution = ConflictResolution::resolve_lww(&[history_a.clone(), history_b.clone()]);
493
+ assert!(resolution.is_concurrent);
494
+ assert!(!resolution.is_causal);
495
+ assert!(resolution.winner_index.is_some());
496
+
497
+ // Both resolutions should favor inst-b due to lexicographic ordering
498
+ assert_eq!(
499
+ resolution.winner_index,
500
+ Some(1), // inst-b when b is second
501
+ );
502
+ }
503
+
504
+ #[test]
505
+ fn causal_chain_three_operations() {
506
+ // Scenario: Three operations in a causal chain
507
+ // inst1 -> inst2 -> inst3
508
+
509
+ // Operation 1: inst1 creates something
510
+ let mut clock1 = VectorClock::new();
511
+ clock1.increment("inst1");
512
+ let version1 = StateVersion::new(clock1.clone(), 1000);
513
+ let history1 = CausalHistory::new(version1, "inst1".to_string(), 1);
514
+
515
+ // Operation 2: inst2 observes inst1 and adds to it
516
+ let mut clock2 = clock1.clone();
517
+ clock2.increment("inst2");
518
+ let version2 = StateVersion::new(clock2.clone(), 2000);
519
+ let history2 = CausalHistory::new(version2, "inst2".to_string(), 1);
520
+
521
+ // Operation 3: inst3 observes both and adds more
522
+ let mut clock3 = clock2.clone();
523
+ clock3.increment("inst3");
524
+ let version3 = StateVersion::new(clock3, 3000);
525
+ let history3 = CausalHistory::new(version3, "inst3".to_string(), 1);
526
+
527
+ // Verify causal chain
528
+ assert!(history1
529
+ .state_version
530
+ .happens_before(&history2.state_version));
531
+ assert!(history2
532
+ .state_version
533
+ .happens_before(&history3.state_version));
534
+ assert!(history1
535
+ .state_version
536
+ .happens_before(&history3.state_version));
537
+
538
+ // Resolve in different orders - should always choose the one that happens last
539
+ let resolution1 = ConflictResolution::resolve_lww(&[history1, history2, history3]);
540
+ assert_eq!(resolution1.winner_index, Some(2)); // history3 is latest
541
+ assert!(resolution1.is_causal);
542
+ }
543
+
544
+ #[test]
545
+ fn merge_result_construction() {
546
+ let mut merged_clock = VectorClock::new();
547
+ merged_clock.increment("inst1");
548
+ merged_clock.increment("inst2");
549
+
550
+ let mut merge_result = MergeResult::new(merged_clock.clone());
551
+ assert!(!merge_result.changed);
552
+ assert_eq!(merge_result.conflicts.len(), 0);
553
+
554
+ // Add a conflict
555
+ let history_a = CausalHistory::new(
556
+ StateVersion::new(VectorClock::new(), 1000),
557
+ "inst-a".to_string(),
558
+ 1,
559
+ );
560
+ let history_b = CausalHistory::new(
561
+ StateVersion::new(VectorClock::new(), 1000),
562
+ "inst-b".to_string(),
563
+ 1,
564
+ );
565
+ let resolution = ConflictResolution::resolve_lww(&[history_a, history_b]);
566
+ merge_result.conflicts.push((0, 1, resolution));
567
+
568
+ assert_eq!(merge_result.conflicts.len(), 1);
569
+ }
570
+
571
+ #[test]
572
+ fn vector_clock_partial_order() {
573
+ // Test partial ordering properties of vector clocks
574
+
575
+ let mut clock1 = VectorClock::new();
576
+ clock1.increment("inst1");
577
+
578
+ let mut clock2 = VectorClock::new();
579
+ clock2.increment("inst1");
580
+ clock2.increment("inst2");
581
+
582
+ let mut clock3 = VectorClock::new();
583
+ clock3.increment("inst2");
584
+ clock3.increment("inst2");
585
+
586
+ // clock1 happens before clock2
587
+ assert!(clock1.happens_before(&clock2));
588
+ assert!(!clock2.happens_before(&clock1));
589
+
590
+ // clock1 and clock3 are concurrent
591
+ assert!(clock1.is_concurrent(&clock3));
592
+ assert!(clock3.is_concurrent(&clock1));
593
+
594
+ // clock2 and clock3 are concurrent (neither is strictly before the other)
595
+ assert!(clock2.is_concurrent(&clock3));
596
+ }
597
+
598
+ #[test]
599
+ fn state_version_ordering() {
600
+ // Test that StateVersion uses vector clock ordering
601
+
602
+ let mut clock1 = VectorClock::new();
603
+ clock1.increment("inst1");
604
+ let version1 = StateVersion::new(clock1, 1000);
605
+
606
+ let mut clock2 = VectorClock::new();
607
+ clock2.increment("inst1");
608
+ clock2.increment("inst1");
609
+ let version2 = StateVersion::new(clock2, 2000);
610
+
611
+ // version1 causally precedes version2
612
+ assert!(version1.happens_before(&version2));
613
+
614
+ // version1 is ordered before version2
615
+ assert_eq!(version1.causal_cmp(&version2), Ordering::Less);
616
+ assert_eq!(version2.causal_cmp(&version1), Ordering::Greater);
617
+ }
618
+ }