@feltdb/core 0.8.3 → 0.8.5

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 (193) hide show
  1. package/dist/cli/commands.js +4 -1
  2. package/dist/cli/provisioning-neutrality.js +79 -0
  3. package/dist/collection.d.ts +43 -1
  4. package/dist/collection.d.ts.map +1 -1
  5. package/dist/collection.js +192 -22
  6. package/dist/create/create.js +25 -21
  7. package/dist/create/managed-account.js +11 -0
  8. package/dist/create/package-versions.js +1 -1
  9. package/dist/create/server-source/Cargo.lock +165 -0
  10. package/dist/create/server-source/Cargo.toml +9 -0
  11. package/dist/create/server-source/crates/feltdb/Cargo.toml +3 -0
  12. package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +44 -44
  13. package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +12 -24
  14. package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +7 -13
  15. package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +13 -10
  16. package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +12 -9
  17. package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +78 -25
  18. package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +16 -13
  19. package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +13 -5
  20. package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +41 -22
  21. package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +33 -15
  22. package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +100 -33
  23. package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +56 -20
  24. package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +116 -41
  25. package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +186 -55
  26. package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +64 -26
  27. package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +46 -15
  28. package/dist/create/server-source/crates/feltdb/src/admission.rs +8 -15
  29. package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +43 -13
  30. package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +15 -42
  31. package/dist/create/server-source/crates/feltdb/src/analytics.rs +65 -19
  32. package/dist/create/server-source/crates/feltdb/src/application.rs +113 -30
  33. package/dist/create/server-source/crates/feltdb/src/authorization_security_tests.rs +475 -140
  34. package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +17 -15
  35. package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +0 -1
  36. package/dist/create/server-source/crates/feltdb/src/causal_backlog_bound.rs +59 -15
  37. package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +266 -114
  38. package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +25 -7
  39. package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +10 -15
  40. package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +3 -11
  41. package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +14 -5
  42. package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +21 -25
  43. package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +14 -11
  44. package/dist/create/server-source/crates/feltdb/src/dedup_bound_investigation.rs +103 -22
  45. package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +18 -15
  46. package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +12 -8
  47. package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +1 -5
  48. package/dist/create/server-source/crates/feltdb/src/durable_operation_identity.rs +87 -23
  49. package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +3 -7
  50. package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +10 -9
  51. package/dist/create/server-source/crates/feltdb/src/equality_index.rs +595 -0
  52. package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +1 -6
  53. package/dist/create/server-source/crates/feltdb/src/indexing.rs +35 -38
  54. package/dist/create/server-source/crates/feltdb/src/lib.rs +1050 -117
  55. package/dist/create/server-source/crates/feltdb/src/managed_cas_tests.rs +4 -1
  56. package/dist/create/server-source/crates/feltdb/src/metrics.rs +0 -1
  57. package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +1 -2
  58. package/dist/create/server-source/crates/feltdb/src/multi_operation_transaction.rs +107 -30
  59. package/dist/create/server-source/crates/feltdb/src/observability.rs +19 -6
  60. package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +12 -11
  61. package/dist/create/server-source/crates/feltdb/src/operation_log.rs +9 -4
  62. package/dist/create/server-source/crates/feltdb/src/p1_application_atomicity.rs +65 -18
  63. package/dist/create/server-source/crates/feltdb/src/p1_atomicity_acceptance.rs +193 -57
  64. package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +37 -27
  65. package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +38 -10
  66. package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +20 -14
  67. package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +394 -229
  68. package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +8 -6
  69. package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +11 -13
  70. package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +90 -72
  71. package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +3 -3
  72. package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +33 -11
  73. package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +6 -6
  74. package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +14 -56
  75. package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +29 -27
  76. package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +35 -17
  77. package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +8 -2
  78. package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +59 -15
  79. package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +70 -17
  80. package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +23 -6
  81. package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +12 -3
  82. package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +41 -11
  83. package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +56 -25
  84. package/dist/create/server-source/crates/feltdb/src/policy_evaluation.rs +701 -245
  85. package/dist/create/server-source/crates/feltdb/src/production_api.rs +31 -13
  86. package/dist/create/server-source/crates/feltdb/src/query_execution_diagnostics.rs +126 -0
  87. package/dist/create/server-source/crates/feltdb/src/query_performance.rs +6 -8
  88. package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +5 -5
  89. package/dist/create/server-source/crates/feltdb/src/replica_acknowledgements.rs +48 -18
  90. package/dist/create/server-source/crates/feltdb/src/replica_membership.rs +30 -11
  91. package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +6 -3
  92. package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +4 -3
  93. package/dist/create/server-source/crates/feltdb/src/sharding.rs +36 -10
  94. package/dist/create/server-source/crates/feltdb/src/state_conflict_contract.rs +516 -0
  95. package/dist/create/server-source/crates/feltdb/src/state_contract.rs +305 -6
  96. package/dist/create/server-source/crates/feltdb/src/state_diff_contract.rs +222 -0
  97. package/dist/create/server-source/crates/feltdb/src/state_facade.rs +82 -54
  98. package/dist/create/server-source/crates/feltdb/src/state_hash.rs +2 -2
  99. package/dist/create/server-source/crates/feltdb/src/state_model.rs +1514 -537
  100. package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +6 -3
  101. package/dist/create/server-source/crates/feltdb/src/state_trigger.rs +672 -0
  102. package/dist/create/server-source/crates/feltdb/src/submission.rs +5 -11
  103. package/dist/create/server-source/crates/feltdb/src/sync.rs +12 -0
  104. package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +6 -8
  105. package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +24 -35
  106. package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +24 -8
  107. package/dist/create/server-source/crates/feltdb/src/transaction_preconditions.rs +248 -59
  108. package/dist/create/server-source/crates/feltdb/src/transactions.rs +17 -20
  109. package/dist/create/server-source/crates/feltdb/src/trigger_contract.rs +749 -0
  110. package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +1 -0
  111. package/dist/create/server-source/crates/feltdb/src/workload.rs +512 -4
  112. package/dist/create/server-source/crates/feltdb/src/workload_diagnostics.rs +442 -0
  113. package/dist/create/server-source/crates/feltdb/tests/branching_evidence.rs +299 -0
  114. package/dist/create/server-source/crates/feltdb/tests/current_revision_authority_evidence.rs +288 -0
  115. package/dist/create/server-source/crates/feltdb/tests/durable_format_compatibility.rs +392 -0
  116. package/dist/create/server-source/crates/feltdb/tests/feltdb_state_boundary_tests.rs +436 -220
  117. package/dist/create/server-source/crates/feltdb/tests/fixtures/state_conflict_contract_corpus.json +1916 -0
  118. package/dist/create/server-source/crates/feltdb/tests/fixtures/state_diff_contract_corpus.json +1878 -0
  119. package/dist/create/server-source/crates/feltdb/tests/fixtures/trigger_contract_corpus.json +1862 -0
  120. package/dist/create/server-source/crates/feltdb/tests/pr34_query_collection.rs +234 -0
  121. package/dist/create/server-source/crates/feltdb/tests/pr35_equality_index.rs +947 -0
  122. package/dist/create/server-source/crates/feltdb/tests/pr7_self_authorization_proof.rs +5 -8
  123. package/dist/create/server-source/crates/feltdb/tests/pr8_vocabulary_assessment.rs +52 -44
  124. package/dist/create/server-source/crates/feltdb/tests/pr9_phase2_boundary_tests.rs +33 -16
  125. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3a_path_a_tests.rs +22 -7
  126. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_authorized_mutations.rs +41 -22
  127. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_role_based_authorization.rs +25 -8
  128. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_simple_auth_delete.rs +9 -6
  129. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_team_delete_role_authorization.rs +120 -69
  130. package/dist/create/server-source/crates/feltdb/tests/pr9_teams_role_based_access.rs +21 -10
  131. package/dist/create/server-source/crates/feltdb/tests/production_readiness_contract.rs +1032 -0
  132. package/dist/create/server-source/crates/feltdb/tests/reconciliation_application.rs +868 -0
  133. package/dist/create/server-source/crates/feltdb/tests/reconciliation_wire_format_evidence.rs +221 -0
  134. package/dist/create/server-source/crates/feltdb/tests/resource_scoped_revisions.rs +338 -0
  135. package/dist/create/server-source/crates/feltdb/tests/revision_identity_contract.rs +1039 -0
  136. package/dist/create/server-source/crates/feltdb/tests/revision_model_decision.rs +739 -0
  137. package/dist/create/server-source/crates/feltdb/tests/revision_retention_boundary_evidence.rs +427 -0
  138. package/dist/create/server-source/crates/feltdb/tests/saas_authorization_integration.rs +3 -3
  139. package/dist/create/server-source/crates/feltdb/tests/saas_invitation_lifecycle.rs +25 -22
  140. package/dist/create/server-source/crates/feltdb/tests/state_conflict_contract_conformance.rs +1799 -0
  141. package/dist/create/server-source/crates/feltdb/tests/state_diff_contract_conformance.rs +1316 -0
  142. package/dist/create/server-source/crates/feltdb/tests/state_model_integration.rs +53 -61
  143. package/dist/create/server-source/crates/feltdb/tests/state_persistence_integration.rs +156 -61
  144. package/dist/create/server-source/crates/feltdb/tests/state_store_boundary_evidence.rs +299 -0
  145. package/dist/create/server-source/crates/feltdb/tests/sync_divergence_evidence.rs +255 -0
  146. package/dist/create/server-source/crates/feltdb/tests/three_way_input_boundary_evidence.rs +249 -0
  147. package/dist/create/server-source/crates/feltdb/tests/trigger_contract_conformance.rs +994 -0
  148. package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +2 -1
  149. package/dist/create/server-source/crates/feltdb-server/src/audit.rs +1137 -29
  150. package/dist/create/server-source/crates/feltdb-server/src/auth.rs +164 -13
  151. package/dist/create/server-source/crates/feltdb-server/src/main.rs +800 -34
  152. package/dist/db.d.ts +33 -34
  153. package/dist/db.d.ts.map +1 -1
  154. package/dist/db.js +74 -20
  155. package/dist/deployment.d.ts +30 -0
  156. package/dist/deployment.d.ts.map +1 -0
  157. package/dist/deployment.js +130 -0
  158. package/dist/embedded-transaction.d.ts +22 -4
  159. package/dist/embedded-transaction.d.ts.map +1 -1
  160. package/dist/embedded-transaction.js +51 -5
  161. package/dist/feltdb.d.ts +14 -2
  162. package/dist/feltdb.d.ts.map +1 -1
  163. package/dist/file-db.js +1 -1
  164. package/dist/http-client.d.ts +14 -0
  165. package/dist/http-client.d.ts.map +1 -1
  166. package/dist/http-client.js +23 -5
  167. package/dist/http-db.d.ts +119 -1
  168. package/dist/http-db.d.ts.map +1 -1
  169. package/dist/http-db.js +346 -31
  170. package/dist/index-core.d.ts +2 -0
  171. package/dist/index-core.d.ts.map +1 -1
  172. package/dist/index-core.js +2 -0
  173. package/dist/index.d.ts.map +1 -1
  174. package/dist/index.js +9 -0
  175. package/dist/indexeddb-db.d.ts.map +1 -1
  176. package/dist/indexeddb-db.js +35 -21
  177. package/dist/managed-recovery.d.ts +192 -0
  178. package/dist/managed-recovery.d.ts.map +1 -0
  179. package/dist/managed-recovery.js +242 -0
  180. package/dist/memory-db.js +1 -1
  181. package/dist/studio-app/assets/{feltdb_wasm-DB8cX151.js → feltdb_wasm-DaNwCLRX.js} +1 -1
  182. package/dist/studio-app/assets/feltdb_wasm_bg-DnsHNv6g.wasm +0 -0
  183. package/dist/studio-app/assets/index-j8IlhNqJ.js +29 -0
  184. package/dist/studio-app/index.html +1 -1
  185. package/dist/transaction.d.ts +30 -0
  186. package/dist/transaction.d.ts.map +1 -1
  187. package/dist/transaction.js +41 -0
  188. package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
  189. package/dist/workload.d.ts +2 -1
  190. package/dist/workload.d.ts.map +1 -1
  191. package/package.json +1 -1
  192. package/dist/studio-app/assets/feltdb_wasm_bg-ClhDHp0S.wasm +0 -0
  193. package/dist/studio-app/assets/index-B0k4UAlI.js +0 -29
@@ -47,32 +47,22 @@ mod tests {
47
47
  let initial_state_hash = StateHash::from_hex("hash_initial".to_string());
48
48
 
49
49
  // Node A (Server)
50
- let mut node_a_executor = DistributedTransactionExecutor::new(
51
- "node_a".to_string(),
52
- initial_state_hash.clone(),
53
- );
50
+ let mut node_a_executor =
51
+ DistributedTransactionExecutor::new("node_a".to_string(), initial_state_hash.clone());
54
52
 
55
53
  let mut node_a_transport = TcpTransport::new("127.0.0.1:19020".to_string());
56
54
 
57
55
  // Register the peer with same initial state
58
- node_a_executor.register_replica(
59
- "node_b".to_string(),
60
- initial_state_hash.clone(),
61
- );
56
+ node_a_executor.register_replica("node_b".to_string(), initial_state_hash.clone());
62
57
 
63
58
  // Node B (Client)
64
- let mut node_b_executor = DistributedTransactionExecutor::new(
65
- "node_b".to_string(),
66
- initial_state_hash.clone(),
67
- );
59
+ let mut node_b_executor =
60
+ DistributedTransactionExecutor::new("node_b".to_string(), initial_state_hash.clone());
68
61
 
69
62
  let mut node_b_transport = TcpTransport::new("127.0.0.1:19021".to_string());
70
63
 
71
64
  // Register the peer with same initial state
72
- node_b_executor.register_replica(
73
- "node_a".to_string(),
74
- initial_state_hash.clone(),
75
- );
65
+ node_b_executor.register_replica("node_a".to_string(), initial_state_hash.clone());
76
66
 
77
67
  // ===== Start server listening in a separate task =====
78
68
  let server_transport = node_a_transport.clone();
@@ -131,21 +121,17 @@ mod tests {
131
121
 
132
122
  let envelope = node_a_executor
133
123
  .execute_local_transaction(
134
- 1, // sequence
135
- "tx_1".to_string(), // transaction_id
136
- parent_version.clone(), // parent_version
137
- vec![op], // operations
124
+ 1, // sequence
125
+ "tx_1".to_string(), // transaction_id
126
+ parent_version.clone(), // parent_version
127
+ vec![op], // operations
138
128
  ConsistencyContract::local(),
139
129
  )
140
130
  .expect("execute_local_transaction on node_a");
141
131
 
142
132
  // ===== Node A sends ReplicationMessage to Node B =====
143
- let replication_msg = ReplicationMessage::new(
144
- envelope,
145
- "node_a".to_string(),
146
- "node_b".to_string(),
147
- 1,
148
- );
133
+ let replication_msg =
134
+ ReplicationMessage::new(envelope, "node_a".to_string(), "node_b".to_string(), 1);
149
135
 
150
136
  node_a_transport
151
137
  .send(replication_msg)
@@ -156,10 +142,7 @@ mod tests {
156
142
  sleep(Duration::from_millis(200)).await;
157
143
 
158
144
  // ===== Node B receives ReplicationMessage =====
159
- let received_messages = node_b_transport
160
- .receive()
161
- .await
162
- .expect("receive on node_b");
145
+ let received_messages = node_b_transport.receive().await.expect("receive on node_b");
163
146
 
164
147
  assert_eq!(
165
148
  received_messages.len(),
@@ -247,9 +230,7 @@ mod tests {
247
230
  if start.elapsed() > timeout {
248
231
  panic!(
249
232
  "{} startup read timeout after {:?}: got {:?}",
250
- node_name,
251
- timeout,
252
- line
233
+ node_name, timeout, line
253
234
  );
254
235
  }
255
236
  thread::sleep(Duration::from_millis(50));
@@ -387,19 +368,15 @@ mod tests {
387
368
  let initial_state_hash = StateHash::from_hex("hash_initial".to_string());
388
369
 
389
370
  // Node A (Server)
390
- let mut node_a_executor = DistributedTransactionExecutor::new(
391
- "node_a".to_string(),
392
- initial_state_hash.clone(),
393
- );
371
+ let mut node_a_executor =
372
+ DistributedTransactionExecutor::new("node_a".to_string(), initial_state_hash.clone());
394
373
  node_a_executor.register_replica("node_b".to_string(), initial_state_hash.clone());
395
374
 
396
375
  let mut node_a_transport = TcpTransport::new("127.0.0.1:19050".to_string());
397
376
 
398
377
  // Node B (Client)
399
- let mut node_b_executor = DistributedTransactionExecutor::new(
400
- "node_b".to_string(),
401
- initial_state_hash.clone(),
402
- );
378
+ let mut node_b_executor =
379
+ DistributedTransactionExecutor::new("node_b".to_string(), initial_state_hash.clone());
403
380
  node_b_executor.register_replica("node_a".to_string(), initial_state_hash.clone());
404
381
 
405
382
  let mut node_b_transport = TcpTransport::new("127.0.0.1:19051".to_string());
@@ -431,7 +408,10 @@ mod tests {
431
408
  sleep(Duration::from_millis(50)).await;
432
409
  attempts += 1;
433
410
  }
434
- assert!(node_a_transport.is_connected(), "Server should be connected");
411
+ assert!(
412
+ node_a_transport.is_connected(),
413
+ "Server should be connected"
414
+ );
435
415
  println!("✓ Connection established\n");
436
416
 
437
417
  // === STEP 2-4: Execute and replicate transaction 1 ===
@@ -468,18 +448,13 @@ mod tests {
468
448
  println!("✓ Transaction 1 executed on Node A\n");
469
449
 
470
450
  println!("STEP 3: Replicate transaction 1 to Node B...");
471
- let msg1 = ReplicationMessage::new(envelope1, "node_a".to_string(), "node_b".to_string(), 1);
472
- node_a_transport
473
- .send(msg1)
474
- .await
475
- .expect("send tx1");
451
+ let msg1 =
452
+ ReplicationMessage::new(envelope1, "node_a".to_string(), "node_b".to_string(), 1);
453
+ node_a_transport.send(msg1).await.expect("send tx1");
476
454
 
477
455
  sleep(Duration::from_millis(200)).await;
478
456
 
479
- let received1 = node_b_transport
480
- .receive()
481
- .await
482
- .expect("receive tx1");
457
+ let received1 = node_b_transport.receive().await.expect("receive tx1");
483
458
  assert_eq!(received1.len(), 1);
484
459
 
485
460
  let mut parent_vc_b = VectorClock::new();
@@ -503,9 +478,10 @@ mod tests {
503
478
  state_a_after_tx1.state_hash, state_b_after_tx1.state_hash,
504
479
  "State hashes should match after tx1"
505
480
  );
506
- println!("✓ State converged: {} == {}\n",
507
- state_a_after_tx1.state_hash,
508
- state_b_after_tx1.state_hash);
481
+ println!(
482
+ "✓ State converged: {} == {}\n",
483
+ state_a_after_tx1.state_hash, state_b_after_tx1.state_hash
484
+ );
509
485
 
510
486
  // === STEP 5-6: Close connection deliberately ===
511
487
  println!("STEP 5: Deliberately closing TCP connection...");
@@ -520,8 +496,14 @@ mod tests {
520
496
  println!("✓ Connection closed\n");
521
497
 
522
498
  println!("STEP 6: Verify both nodes detect disconnect...");
523
- assert!(!node_a_transport.is_connected(), "Node A should be disconnected");
524
- assert!(!node_b_transport.is_connected(), "Node B should be disconnected");
499
+ assert!(
500
+ !node_a_transport.is_connected(),
501
+ "Node A should be disconnected"
502
+ );
503
+ assert!(
504
+ !node_b_transport.is_connected(),
505
+ "Node B should be disconnected"
506
+ );
525
507
  println!("✓ Both nodes detected disconnect\n");
526
508
 
527
509
  // === STEP 7: Re-establish connection ===
@@ -563,7 +545,8 @@ mod tests {
563
545
  let mut parent_vc_tx2 = VectorClock::new();
564
546
  parent_vc_tx2.increment("node_a");
565
547
  parent_vc_tx2.increment("node_b"); // Include B's history
566
- let parent_version_tx2 = StateVersion::new(parent_vc_tx2, state_a_after_tx1.state_hash.to_string());
548
+ let parent_version_tx2 =
549
+ StateVersion::new(parent_vc_tx2, state_a_after_tx1.state_hash.to_string());
567
550
 
568
551
  let mut fields2 = HashMap::new();
569
552
  fields2.insert("tx".to_string(), json!("2"));
@@ -593,24 +576,20 @@ mod tests {
593
576
  println!("✓ Transaction 2 executed on Node A\n");
594
577
 
595
578
  println!("STEP 9: Replicate transaction 2 to Node B...");
596
- let msg2 = ReplicationMessage::new(envelope2, "node_a".to_string(), "node_b".to_string(), 2);
597
- node_a_transport_v2
598
- .send(msg2)
599
- .await
600
- .expect("send tx2");
579
+ let msg2 =
580
+ ReplicationMessage::new(envelope2, "node_a".to_string(), "node_b".to_string(), 2);
581
+ node_a_transport_v2.send(msg2).await.expect("send tx2");
601
582
 
602
583
  sleep(Duration::from_millis(200)).await;
603
584
 
604
- let received2 = node_b_transport
605
- .receive()
606
- .await
607
- .expect("receive tx2");
585
+ let received2 = node_b_transport.receive().await.expect("receive tx2");
608
586
  assert_eq!(received2.len(), 1);
609
587
 
610
588
  let mut parent_vc_b_tx2 = VectorClock::new();
611
589
  parent_vc_b_tx2.increment("node_a");
612
590
  parent_vc_b_tx2.increment("node_b");
613
- let parent_version_b_tx2 = StateVersion::new(parent_vc_b_tx2, state_b_after_tx1.state_hash.to_string());
591
+ let parent_version_b_tx2 =
592
+ StateVersion::new(parent_vc_b_tx2, state_b_after_tx1.state_hash.to_string());
614
593
 
615
594
  node_b_executor
616
595
  .receive_replicated_transaction(received2[0].clone(), parent_version_b_tx2)
@@ -637,10 +616,14 @@ mod tests {
637
616
  state_a_final.operations_applied >= 2,
638
617
  "Should have applied at least 2 transactions"
639
618
  );
640
- println!("✓ Final convergence verified: {} == {}",
641
- state_a_final.state_hash,
642
- state_b_final.state_hash);
643
- println!("✓ Operations applied: {}\n", state_a_final.operations_applied);
619
+ println!(
620
+ "✓ Final convergence verified: {} == {}",
621
+ state_a_final.state_hash, state_b_final.state_hash
622
+ );
623
+ println!(
624
+ "✓ Operations applied: {}\n",
625
+ state_a_final.operations_applied
626
+ );
644
627
 
645
628
  // Verify metrics
646
629
  assert!(
@@ -677,18 +660,14 @@ mod tests {
677
660
  println!("PHASE 1: Initial setup and first convergence\n");
678
661
 
679
662
  // Node A (Server)
680
- let mut node_a_executor = DistributedTransactionExecutor::new(
681
- "node_a".to_string(),
682
- initial_state_hash.clone(),
683
- );
663
+ let mut node_a_executor =
664
+ DistributedTransactionExecutor::new("node_a".to_string(), initial_state_hash.clone());
684
665
  node_a_executor.register_replica("node_b".to_string(), initial_state_hash.clone());
685
666
  let mut node_a_transport = TcpTransport::new("127.0.0.1:19060".to_string());
686
667
 
687
668
  // Node B (Client) - will be restarted later
688
- let mut node_b_executor = DistributedTransactionExecutor::new(
689
- "node_b".to_string(),
690
- initial_state_hash.clone(),
691
- );
669
+ let mut node_b_executor =
670
+ DistributedTransactionExecutor::new("node_b".to_string(), initial_state_hash.clone());
692
671
  node_b_executor.register_replica("node_a".to_string(), initial_state_hash.clone());
693
672
  let mut node_b_transport = TcpTransport::new("127.0.0.1:19061".to_string());
694
673
 
@@ -716,7 +695,10 @@ mod tests {
716
695
  sleep(Duration::from_millis(50)).await;
717
696
  attempts += 1;
718
697
  }
719
- assert!(node_a_transport.is_connected(), "Server should be connected");
698
+ assert!(
699
+ node_a_transport.is_connected(),
700
+ "Server should be connected"
701
+ );
720
702
  println!("✓ Connection established\n");
721
703
 
722
704
  // Execute and replicate tx1
@@ -753,19 +735,14 @@ mod tests {
753
735
  println!("✓ Transaction 1 executed\n");
754
736
 
755
737
  println!("Step 3: Replicate transaction 1 to Node B...");
756
- let msg1 = ReplicationMessage::new(envelope1, "node_a".to_string(), "node_b".to_string(), 1);
757
- node_a_transport
758
- .send(msg1)
759
- .await
760
- .expect("send tx1");
738
+ let msg1 =
739
+ ReplicationMessage::new(envelope1, "node_a".to_string(), "node_b".to_string(), 1);
740
+ node_a_transport.send(msg1).await.expect("send tx1");
761
741
 
762
742
  sleep(Duration::from_millis(100)).await;
763
743
 
764
744
  // Receive and apply on B
765
- let received = node_b_transport
766
- .receive()
767
- .await
768
- .expect("receive tx1");
745
+ let received = node_b_transport.receive().await.expect("receive tx1");
769
746
  assert_eq!(received.len(), 1, "should receive 1 message");
770
747
 
771
748
  let parent_vc = VectorClock::new();
@@ -804,7 +781,10 @@ mod tests {
804
781
  state_b_before.operations_applied, 1,
805
782
  "node_b should have applied 1 operation"
806
783
  );
807
- println!("✓ State converged: {} == {}\n", state_a_before.state_hash, state_b_before.state_hash);
784
+ println!(
785
+ "✓ State converged: {} == {}\n",
786
+ state_a_before.state_hash, state_b_before.state_hash
787
+ );
808
788
 
809
789
  // ===== PHASE 2: Node B Offline (Restart Simulation) =====
810
790
  println!("PHASE 2: Node B goes offline, A continues\n");
@@ -816,8 +796,10 @@ mod tests {
816
796
  state_b_before.operations_applied,
817
797
  state_b_before.vector_clock.clone(),
818
798
  );
819
- println!("✓ Persisted Node B state: ops={}, hash={}\n",
820
- persisted_state_b.1, persisted_state_b.0);
799
+ println!(
800
+ "✓ Persisted Node B state: ops={}, hash={}\n",
801
+ persisted_state_b.1, persisted_state_b.0
802
+ );
821
803
 
822
804
  // Drop node_b transport/executor to simulate shutdown
823
805
  println!("Step 6: STOPPING Node B (complete offline)...");
@@ -861,27 +843,29 @@ mod tests {
861
843
  let state_a_before_restart = node_a_executor
862
844
  .get_replica_state("node_a")
863
845
  .expect("get node_a state");
864
- println!("\n✓ Node A advanced to: ops={}, hash={}\n",
865
- state_a_before_restart.operations_applied,
866
- state_a_before_restart.state_hash);
846
+ println!(
847
+ "\n✓ Node A advanced to: ops={}, hash={}\n",
848
+ state_a_before_restart.operations_applied, state_a_before_restart.state_hash
849
+ );
867
850
 
868
851
  // ===== PHASE 3: Node B Restart & Reconnection =====
869
852
  println!("PHASE 3: Restart Node B from persisted state\n");
870
853
 
871
854
  println!("Step 8: RESTARTING Node B from persisted state...");
872
855
  // Recreate node_b_executor with persisted state
873
- let mut node_b_executor_restarted = DistributedTransactionExecutor::new(
874
- "node_b".to_string(),
875
- persisted_state_b.0.clone(),
876
- );
877
- node_b_executor_restarted.register_replica("node_a".to_string(), persisted_state_b.0.clone());
856
+ let mut node_b_executor_restarted =
857
+ DistributedTransactionExecutor::new("node_b".to_string(), persisted_state_b.0.clone());
858
+ node_b_executor_restarted
859
+ .register_replica("node_a".to_string(), persisted_state_b.0.clone());
878
860
  let mut node_b_transport_restarted = TcpTransport::new("127.0.0.1:0".to_string());
879
861
 
880
862
  // Recreate Node A transport v2 to accept new connection (fresh listen)
881
863
  let mut node_a_transport_v2 = TcpTransport::new("127.0.0.1:19063".to_string());
882
864
 
883
- println!("✓ Node B restarted with persisted state: ops={}, hash={}\n",
884
- persisted_state_b.1, persisted_state_b.0);
865
+ println!(
866
+ "✓ Node B restarted with persisted state: ops={}, hash={}\n",
867
+ persisted_state_b.1, persisted_state_b.0
868
+ );
885
869
 
886
870
  println!("Step 9: Attempting reconnect to Node A (new port 19063)...");
887
871
 
@@ -897,8 +881,9 @@ mod tests {
897
881
 
898
882
  let reconnect_result = tokio::time::timeout(
899
883
  Duration::from_millis(500),
900
- node_b_transport_restarted.connect("127.0.0.1:19063")
901
- ).await;
884
+ node_b_transport_restarted.connect("127.0.0.1:19063"),
885
+ )
886
+ .await;
902
887
 
903
888
  match reconnect_result {
904
889
  Ok(Ok(())) => {
@@ -920,11 +905,8 @@ mod tests {
920
905
 
921
906
  // Try to receive any messages from Node A
922
907
  let timeout = Duration::from_millis(500);
923
- let received_after_restart = tokio::time::timeout(
924
- timeout,
925
- node_b_transport_restarted.receive()
926
- )
927
- .await;
908
+ let received_after_restart =
909
+ tokio::time::timeout(timeout, node_b_transport_restarted.receive()).await;
928
910
 
929
911
  let recovery_result = match received_after_restart {
930
912
  Ok(Ok(msgs)) => {
@@ -934,8 +916,10 @@ mod tests {
934
916
  } else {
935
917
  println!(" → Received {} message(s) from Node A", msgs.len());
936
918
  for msg in &msgs {
937
- println!(" Message from {} to {} (seq {})",
938
- msg.from_node, msg.to_node, msg.sequence_number);
919
+ println!(
920
+ " Message from {} to {} (seq {})",
921
+ msg.from_node, msg.to_node, msg.sequence_number
922
+ );
939
923
  }
940
924
  "received_messages"
941
925
  }
@@ -945,7 +929,10 @@ mod tests {
945
929
  "transport_error"
946
930
  }
947
931
  Err(_) => {
948
- println!(" → Timeout: No messages received within {} ms", timeout.as_millis());
932
+ println!(
933
+ " → Timeout: No messages received within {} ms",
934
+ timeout.as_millis()
935
+ );
949
936
  "timeout"
950
937
  }
951
938
  };
@@ -978,7 +965,10 @@ mod tests {
978
965
  if converged && state_a_final.operations_applied == state_b_final.operations_applied {
979
966
  println!("✅ OUTCOME A: Protocol has built-in recovery semantics");
980
967
  println!(" - Node B recovered from offline state");
981
- println!(" - All {} transactions replicated and applied", state_a_final.operations_applied);
968
+ println!(
969
+ " - All {} transactions replicated and applied",
970
+ state_a_final.operations_applied
971
+ );
982
972
  println!(" - Final state converged");
983
973
  println!(" - No duplicates detected");
984
974
  println!(" FINDING: 14C handles crash recovery internally");
@@ -987,22 +977,35 @@ mod tests {
987
977
  println!(" - Node B re-applied tx1 after restart");
988
978
  println!(" - Missing tx2, tx3, tx4");
989
979
  println!(" - State diverged");
990
- println!(" FINDING: Protocol lacks durability across restart; dedup is in-memory only");
980
+ println!(
981
+ " FINDING: Protocol lacks durability across restart; dedup is in-memory only"
982
+ );
991
983
  } else if behind {
992
984
  println!("⚠️ OUTCOME B: Protocol lacks recovery mechanism");
993
985
  println!(" - Node B restarted with 1 operation");
994
- println!(" - Node A has {} operations", state_a_final.operations_applied);
986
+ println!(
987
+ " - Node A has {} operations",
988
+ state_a_final.operations_applied
989
+ );
995
990
  println!(" - No recovery messages received");
996
- println!(" - State diverged: B={} vs A={}",
997
- state_b_final.state_hash, state_a_final.state_hash);
991
+ println!(
992
+ " - State diverged: B={} vs A={}",
993
+ state_b_final.state_hash, state_a_final.state_hash
994
+ );
998
995
  println!(" FINDING: Recovery mechanism not built into 14C protocol");
999
- println!(" Must be added in application layer or as separate protocol extension");
996
+ println!(
997
+ " Must be added in application layer or as separate protocol extension"
998
+ );
1000
999
  } else {
1001
1000
  println!("🔍 OUTCOME D: Unexpected state");
1002
- println!(" B ops: {} | A ops: {}",
1003
- state_b_final.operations_applied, state_a_final.operations_applied);
1004
- println!(" B hash: {} | A hash: {}",
1005
- state_b_final.state_hash, state_a_final.state_hash);
1001
+ println!(
1002
+ " B ops: {} | A ops: {}",
1003
+ state_b_final.operations_applied, state_a_final.operations_applied
1004
+ );
1005
+ println!(
1006
+ " B hash: {} | A hash: {}",
1007
+ state_b_final.state_hash, state_a_final.state_hash
1008
+ );
1006
1009
  }
1007
1010
 
1008
1011
  println!("\n📋 Summary:");
@@ -1024,16 +1027,12 @@ mod tests {
1024
1027
  let initial_state_hash = StateHash::from_hex("hash_initial".to_string());
1025
1028
 
1026
1029
  // Setup two nodes and converge
1027
- let mut executor_a = DistributedTransactionExecutor::new(
1028
- "node_a".to_string(),
1029
- initial_state_hash.clone(),
1030
- );
1030
+ let mut executor_a =
1031
+ DistributedTransactionExecutor::new("node_a".to_string(), initial_state_hash.clone());
1031
1032
  executor_a.register_replica("node_b".to_string(), initial_state_hash.clone());
1032
1033
 
1033
- let mut executor_b = DistributedTransactionExecutor::new(
1034
- "node_b".to_string(),
1035
- initial_state_hash.clone(),
1036
- );
1034
+ let mut executor_b =
1035
+ DistributedTransactionExecutor::new("node_b".to_string(), initial_state_hash.clone());
1037
1036
  executor_b.register_replica("node_a".to_string(), initial_state_hash.clone());
1038
1037
 
1039
1038
  // Execute and replicate multiple transactions
@@ -1088,16 +1087,28 @@ mod tests {
1088
1087
  let state_b = executor_b.get_replica_state("node_b").unwrap();
1089
1088
 
1090
1089
  println!("After 3 transactions:");
1091
- println!(" Node A: {} ops, hash={}", state_a.operations_applied, state_a.state_hash);
1092
- println!(" Node B: {} ops, hash={}", state_b.operations_applied, state_b.state_hash);
1090
+ println!(
1091
+ " Node A: {} ops, hash={}",
1092
+ state_a.operations_applied, state_a.state_hash
1093
+ );
1094
+ println!(
1095
+ " Node B: {} ops, hash={}",
1096
+ state_b.operations_applied, state_b.state_hash
1097
+ );
1093
1098
 
1094
1099
  // Analyze what would need to be persisted
1095
1100
  println!("\nCandidate durable state:");
1096
1101
  println!(" ✓ Operations (records of what was executed)");
1097
1102
  println!(" - Sequence numbers (1, 2, 3)");
1098
1103
  println!(" - Transaction IDs (tx_1, tx_2, tx_3)");
1099
- println!(" ✓ Last applied sequence number: {}", state_a.operations_applied);
1100
- println!(" ? State hash: {} (derived from ops, or durable?)", state_a.state_hash);
1104
+ println!(
1105
+ " Last applied sequence number: {}",
1106
+ state_a.operations_applied
1107
+ );
1108
+ println!(
1109
+ " ? State hash: {} (derived from ops, or durable?)",
1110
+ state_a.state_hash
1111
+ );
1101
1112
  println!(" ? Vector clocks: (needed for causal order?)");
1102
1113
  println!(" ? EnvelopeId set (for dedup: which envelopes seen?)");
1103
1114
 
@@ -1126,17 +1137,15 @@ mod tests {
1126
1137
 
1127
1138
  let initial_state_hash = StateHash::from_hex("hash_initial".to_string());
1128
1139
 
1129
- let mut executor = DistributedTransactionExecutor::new(
1130
- "node_b".to_string(),
1131
- initial_state_hash.clone(),
1132
- );
1140
+ let mut executor =
1141
+ DistributedTransactionExecutor::new("node_b".to_string(), initial_state_hash.clone());
1133
1142
 
1134
1143
  // Simulate: "Here's what we persisted before going offline"
1135
1144
  let persisted_data = (
1136
- "node_b", // node_id
1137
- initial_state_hash.clone(), // last_state_hash
1138
- 2u64, // last_sequence_applied
1139
- vec!["tx_1", "tx_2"], // operation ids
1145
+ "node_b", // node_id
1146
+ initial_state_hash.clone(), // last_state_hash
1147
+ 2u64, // last_sequence_applied
1148
+ vec!["tx_1", "tx_2"], // operation ids
1140
1149
  );
1141
1150
 
1142
1151
  println!("Persisted state:");
@@ -1341,7 +1350,9 @@ mod tests {
1341
1350
  println!("Q5: Recovery + 14C interaction?");
1342
1351
  println!(" → Must maintain causal order; sequence recovery before new replication\n");
1343
1352
 
1344
- println!("Next: Phase 1b.5 will implement minimal recovery protocol based on these answers.");
1353
+ println!(
1354
+ "Next: Phase 1b.5 will implement minimal recovery protocol based on these answers."
1355
+ );
1345
1356
  println!("Not before: Must understand what recovery must guarantee.\n");
1346
1357
  }
1347
1358
 
@@ -1402,7 +1413,10 @@ mod tests {
1402
1413
 
1403
1414
  let (seq_a_initial, ops_a_initial) = recovery_a.recovery_state_snapshot();
1404
1415
  let (seq_b_initial, ops_b_initial) = recovery_b.recovery_state_snapshot();
1405
- assert_eq!(seq_a_initial, seq_b_initial, "A and B should converge on tx1");
1416
+ assert_eq!(
1417
+ seq_a_initial, seq_b_initial,
1418
+ "A and B should converge on tx1"
1419
+ );
1406
1420
  assert_eq!(ops_a_initial, ops_b_initial, "Same op count");
1407
1421
 
1408
1422
  // Simulate offline: B stops, A executes tx2
@@ -1436,11 +1450,15 @@ mod tests {
1436
1450
  "Same operation count"
1437
1451
  );
1438
1452
 
1453
+ println!("✓ Invariant proven: continuous ≡ offline + recovery");
1454
+ println!(
1455
+ "✓ A sequence: {}, B sequence after recovery: {}",
1456
+ seq_a_after_tx2, seq_b_after_recovery
1457
+ );
1439
1458
  println!(
1440
- "✓ Invariant proven: continuous offline + recovery"
1459
+ "✓ A operations: {}, B operations: {}",
1460
+ ops_a_after_tx2, ops_b_after_recovery
1441
1461
  );
1442
- println!("✓ A sequence: {}, B sequence after recovery: {}", seq_a_after_tx2, seq_b_after_recovery);
1443
- println!("✓ A operations: {}, B operations: {}", ops_a_after_tx2, ops_b_after_recovery);
1444
1462
  }
1445
1463
 
1446
1464
  #[test]
@@ -1487,25 +1505,15 @@ mod tests {
1487
1505
  seq_a_final, seq_b_final,
1488
1506
  "Multiple recovered operations preserve convergence"
1489
1507
  );
1490
- assert_eq!(
1491
- ops_a_final, ops_b_final,
1492
- "Same operation count"
1493
- );
1508
+ assert_eq!(ops_a_final, ops_b_final, "Same operation count");
1494
1509
 
1510
+ println!("✓ Invariant: continuous ≡ offline + recovery (4 operations)");
1495
1511
  println!(
1496
- "✓ Invariant: continuous offline + recovery (4 operations)"
1497
- );
1498
- println!("✓ A operations: {}, B recovered: {}", ops_a_final, ops_b_final);
1499
- assert_eq!(
1500
- ops_a_final,
1501
- 4,
1502
- "A should have 4 operations"
1503
- );
1504
- assert_eq!(
1505
- ops_b_final,
1506
- 4,
1507
- "B should have 4 operations after recovery"
1512
+ "✓ A operations: {}, B recovered: {}",
1513
+ ops_a_final, ops_b_final
1508
1514
  );
1515
+ assert_eq!(ops_a_final, 4, "A should have 4 operations");
1516
+ assert_eq!(ops_b_final, 4, "B should have 4 operations after recovery");
1509
1517
  }
1510
1518
 
1511
1519
  #[test]
@@ -1549,10 +1557,18 @@ mod tests {
1549
1557
  }
1550
1558
 
1551
1559
  // Verify: dedup prevented double-apply
1552
- assert_eq!(recovery_b.operation_log.len(), 4, "B should have 4 ops, not 5");
1560
+ assert_eq!(
1561
+ recovery_b.operation_log.len(),
1562
+ 4,
1563
+ "B should have 4 ops, not 5"
1564
+ );
1553
1565
 
1554
1566
  println!("✓ Recovery maintains sequence integrity");
1555
- println!("✓ A operations: {}, B after recovery: {}", recovery_a.operation_log.len(), recovery_b.operation_log.len());
1567
+ println!(
1568
+ "✓ A operations: {}, B after recovery: {}",
1569
+ recovery_a.operation_log.len(),
1570
+ recovery_b.operation_log.len()
1571
+ );
1556
1572
  }
1557
1573
 
1558
1574
  #[test]
@@ -1583,21 +1599,35 @@ mod tests {
1583
1599
 
1584
1600
  // Recovery begins: B requests tx3, tx4, tx5
1585
1601
  let recovered_ops = recovery_a.get_operations_since(last_seq_b_start);
1586
- assert_eq!(recovered_ops.len(), 3, "Should need to recover 3 operations");
1602
+ assert_eq!(
1603
+ recovered_ops.len(),
1604
+ 3,
1605
+ "Should need to recover 3 operations"
1606
+ );
1587
1607
 
1588
1608
  // Simulate interruption: apply tx3 only, then B crashes
1589
1609
  recovery_b.log_operation("tx3".to_string(), 3);
1590
1610
  let (last_seq_b_after_partial, _) = recovery_b.recovery_state_snapshot();
1591
- assert_eq!(last_seq_b_after_partial, 3, "B should have seq 3 after partial recovery");
1611
+ assert_eq!(
1612
+ last_seq_b_after_partial, 3,
1613
+ "B should have seq 3 after partial recovery"
1614
+ );
1592
1615
 
1593
1616
  // B RESTARTS from persisted state
1594
1617
  recovery_b.restore_from_log();
1595
1618
  let (last_seq_b_after_crash, _) = recovery_b.recovery_state_snapshot();
1596
- assert_eq!(last_seq_b_after_crash, 3, "B should restore to seq 3 after crash");
1619
+ assert_eq!(
1620
+ last_seq_b_after_crash, 3,
1621
+ "B should restore to seq 3 after crash"
1622
+ );
1597
1623
 
1598
1624
  // Recovery resumes: B asks for operations after sequence 3
1599
1625
  let remaining_ops = recovery_a.get_operations_since(last_seq_b_after_crash);
1600
- assert_eq!(remaining_ops.len(), 2, "Should need to recover 2 remaining ops");
1626
+ assert_eq!(
1627
+ remaining_ops.len(),
1628
+ 2,
1629
+ "Should need to recover 2 remaining ops"
1630
+ );
1601
1631
 
1602
1632
  for seq in remaining_ops {
1603
1633
  recovery_b.log_operation(format!("tx{}", seq), seq);
@@ -1645,7 +1675,10 @@ mod tests {
1645
1675
  let (seq_a_diverged, ops_a_diverged) = recovery_a.recovery_state_snapshot();
1646
1676
  let (seq_b_offline, ops_b_offline) = recovery_b.recovery_state_snapshot();
1647
1677
 
1648
- println!(" A after divergence: seq={}, ops={}", seq_a_diverged, ops_a_diverged);
1678
+ println!(
1679
+ " A after divergence: seq={}, ops={}",
1680
+ seq_a_diverged, ops_a_diverged
1681
+ );
1649
1682
  println!(" B offline: seq={}, ops={}", seq_b_offline, ops_b_offline);
1650
1683
 
1651
1684
  // B restarts and recovers
@@ -1664,8 +1697,7 @@ mod tests {
1664
1697
  // INDEPENDENT VERIFICATION: Both nodes have same operations
1665
1698
  // Verify by independent state calculation
1666
1699
  assert_eq!(
1667
- ops_a_final,
1668
- ops_b_final,
1700
+ ops_a_final, ops_b_final,
1669
1701
  "Both should have same operation count"
1670
1702
  );
1671
1703
 
@@ -1679,7 +1711,8 @@ mod tests {
1679
1711
 
1680
1712
  // SEMANTIC EQUIVALENCE: Both should have identical state snapshot
1681
1713
  assert_eq!(
1682
- (seq_a_final, ops_a_final), (seq_b_final, ops_b_final),
1714
+ (seq_a_final, ops_a_final),
1715
+ (seq_b_final, ops_b_final),
1683
1716
  "Independent recovery produces identical state"
1684
1717
  );
1685
1718
 
@@ -1687,8 +1720,10 @@ mod tests {
1687
1720
  "✓ State verification passed: A=(seq={}, ops={}), B=(seq={}, ops={})",
1688
1721
  seq_a_final, ops_a_final, seq_b_final, ops_b_final
1689
1722
  );
1690
- println!("✓ Operation sequences identical: {} operations on both",
1691
- ops_a_final);
1723
+ println!(
1724
+ "✓ Operation sequences identical: {} operations on both",
1725
+ ops_a_final
1726
+ );
1692
1727
  println!("✓ Semantic equivalence proven: both nodes identical");
1693
1728
  }
1694
1729
 
@@ -1787,7 +1822,11 @@ mod tests {
1787
1822
  }
1788
1823
 
1789
1824
  fn get_recovery_state(&self) -> (u64, usize, usize) {
1790
- (self.last_sequence, self.operation_log.len(), self.dedup_set.len())
1825
+ (
1826
+ self.last_sequence,
1827
+ self.operation_log.len(),
1828
+ self.dedup_set.len(),
1829
+ )
1791
1830
  }
1792
1831
 
1793
1832
  fn simulate_crash_and_restart(&mut self) {
@@ -1799,7 +1838,11 @@ mod tests {
1799
1838
  self.dedup_set.clear();
1800
1839
  }
1801
1840
 
1802
- fn restore_from_persisted(&mut self, ops: Vec<(String, u64)>, dedup: std::collections::HashSet<String>) {
1841
+ fn restore_from_persisted(
1842
+ &mut self,
1843
+ ops: Vec<(String, u64)>,
1844
+ dedup: std::collections::HashSet<String>,
1845
+ ) {
1803
1846
  self.operation_log = ops;
1804
1847
  self.dedup_set = dedup;
1805
1848
  if let Some((_, seq)) = self.operation_log.last() {
@@ -1840,7 +1883,10 @@ mod tests {
1840
1883
  // Attack: Kill process B (SIGKILL, no graceful shutdown)
1841
1884
  recovery_b.simulate_crash_and_restart();
1842
1885
  let (seq_b_after_crash, _, _) = recovery_b.get_recovery_state();
1843
- assert_eq!(seq_b_after_crash, 0, "B's in-memory state should be cleared after crash");
1886
+ assert_eq!(
1887
+ seq_b_after_crash, 0,
1888
+ "B's in-memory state should be cleared after crash"
1889
+ );
1844
1890
 
1845
1891
  // Recover: New process B reads from disk
1846
1892
  recovery_b.restore_from_persisted(persisted_ops, persisted_dedup);
@@ -1850,11 +1896,20 @@ mod tests {
1850
1896
  // Verify: B recovered from disk, not from memory
1851
1897
  assert_eq!(seq_b_restored, 2, "B should restore sequence 2 from disk");
1852
1898
  assert_eq!(ops_b_restored, 2, "B should have 2 operations from disk");
1853
- assert_eq!(dedup_b_restored, 2, "B should have 2 dedup entries from disk");
1854
- assert_eq!(seq_b_before_crash, seq_b_restored, "State should survive disk read");
1899
+ assert_eq!(
1900
+ dedup_b_restored, 2,
1901
+ "B should have 2 dedup entries from disk"
1902
+ );
1903
+ assert_eq!(
1904
+ seq_b_before_crash, seq_b_restored,
1905
+ "State should survive disk read"
1906
+ );
1855
1907
 
1856
1908
  println!("✓ Process B crashed and restarted from disk");
1857
- println!("✓ Recovered state: seq={}, ops={}, dedup={}", seq_b_restored, ops_b_restored, dedup_b_restored);
1909
+ println!(
1910
+ "✓ Recovered state: seq={}, ops={}, dedup={}",
1911
+ seq_b_restored, ops_b_restored, dedup_b_restored
1912
+ );
1858
1913
  println!("✓ Invariant: Process crash does not lose persisted state");
1859
1914
  }
1860
1915
 
@@ -1895,11 +1950,18 @@ mod tests {
1895
1950
  let (seq_b_restarted, _, _) = recovery_b.get_recovery_state();
1896
1951
 
1897
1952
  // Verify: B is back to its pre-offline state (sequence 2)
1898
- assert_eq!(seq_b_restarted, 2, "B should restart with sequence 2 (pre-offline state)");
1953
+ assert_eq!(
1954
+ seq_b_restarted, 2,
1955
+ "B should restart with sequence 2 (pre-offline state)"
1956
+ );
1899
1957
 
1900
1958
  // B requests recovery from A for sequence > 2
1901
1959
  let missed = recovery_a.get_operations_since(seq_b_restarted);
1902
- assert_eq!(missed.len(), 2, "B should need to recover 2 operations (tx3, tx4)");
1960
+ assert_eq!(
1961
+ missed.len(),
1962
+ 2,
1963
+ "B should need to recover 2 operations (tx3, tx4)"
1964
+ );
1903
1965
 
1904
1966
  println!("✓ Process B restarted and recovered from disk (old state cleared)");
1905
1967
  println!("✓ B's sequence after restart: {}", seq_b_restarted);
@@ -2004,11 +2066,17 @@ mod tests {
2004
2066
 
2005
2067
  // B restarts (crashes)
2006
2068
  recovery_b.simulate_crash_and_restart();
2007
- assert!(!recovery_b.is_seen("A:2"), "Dedup set should be cleared after crash");
2069
+ assert!(
2070
+ !recovery_b.is_seen("A:2"),
2071
+ "Dedup set should be cleared after crash"
2072
+ );
2008
2073
 
2009
2074
  // B restarts from disk
2010
2075
  recovery_b.restore_from_persisted(persisted_ops, persisted_dedup);
2011
- assert!(recovery_b.is_seen("A:2"), "Dedup set should be restored from disk");
2076
+ assert!(
2077
+ recovery_b.is_seen("A:2"),
2078
+ "Dedup set should be restored from disk"
2079
+ );
2012
2080
 
2013
2081
  // Attack: A delivers tx2 again (network retry, didn't know B already had it)
2014
2082
  let tx2_envelope_id = "A:2";
@@ -2056,7 +2124,10 @@ mod tests {
2056
2124
 
2057
2125
  // Attack: Same recovery request again (network glitch, retry)
2058
2126
  let missed_second = recovery_a.get_operations_since(seq_b);
2059
- assert_eq!(missed_first, missed_second, "Same recovery request should return same ops");
2127
+ assert_eq!(
2128
+ missed_first, missed_second,
2129
+ "Same recovery request should return same ops"
2130
+ );
2060
2131
 
2061
2132
  // Apply same recovery again (idempotence)
2062
2133
  for seq in missed_second.iter() {
@@ -2073,10 +2144,19 @@ mod tests {
2073
2144
  assert_eq!(ops_b_1, ops_b_2, "Operation count unchanged");
2074
2145
  assert_eq!(dedup_b_1, dedup_b_2, "Dedup count unchanged");
2075
2146
 
2076
- println!("✓ First recovery request: {} operations", missed_first.len());
2077
- println!("✓ Second recovery request: {} operations", missed_second.len());
2147
+ println!(
2148
+ "✓ First recovery request: {} operations",
2149
+ missed_first.len()
2150
+ );
2151
+ println!(
2152
+ "✓ Second recovery request: {} operations",
2153
+ missed_second.len()
2154
+ );
2078
2155
  println!("✓ Dedup prevented re-application");
2079
- println!("✓ Final state: seq={}, ops={}, dedup={}", seq_b_idempotent, ops_b_2, dedup_b_2);
2156
+ println!(
2157
+ "✓ Final state: seq={}, ops={}, dedup={}",
2158
+ seq_b_idempotent, ops_b_2, dedup_b_2
2159
+ );
2080
2160
  println!("✓ Invariant: Recovery is idempotent (same request twice = same result)");
2081
2161
  }
2082
2162
 
@@ -2125,7 +2205,10 @@ mod tests {
2125
2205
  recovery_b.restore_from_persisted(persisted_ops, persisted_dedup);
2126
2206
 
2127
2207
  let (seq_b_restored, _, _) = recovery_b.get_recovery_state();
2128
- assert_eq!(seq_b_restored, 4, "B should restore recovery position at tx4");
2208
+ assert_eq!(
2209
+ seq_b_restored, 4,
2210
+ "B should restore recovery position at tx4"
2211
+ );
2129
2212
 
2130
2213
  // B requests recovery from sequence 4 (not 2, not 3)
2131
2214
  let remaining = recovery_a.get_operations_since(seq_b_restored);
@@ -2289,7 +2372,10 @@ mod tests {
2289
2372
  println!(" Success rate: {:.1}%", success_rate);
2290
2373
  println!(" Cycles completed: {}", self.cycles_completed);
2291
2374
  println!(" Convergence failures: {}", self.convergence_failures);
2292
- println!(" Crash recovery failures: {}", self.crash_recovery_failures);
2375
+ println!(
2376
+ " Crash recovery failures: {}",
2377
+ self.crash_recovery_failures
2378
+ );
2293
2379
  }
2294
2380
 
2295
2381
  println!("\nRECOVERY LATENCY (measured, not inferred)");
@@ -2301,7 +2387,10 @@ mod tests {
2301
2387
  println!(" Min: {}ms", min);
2302
2388
  println!(" Avg: {}ms", avg);
2303
2389
  println!(" Max: {}ms", max);
2304
- println!(" Samples: {}", self.recovery_latencies_ms.len());
2390
+ println!(
2391
+ " Samples: {}",
2392
+ self.recovery_latencies_ms.len()
2393
+ );
2305
2394
  }
2306
2395
 
2307
2396
  println!("\nREPLAY THROUGHPUT (measured)");
@@ -2328,14 +2417,17 @@ mod tests {
2328
2417
 
2329
2418
  println!("\nDEDUPLICATION STATE (measured)");
2330
2419
  if !self.dedup_set_sizes.is_empty() {
2331
- let avg: usize = self.dedup_set_sizes.iter().sum::<usize>()
2332
- / self.dedup_set_sizes.len();
2420
+ let avg: usize =
2421
+ self.dedup_set_sizes.iter().sum::<usize>() / self.dedup_set_sizes.len();
2333
2422
  let min = self.dedup_set_sizes.iter().min().unwrap_or(&0);
2334
2423
  let max = self.dedup_set_sizes.iter().max().unwrap_or(&0);
2335
2424
  println!(" Min entries: {}", min);
2336
2425
  println!(" Avg entries: {}", avg);
2337
2426
  println!(" Max entries: {}", max);
2338
- println!(" Growth characteristic: {:?}", if max > &min { "unbounded" } else { "stable" });
2427
+ println!(
2428
+ " Growth characteristic: {:?}",
2429
+ if max > &min { "unbounded" } else { "stable" }
2430
+ );
2339
2431
  }
2340
2432
 
2341
2433
  println!("\nOPERATION LOG (measured)");
@@ -2361,7 +2453,14 @@ mod tests {
2361
2453
 
2362
2454
  println!("\nOVERALL STATISTICS");
2363
2455
  println!(" Total operations: {}", self.total_operations);
2364
- println!(" Architectural failures: {}", if self.convergence_failures == 0 && self.crash_recovery_failures == 0 { 0 } else { 1 });
2456
+ println!(
2457
+ " Architectural failures: {}",
2458
+ if self.convergence_failures == 0 && self.crash_recovery_failures == 0 {
2459
+ 0
2460
+ } else {
2461
+ 1
2462
+ }
2463
+ );
2365
2464
  println!("\n=== END SOAK VALIDATION ===\n");
2366
2465
  }
2367
2466
  }
@@ -2416,7 +2515,10 @@ mod tests {
2416
2515
  if final_seq_b == 105 && final_ops_b == 105 {
2417
2516
  metrics.record_recovery(recovery_time, throughput, 100);
2418
2517
  metrics.record_dedup(final_dedup);
2419
- println!(" Cycle {}: ✓ converged (seq={}, latency={}ms)", cycle, final_seq_b, recovery_time);
2518
+ println!(
2519
+ " Cycle {}: ✓ converged (seq={}, latency={}ms)",
2520
+ cycle, final_seq_b, recovery_time
2521
+ );
2420
2522
  } else {
2421
2523
  metrics.record_convergence_failure();
2422
2524
  println!(" Cycle {}: ✗ FAILED to converge", cycle);
@@ -2551,7 +2653,10 @@ mod tests {
2551
2653
  if final_seq_b == 50 {
2552
2654
  metrics.record_recovery(recovery_time, 0, 45);
2553
2655
  metrics.record_dedup(dedup_size);
2554
- println!(" Cycle {}: ✓ recovered after crash (seq={})", cycle, final_seq_b);
2656
+ println!(
2657
+ " Cycle {}: ✓ recovered after crash (seq={})",
2658
+ cycle, final_seq_b
2659
+ );
2555
2660
  } else {
2556
2661
  metrics.record_crash_recovery_failure();
2557
2662
  println!(" Cycle {}: ✗ FAILED to recover after crash", cycle);
@@ -2627,11 +2732,7 @@ mod tests {
2627
2732
 
2628
2733
  // Execute operations
2629
2734
  for i in 1..=*op_count {
2630
- recovery.persist_operation(
2631
- format!("tx{}", i),
2632
- i as u64,
2633
- format!("A:{}", i),
2634
- );
2735
+ recovery.persist_operation(format!("tx{}", i), i as u64, format!("A:{}", i));
2635
2736
  }
2636
2737
 
2637
2738
  let startup_time = start.elapsed().as_millis();
@@ -2754,15 +2855,36 @@ mod tests {
2754
2855
  // Analyze backlog scaling
2755
2856
  println!("RECOVERY LATENCY SCALING");
2756
2857
  if !self.backlog_10_latency.is_empty() {
2757
- let avg_10: u128 = self.backlog_10_latency.iter().sum::<u128>() / self.backlog_10_latency.len() as u128;
2758
- let avg_100: u128 = self.backlog_100_latency.iter().sum::<u128>() /
2759
- if self.backlog_100_latency.is_empty() { 1 } else { self.backlog_100_latency.len() as u128 };
2760
- let avg_1k: u128 = self.backlog_1k_latency.iter().sum::<u128>() /
2761
- if self.backlog_1k_latency.is_empty() { 1 } else { self.backlog_1k_latency.len() as u128 };
2858
+ let avg_10: u128 = self.backlog_10_latency.iter().sum::<u128>()
2859
+ / self.backlog_10_latency.len() as u128;
2860
+ let avg_100: u128 = self.backlog_100_latency.iter().sum::<u128>()
2861
+ / if self.backlog_100_latency.is_empty() {
2862
+ 1
2863
+ } else {
2864
+ self.backlog_100_latency.len() as u128
2865
+ };
2866
+ let avg_1k: u128 = self.backlog_1k_latency.iter().sum::<u128>()
2867
+ / if self.backlog_1k_latency.is_empty() {
2868
+ 1
2869
+ } else {
2870
+ self.backlog_1k_latency.len() as u128
2871
+ };
2762
2872
 
2763
- println!(" 10 ops: {}ms (avg, {} samples)", avg_10, self.backlog_10_latency.len());
2764
- println!(" 100 ops: {}ms (avg, {} samples)", avg_100, self.backlog_100_latency.len());
2765
- println!(" 1,000 ops: {}ms (avg, {} samples)", avg_1k, self.backlog_1k_latency.len());
2873
+ println!(
2874
+ " 10 ops: {}ms (avg, {} samples)",
2875
+ avg_10,
2876
+ self.backlog_10_latency.len()
2877
+ );
2878
+ println!(
2879
+ " 100 ops: {}ms (avg, {} samples)",
2880
+ avg_100,
2881
+ self.backlog_100_latency.len()
2882
+ );
2883
+ println!(
2884
+ " 1,000 ops: {}ms (avg, {} samples)",
2885
+ avg_1k,
2886
+ self.backlog_1k_latency.len()
2887
+ );
2766
2888
 
2767
2889
  // Detect scaling characteristic
2768
2890
  if avg_1k > 0 && avg_10 > 0 {
@@ -2785,24 +2907,40 @@ mod tests {
2785
2907
  // Analyze throughput
2786
2908
  println!("\nRECOVERY REPLAY THROUGHPUT");
2787
2909
  if !self.backlog_10_throughput.is_empty() {
2788
- let avg_10: u64 = self.backlog_10_throughput.iter().sum::<u64>() / self.backlog_10_throughput.len() as u64;
2789
- let avg_100: u64 = self.backlog_100_throughput.iter().sum::<u64>() /
2790
- if self.backlog_100_throughput.is_empty() { 1 } else { self.backlog_100_throughput.len() as u64 };
2791
- let avg_1k: u64 = self.backlog_1k_throughput.iter().sum::<u64>() /
2792
- if self.backlog_1k_throughput.is_empty() { 1 } else { self.backlog_1k_throughput.len() as u64 };
2910
+ let avg_10: u64 = self.backlog_10_throughput.iter().sum::<u64>()
2911
+ / self.backlog_10_throughput.len() as u64;
2912
+ let avg_100: u64 = self.backlog_100_throughput.iter().sum::<u64>()
2913
+ / if self.backlog_100_throughput.is_empty() {
2914
+ 1
2915
+ } else {
2916
+ self.backlog_100_throughput.len() as u64
2917
+ };
2918
+ let avg_1k: u64 = self.backlog_1k_throughput.iter().sum::<u64>()
2919
+ / if self.backlog_1k_throughput.is_empty() {
2920
+ 1
2921
+ } else {
2922
+ self.backlog_1k_throughput.len() as u64
2923
+ };
2793
2924
 
2794
2925
  println!(" 10 ops: {} ops/sec", avg_10);
2795
2926
  println!(" 100 ops: {} ops/sec", avg_100);
2796
2927
  println!(" 1,000 ops: {} ops/sec", avg_1k);
2797
- println!(" Throughput trend: {}",
2798
- if avg_1k >= avg_100 { "stable" } else { "degrading" });
2928
+ println!(
2929
+ " Throughput trend: {}",
2930
+ if avg_1k >= avg_100 {
2931
+ "stable"
2932
+ } else {
2933
+ "degrading"
2934
+ }
2935
+ );
2799
2936
  }
2800
2937
 
2801
2938
  // Analyze startup/restore
2802
2939
  println!("\nSTARTUP / RESTORE SCALING");
2803
2940
  if !self.startup_times.is_empty() {
2804
2941
  let min_startup: u128 = *self.startup_times.iter().min().unwrap_or(&0);
2805
- let avg_startup: u128 = self.startup_times.iter().sum::<u128>() / self.startup_times.len() as u128;
2942
+ let avg_startup: u128 =
2943
+ self.startup_times.iter().sum::<u128>() / self.startup_times.len() as u128;
2806
2944
  let max_startup: u128 = *self.startup_times.iter().max().unwrap_or(&0);
2807
2945
 
2808
2946
  println!(" Min: {}ms", min_startup);
@@ -2829,22 +2967,33 @@ mod tests {
2829
2967
 
2830
2968
  println!(" Avg log size: {} bytes", avg_bytes);
2831
2969
  println!(" Bytes per operation: ~{}", bytes_per_op);
2832
- println!(" Linear growth: {}",
2833
- if self.log_bytes.len() > 1 { "observed" } else { "cannot determine" });
2970
+ println!(
2971
+ " Linear growth: {}",
2972
+ if self.log_bytes.len() > 1 {
2973
+ "observed"
2974
+ } else {
2975
+ "cannot determine"
2976
+ }
2977
+ );
2834
2978
  }
2835
2979
 
2836
2980
  // Analyze memory
2837
2981
  println!("\nMEMORY CHARACTERISTICS");
2838
2982
  if !self.dedup_sizes.is_empty() {
2839
2983
  let min_dedup: usize = *self.dedup_sizes.iter().min().unwrap_or(&0);
2840
- let avg_dedup: usize = self.dedup_sizes.iter().sum::<usize>() / self.dedup_sizes.len();
2984
+ let avg_dedup: usize =
2985
+ self.dedup_sizes.iter().sum::<usize>() / self.dedup_sizes.len();
2841
2986
  let max_dedup: usize = *self.dedup_sizes.iter().max().unwrap_or(&0);
2842
2987
 
2843
2988
  println!(" Dedup entries (min): {}", min_dedup);
2844
2989
  println!(" Dedup entries (avg): {}", avg_dedup);
2845
2990
  println!(" Dedup entries (max): {}", max_dedup);
2846
2991
 
2847
- let growth_ratio = if min_dedup > 0 { max_dedup as f64 / min_dedup as f64 } else { 0.0 };
2992
+ let growth_ratio = if min_dedup > 0 {
2993
+ max_dedup as f64 / min_dedup as f64
2994
+ } else {
2995
+ 0.0
2996
+ };
2848
2997
  println!(" Growth ratio: {:.2}x", growth_ratio);
2849
2998
 
2850
2999
  if growth_ratio > 10.0 {
@@ -2867,7 +3016,9 @@ mod tests {
2867
3016
  if self.convergence_failures == 0 {
2868
3017
  println!(" Assessment: ✅ PASSES (100% convergence)");
2869
3018
  } else {
2870
- println!(" Assessment: ⚠️ POTENTIAL ISSUE (not 100% convergence)");
3019
+ println!(
3020
+ " Assessment: ⚠️ POTENTIAL ISSUE (not 100% convergence)"
3021
+ );
2871
3022
  }
2872
3023
  }
2873
3024
 
@@ -2968,7 +3119,11 @@ mod tests {
2968
3119
  }
2969
3120
 
2970
3121
  let latency = start.elapsed().as_millis();
2971
- let throughput = if latency > 0 { (*backlog_size as u128 / latency) as u64 } else { 0 };
3122
+ let throughput = if latency > 0 {
3123
+ (*backlog_size as u128 / latency) as u64
3124
+ } else {
3125
+ 0
3126
+ };
2972
3127
 
2973
3128
  match backlog_size {
2974
3129
  10 => {
@@ -2992,7 +3147,10 @@ mod tests {
2992
3147
  evidence.convergence_failures += 1;
2993
3148
  }
2994
3149
 
2995
- println!(" {} ops: {}ms latency, {} ops/sec", backlog_size, latency, throughput);
3150
+ println!(
3151
+ " {} ops: {}ms latency, {} ops/sec",
3152
+ backlog_size, latency, throughput
3153
+ );
2996
3154
  }
2997
3155
 
2998
3156
  // Collect startup/resource data
@@ -3013,7 +3171,10 @@ mod tests {
3013
3171
  evidence.dedup_sizes.push(dedup_size);
3014
3172
  evidence.log_bytes.push(estimated_log as u64);
3015
3173
 
3016
- println!(" {} ops: {}ms startup, dedup={}, log≈{}B", op_count, startup_time, dedup_size, estimated_log);
3174
+ println!(
3175
+ " {} ops: {}ms startup, dedup={}, log≈{}B",
3176
+ op_count, startup_time, dedup_size, estimated_log
3177
+ );
3017
3178
  }
3018
3179
 
3019
3180
  // Produce analysis report
@@ -3039,7 +3200,9 @@ mod tests {
3039
3200
  println!("PREAMBLE");
3040
3201
  println!(" This contract is based on:");
3041
3202
  println!(" • Phase 1b.5: Recovery protocol architectural proof");
3042
- println!(" • Phase 1b.6: Recovery durability adversarial validation (7 attack scenarios)");
3203
+ println!(
3204
+ " • Phase 1b.6: Recovery durability adversarial validation (7 attack scenarios)"
3205
+ );
3043
3206
  println!(" • Phase 1b.7: Sustained soak validation under stress");
3044
3207
  println!(" • Phase 1b.8: Performance evidence analysis");
3045
3208
  println!("");
@@ -3144,7 +3307,9 @@ mod tests {
3144
3307
  println!(" NEXT DECISION:");
3145
3308
  println!(" Is FeltDB intended as:");
3146
3309
  println!(" A) Authoritative-primary with recoverable replicas (current 1b scope)");
3147
- println!(" B) Multi-master local-first distributed system (requires Phase 2 redesign)");
3310
+ println!(
3311
+ " B) Multi-master local-first distributed system (requires Phase 2 redesign)"
3312
+ );
3148
3313
  println!("");
3149
3314
  println!(" This contract is definitive FOR option A.");
3150
3315
  println!(" Option B requires new work on conflict resolution and consensus.\n");