@feltdb/core 0.13.0 → 0.16.0

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 (300) hide show
  1. package/dist/agent-runtime.d.ts +64 -2
  2. package/dist/agent-runtime.d.ts.map +1 -1
  3. package/dist/agent-runtime.js +121 -11
  4. package/dist/agent-state.d.ts +68 -0
  5. package/dist/agent-state.d.ts.map +1 -1
  6. package/dist/agent-state.js +210 -12
  7. package/dist/agent.d.ts +10 -1
  8. package/dist/agent.d.ts.map +1 -1
  9. package/dist/agent.js +10 -1
  10. package/dist/build-identity.json +12 -4
  11. package/dist/cli/commands.js +214 -10
  12. package/dist/cli/publish-engine.js +116 -16
  13. package/dist/collection.d.ts +3 -1
  14. package/dist/collection.d.ts.map +1 -1
  15. package/dist/collection.js +33 -8
  16. package/dist/create/build-identity.json +12 -4
  17. package/dist/create/cli.js +1 -1
  18. package/dist/create/create.js +4 -6
  19. package/dist/create/managed-account.js +93 -35
  20. package/dist/create/package-versions.js +1 -1
  21. package/dist/create/server-source/Cargo.lock +8 -511
  22. package/dist/create/server-source/Cargo.toml +2 -13
  23. package/dist/create/server-source/crates/feltdb-client/Cargo.toml +1 -1
  24. package/dist/create/server-source/crates/feltdb-server/Cargo.toml +1 -1
  25. package/dist/create/server-source/crates/feltdb-server/README.md +6 -1
  26. package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +9 -1
  27. package/dist/create/server-source/crates/feltdb-server/src/auth.rs +85 -25
  28. package/dist/create/server-source/crates/feltdb-server/src/backup.rs +4 -0
  29. package/dist/create/server-source/crates/feltdb-server/src/console_accounts.rs +1009 -0
  30. package/dist/create/server-source/crates/feltdb-server/src/control_authorization.rs +441 -0
  31. package/dist/create/server-source/crates/feltdb-server/src/identity.rs +68 -6
  32. package/dist/create/server-source/crates/feltdb-server/src/key_management.rs +355 -23
  33. package/dist/create/server-source/crates/feltdb-server/src/lib.rs +3 -0
  34. package/dist/create/server-source/crates/feltdb-server/src/main.rs +1134 -96
  35. package/dist/create/server-source/crates/feltdb-server/src/redaction.rs +208 -0
  36. package/dist/create/server-source/crates/feltdb-server/src/tenancy.rs +198 -19
  37. package/dist/create/server-source/crates/feltdb-server/tests/transaction_precondition_contract.rs +403 -0
  38. package/dist/create/server-source/crates/feltdb-wasm/Cargo.toml +1 -1
  39. package/dist/db.d.ts +75 -10
  40. package/dist/db.d.ts.map +1 -1
  41. package/dist/db.js +168 -29
  42. package/dist/deployment.d.ts +19 -0
  43. package/dist/deployment.d.ts.map +1 -1
  44. package/dist/deployment.js +38 -2
  45. package/dist/embedded-query.d.ts.map +1 -1
  46. package/dist/embedded-query.js +5 -4
  47. package/dist/embedded-transaction.d.ts +8 -4
  48. package/dist/embedded-transaction.d.ts.map +1 -1
  49. package/dist/embedded-transaction.js +55 -19
  50. package/dist/error-codes.d.ts +36 -2
  51. package/dist/error-codes.d.ts.map +1 -1
  52. package/dist/error-codes.js +50 -3
  53. package/dist/feltdb.d.ts +7 -0
  54. package/dist/feltdb.d.ts.map +1 -1
  55. package/dist/file-db.d.ts +27 -0
  56. package/dist/file-db.d.ts.map +1 -1
  57. package/dist/file-db.js +145 -7
  58. package/dist/http-client.d.ts +11 -3
  59. package/dist/http-client.d.ts.map +1 -1
  60. package/dist/http-client.js +27 -14
  61. package/dist/http-db.d.ts +43 -1
  62. package/dist/http-db.d.ts.map +1 -1
  63. package/dist/http-db.js +287 -59
  64. package/dist/index.js +1 -1
  65. package/dist/local-state-lock.d.ts +34 -0
  66. package/dist/local-state-lock.d.ts.map +1 -0
  67. package/dist/local-state-lock.js +225 -0
  68. package/dist/managed-recovery.d.ts +6 -0
  69. package/dist/managed-recovery.d.ts.map +1 -1
  70. package/dist/managed-recovery.js +9 -0
  71. package/dist/reactive-graph.d.ts +0 -3
  72. package/dist/reactive-graph.d.ts.map +1 -1
  73. package/dist/reactive-graph.js +11 -1
  74. package/dist/runtime-registry.d.ts +7 -2
  75. package/dist/runtime-registry.d.ts.map +1 -1
  76. package/dist/runtime-registry.js +2 -2
  77. package/dist/studio/components/index.js +1 -1
  78. package/dist/studio/{components-BSGmHfif.js → components-CJmzntBi.js} +2 -1
  79. package/dist/studio/index.js +1 -1
  80. package/dist/studio-app/assets/{feltdb_wasm-3hDngl1h.js → feltdb_wasm-DtybUY0q.js} +1 -1
  81. package/dist/studio-app/assets/feltdb_wasm_bg-DsgIUwaB.wasm +0 -0
  82. package/dist/studio-app/assets/index-C4OffsZl.js +37 -0
  83. package/dist/studio-app/index.html +1 -1
  84. package/dist/transaction.d.ts +27 -11
  85. package/dist/transaction.d.ts.map +1 -1
  86. package/dist/transaction.js +20 -14
  87. package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
  88. package/dist/wasm/package.json +1 -1
  89. package/dist/workload.d.ts +86 -6
  90. package/dist/workload.d.ts.map +1 -1
  91. package/dist/workload.js +51 -3
  92. package/dist/workspace/workspace-connection.d.ts.map +1 -1
  93. package/dist/workspace/workspace-connection.js +14 -3
  94. package/package.json +1 -1
  95. package/dist/create/server-source/crates/feltdb/Cargo.lock +0 -175
  96. package/dist/create/server-source/crates/feltdb/Cargo.toml +0 -102
  97. package/dist/create/server-source/crates/feltdb/LICENSE +0 -21
  98. package/dist/create/server-source/crates/feltdb/README.md +0 -75
  99. package/dist/create/server-source/crates/feltdb/benches/baselines/gate-13-redux.json +0 -370
  100. package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +0 -589
  101. package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +0 -247
  102. package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +0 -440
  103. package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +0 -275
  104. package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +0 -381
  105. package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +0 -467
  106. package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +0 -489
  107. package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +0 -281
  108. package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +0 -606
  109. package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +0 -531
  110. package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +0 -462
  111. package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +0 -505
  112. package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +0 -484
  113. package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +0 -758
  114. package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +0 -421
  115. package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +0 -329
  116. package/dist/create/server-source/crates/feltdb/src/acceptance_tests.rs +0 -1705
  117. package/dist/create/server-source/crates/feltdb/src/acquisition.rs +0 -286
  118. package/dist/create/server-source/crates/feltdb/src/admission.rs +0 -185
  119. package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +0 -507
  120. package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +0 -539
  121. package/dist/create/server-source/crates/feltdb/src/agent_state.rs +0 -320
  122. package/dist/create/server-source/crates/feltdb/src/agent_transition_contract.rs +0 -1545
  123. package/dist/create/server-source/crates/feltdb/src/analytics.rs +0 -521
  124. package/dist/create/server-source/crates/feltdb/src/application.rs +0 -2674
  125. package/dist/create/server-source/crates/feltdb/src/application_runtime.rs +0 -1070
  126. package/dist/create/server-source/crates/feltdb/src/authority.rs +0 -479
  127. package/dist/create/server-source/crates/feltdb/src/authority_failover.rs +0 -847
  128. package/dist/create/server-source/crates/feltdb/src/authorization.rs +0 -1030
  129. package/dist/create/server-source/crates/feltdb/src/authorization_security_tests.rs +0 -1122
  130. package/dist/create/server-source/crates/feltdb/src/bin/feltdb-authority-client.rs +0 -32
  131. package/dist/create/server-source/crates/feltdb/src/bin/feltdb-authority.rs +0 -19
  132. package/dist/create/server-source/crates/feltdb/src/bin/feltdb_node.rs +0 -1474
  133. package/dist/create/server-source/crates/feltdb/src/capabilities/mod.rs +0 -8
  134. package/dist/create/server-source/crates/feltdb/src/capabilities/search.rs +0 -418
  135. package/dist/create/server-source/crates/feltdb/src/capabilities/vector.rs +0 -482
  136. package/dist/create/server-source/crates/feltdb/src/capability.rs +0 -903
  137. package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +0 -263
  138. package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +0 -77
  139. package/dist/create/server-source/crates/feltdb/src/causal_backlog_bound.rs +0 -496
  140. package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +0 -2574
  141. package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +0 -212
  142. package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +0 -422
  143. package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +0 -445
  144. package/dist/create/server-source/crates/feltdb/src/content_distribution.rs +0 -465
  145. package/dist/create/server-source/crates/feltdb/src/convergence.rs +0 -634
  146. package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +0 -427
  147. package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +0 -376
  148. package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +0 -365
  149. package/dist/create/server-source/crates/feltdb/src/cron.rs +0 -294
  150. package/dist/create/server-source/crates/feltdb/src/dedup_bound_investigation.rs +0 -483
  151. package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +0 -477
  152. package/dist/create/server-source/crates/feltdb/src/distributed_tests.rs +0 -1003
  153. package/dist/create/server-source/crates/feltdb/src/distributed_transactions.rs +0 -1334
  154. package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +0 -368
  155. package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +0 -231
  156. package/dist/create/server-source/crates/feltdb/src/durable_operation_identity.rs +0 -482
  157. package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +0 -203
  158. package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +0 -317
  159. package/dist/create/server-source/crates/feltdb/src/equality_index.rs +0 -595
  160. package/dist/create/server-source/crates/feltdb/src/execution.rs +0 -426
  161. package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +0 -214
  162. package/dist/create/server-source/crates/feltdb/src/indexing.rs +0 -776
  163. package/dist/create/server-source/crates/feltdb/src/lib.rs +0 -7274
  164. package/dist/create/server-source/crates/feltdb/src/managed_cas_tests.rs +0 -234
  165. package/dist/create/server-source/crates/feltdb/src/materialization.rs +0 -184
  166. package/dist/create/server-source/crates/feltdb/src/metrics.rs +0 -266
  167. package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +0 -310
  168. package/dist/create/server-source/crates/feltdb/src/multi_operation_transaction.rs +0 -736
  169. package/dist/create/server-source/crates/feltdb/src/observability.rs +0 -379
  170. package/dist/create/server-source/crates/feltdb/src/operation.rs +0 -290
  171. package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +0 -439
  172. package/dist/create/server-source/crates/feltdb/src/operation_log.rs +0 -349
  173. package/dist/create/server-source/crates/feltdb/src/p1_application_atomicity.rs +0 -332
  174. package/dist/create/server-source/crates/feltdb/src/p1_atomicity_acceptance.rs +0 -718
  175. package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +0 -487
  176. package/dist/create/server-source/crates/feltdb/src/peer_registry.rs +0 -166
  177. package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +0 -289
  178. package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +0 -566
  179. package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +0 -3391
  180. package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +0 -203
  181. package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +0 -261
  182. package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +0 -502
  183. package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +0 -216
  184. package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +0 -303
  185. package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +0 -323
  186. package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +0 -531
  187. package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +0 -406
  188. package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +0 -436
  189. package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +0 -387
  190. package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +0 -378
  191. package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +0 -359
  192. package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +0 -372
  193. package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +0 -404
  194. package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +0 -376
  195. package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +0 -461
  196. package/dist/create/server-source/crates/feltdb/src/policy_evaluation.rs +0 -2125
  197. package/dist/create/server-source/crates/feltdb/src/production_api.rs +0 -418
  198. package/dist/create/server-source/crates/feltdb/src/provenance.rs +0 -207
  199. package/dist/create/server-source/crates/feltdb/src/query_execution_diagnostics.rs +0 -126
  200. package/dist/create/server-source/crates/feltdb/src/query_performance.rs +0 -218
  201. package/dist/create/server-source/crates/feltdb/src/read_basis.rs +0 -257
  202. package/dist/create/server-source/crates/feltdb/src/references.rs +0 -404
  203. package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +0 -401
  204. package/dist/create/server-source/crates/feltdb/src/replica_acknowledgements.rs +0 -501
  205. package/dist/create/server-source/crates/feltdb/src/replica_membership.rs +0 -680
  206. package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +0 -163
  207. package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +0 -133
  208. package/dist/create/server-source/crates/feltdb/src/routing.rs +0 -409
  209. package/dist/create/server-source/crates/feltdb/src/semantic_decision.rs +0 -2029
  210. package/dist/create/server-source/crates/feltdb/src/semantic_query.rs +0 -599
  211. package/dist/create/server-source/crates/feltdb/src/sharding.rs +0 -549
  212. package/dist/create/server-source/crates/feltdb/src/state_conflict_contract.rs +0 -516
  213. package/dist/create/server-source/crates/feltdb/src/state_contract.rs +0 -5894
  214. package/dist/create/server-source/crates/feltdb/src/state_diff_contract.rs +0 -222
  215. package/dist/create/server-source/crates/feltdb/src/state_facade.rs +0 -320
  216. package/dist/create/server-source/crates/feltdb/src/state_hash.rs +0 -291
  217. package/dist/create/server-source/crates/feltdb/src/state_model.rs +0 -4158
  218. package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +0 -379
  219. package/dist/create/server-source/crates/feltdb/src/state_trigger.rs +0 -673
  220. package/dist/create/server-source/crates/feltdb/src/storage.rs +0 -273
  221. package/dist/create/server-source/crates/feltdb/src/submission.rs +0 -471
  222. package/dist/create/server-source/crates/feltdb/src/sync.rs +0 -495
  223. package/dist/create/server-source/crates/feltdb/src/sync_contract.rs +0 -822
  224. package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +0 -433
  225. package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +0 -462
  226. package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +0 -965
  227. package/dist/create/server-source/crates/feltdb/src/transaction_preconditions.rs +0 -1120
  228. package/dist/create/server-source/crates/feltdb/src/transactions.rs +0 -643
  229. package/dist/create/server-source/crates/feltdb/src/trigger.rs +0 -390
  230. package/dist/create/server-source/crates/feltdb/src/trigger_contract.rs +0 -749
  231. package/dist/create/server-source/crates/feltdb/src/versioned_object_substrate.rs +0 -708
  232. package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +0 -1133
  233. package/dist/create/server-source/crates/feltdb/src/workflow.rs +0 -713
  234. package/dist/create/server-source/crates/feltdb/src/workflow_acceptance_tests.rs +0 -510
  235. package/dist/create/server-source/crates/feltdb/src/workflow_integration.rs +0 -354
  236. package/dist/create/server-source/crates/feltdb/src/workflow_runtime.rs +0 -594
  237. package/dist/create/server-source/crates/feltdb/src/workload.rs +0 -2886
  238. package/dist/create/server-source/crates/feltdb/src/workload_diagnostics.rs +0 -456
  239. package/dist/create/server-source/crates/feltdb/tests/agent_state_native.rs +0 -1188
  240. package/dist/create/server-source/crates/feltdb/tests/agent_transition_contract_conformance.rs +0 -2884
  241. package/dist/create/server-source/crates/feltdb/tests/authority_process.rs +0 -297
  242. package/dist/create/server-source/crates/feltdb/tests/authority_transaction_identity.rs +0 -534
  243. package/dist/create/server-source/crates/feltdb/tests/bounded_read_contract.rs +0 -132
  244. package/dist/create/server-source/crates/feltdb/tests/branching_evidence.rs +0 -299
  245. package/dist/create/server-source/crates/feltdb/tests/common/mod.rs +0 -96
  246. package/dist/create/server-source/crates/feltdb/tests/compaction_stall_contract.rs +0 -272
  247. package/dist/create/server-source/crates/feltdb/tests/crash_durability_contract.rs +0 -467
  248. package/dist/create/server-source/crates/feltdb/tests/current_revision_authority_evidence.rs +0 -309
  249. package/dist/create/server-source/crates/feltdb/tests/durable_backup_contract.rs +0 -445
  250. package/dist/create/server-source/crates/feltdb/tests/durable_corruption_contract.rs +0 -518
  251. package/dist/create/server-source/crates/feltdb/tests/durable_format_compatibility.rs +0 -392
  252. package/dist/create/server-source/crates/feltdb/tests/feltdb_state_boundary_tests.rs +0 -850
  253. package/dist/create/server-source/crates/feltdb/tests/fixtures/agent_transition_corpus.json +0 -23340
  254. package/dist/create/server-source/crates/feltdb/tests/fixtures/state_conflict_contract_corpus.json +0 -1916
  255. package/dist/create/server-source/crates/feltdb/tests/fixtures/state_diff_contract_corpus.json +0 -1878
  256. package/dist/create/server-source/crates/feltdb/tests/fixtures/trigger_contract_corpus.json +0 -1862
  257. package/dist/create/server-source/crates/feltdb/tests/generalized_version_control_evidence.rs +0 -123
  258. package/dist/create/server-source/crates/feltdb/tests/generalized_version_control_substrate_completion.rs +0 -299
  259. package/dist/create/server-source/crates/feltdb/tests/local_process_ownership.rs +0 -69
  260. package/dist/create/server-source/crates/feltdb/tests/managed_incident_regression.rs +0 -191
  261. package/dist/create/server-source/crates/feltdb/tests/operational_health_contract.rs +0 -278
  262. package/dist/create/server-source/crates/feltdb/tests/pr34_query_collection.rs +0 -234
  263. package/dist/create/server-source/crates/feltdb/tests/pr35_equality_index.rs +0 -947
  264. package/dist/create/server-source/crates/feltdb/tests/pr7_self_authorization_proof.rs +0 -404
  265. package/dist/create/server-source/crates/feltdb/tests/pr8_vocabulary_assessment.rs +0 -916
  266. package/dist/create/server-source/crates/feltdb/tests/pr9_phase2_boundary_tests.rs +0 -1045
  267. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3a_path_a_tests.rs +0 -347
  268. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_authorized_mutations.rs +0 -361
  269. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_role_based_authorization.rs +0 -330
  270. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_simple_auth_delete.rs +0 -93
  271. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_team_delete_role_authorization.rs +0 -622
  272. package/dist/create/server-source/crates/feltdb/tests/pr9_teams_role_based_access.rs +0 -517
  273. package/dist/create/server-source/crates/feltdb/tests/production_certification.rs +0 -1153
  274. package/dist/create/server-source/crates/feltdb/tests/production_contract.rs +0 -771
  275. package/dist/create/server-source/crates/feltdb/tests/production_readiness_contract.rs +0 -1383
  276. package/dist/create/server-source/crates/feltdb/tests/reconciliation_application.rs +0 -868
  277. package/dist/create/server-source/crates/feltdb/tests/reconciliation_wire_format_evidence.rs +0 -221
  278. package/dist/create/server-source/crates/feltdb/tests/replicated_history_contract.rs +0 -417
  279. package/dist/create/server-source/crates/feltdb/tests/resource_scoped_revisions.rs +0 -338
  280. package/dist/create/server-source/crates/feltdb/tests/revision_identity_contract.rs +0 -1039
  281. package/dist/create/server-source/crates/feltdb/tests/revision_model_decision.rs +0 -739
  282. package/dist/create/server-source/crates/feltdb/tests/revision_retention_boundary_evidence.rs +0 -452
  283. package/dist/create/server-source/crates/feltdb/tests/saas_authorization_integration.rs +0 -81
  284. package/dist/create/server-source/crates/feltdb/tests/saas_invitation_lifecycle.rs +0 -438
  285. package/dist/create/server-source/crates/feltdb/tests/semantic_decision.rs +0 -1165
  286. package/dist/create/server-source/crates/feltdb/tests/semantic_query.rs +0 -767
  287. package/dist/create/server-source/crates/feltdb/tests/semantic_query_contract.rs +0 -363
  288. package/dist/create/server-source/crates/feltdb/tests/state_conflict_contract_conformance.rs +0 -1799
  289. package/dist/create/server-source/crates/feltdb/tests/state_diff_contract_conformance.rs +0 -1316
  290. package/dist/create/server-source/crates/feltdb/tests/state_model_integration.rs +0 -358
  291. package/dist/create/server-source/crates/feltdb/tests/state_persistence_integration.rs +0 -365
  292. package/dist/create/server-source/crates/feltdb/tests/state_ref_authority.rs +0 -918
  293. package/dist/create/server-source/crates/feltdb/tests/state_store_boundary_evidence.rs +0 -299
  294. package/dist/create/server-source/crates/feltdb/tests/storage_independent_versioned_object_substrate.rs +0 -328
  295. package/dist/create/server-source/crates/feltdb/tests/sync_divergence_evidence.rs +0 -255
  296. package/dist/create/server-source/crates/feltdb/tests/three_way_input_boundary_evidence.rs +0 -249
  297. package/dist/create/server-source/crates/feltdb/tests/trigger_contract_conformance.rs +0 -994
  298. package/dist/create/server-source/crates/feltdb/tests/workload_envelope_contract.rs +0 -484
  299. package/dist/studio-app/assets/feltdb_wasm_bg-CFQT4gCy.wasm +0 -0
  300. package/dist/studio-app/assets/index-Dql1hS5m.js +0 -30
@@ -1,868 +0,0 @@
1
- //! Applying an explicit `ReconciliationPlan` to materialize a state.
2
- //!
3
- //! ```text
4
- //! Classification — which changes can coexist?
5
- //! Plan — what happens to the ones that cannot?
6
- //! Application — produce the resulting state from that decision.
7
- //! ```
8
- //!
9
- //! These tests establish the application semantics, that inputs are never
10
- //! mutated, that invalid plans are rejected rather than producing arbitrary
11
- //! state, and the whole causal path from three revisions to a materialized one.
12
- //!
13
- //! Nothing here advances a branch head, merges automatically, or reconciles
14
- //! anything FeltDB was not explicitly told to.
15
-
16
- use feltdb::state_model::{
17
- apply_reconciliation_plan, reconcile, ConflictClass, ConflictClassification, PathComponent,
18
- ReconciliationPlan, SemanticDiff, StateId, StateRevision, StateStore,
19
- RECONCILE_CONTENT_UNPARSEABLE, RECONCILE_OVERRIDE_PATHS_OVERLAP,
20
- RECONCILE_OVERRIDE_UNADDRESSABLE, RECONCILE_PLAN_INVALID, RECONCILE_REVISION_MISMATCH,
21
- };
22
- use serde_json::{json, Value};
23
-
24
- // ---------------------------------------------------------------------------
25
- // Helpers
26
- // ---------------------------------------------------------------------------
27
-
28
- /// A revision with fixed metadata, so tests compare states rather than clocks.
29
- fn revision(content: &str) -> StateRevision {
30
- StateRevision {
31
- id: StateId::compute(content),
32
- resource: "docs:1".into(),
33
- content: content.to_string(),
34
- content_id: StateId::compute(content),
35
- parent_id: None,
36
- sequence: 0,
37
- authority: "test".into(),
38
- timestamp_ms: 0,
39
- metadata: Default::default(),
40
- }
41
- }
42
-
43
- fn key(name: &str) -> PathComponent {
44
- PathComponent::Key(name.to_string())
45
- }
46
-
47
- const LEFT: bool = true;
48
- const RIGHT: bool = false;
49
-
50
- /// The three revisions a scenario compares, and a plan builder over them.
51
- struct Scenario {
52
- base: StateRevision,
53
- left: StateRevision,
54
- right: StateRevision,
55
- }
56
-
57
- impl Scenario {
58
- fn new(base: &str, left: &str, right: &str) -> Self {
59
- Self {
60
- base: revision(base),
61
- left: revision(left),
62
- right: revision(right),
63
- }
64
- }
65
-
66
- fn plan(&self, parent_choice: bool) -> ReconciliationPlan {
67
- ReconciliationPlan::new(
68
- self.left.id.clone(),
69
- self.right.id.clone(),
70
- self.base.id.clone(),
71
- parent_choice,
72
- )
73
- }
74
-
75
- fn apply(&self, plan: &ReconciliationPlan) -> Result<String, String> {
76
- apply_reconciliation_plan(&self.base, &self.left, &self.right, plan)
77
- }
78
-
79
- /// The applied result as a value, for comparing states rather than text.
80
- fn applied(&self, plan: &ReconciliationPlan) -> Value {
81
- serde_json::from_str(&self.apply(plan).expect("the plan applies")).unwrap()
82
- }
83
-
84
- fn classification(&self) -> ConflictClassification {
85
- ConflictClassification::classify(&self.base, &self.left, &self.right)
86
- }
87
- }
88
-
89
- /// The error code an application failed with.
90
- fn code(result: Result<String, String>) -> String {
91
- let message = result.expect_err("the plan must be rejected");
92
- message
93
- .split_once(':')
94
- .map(|(code, _)| code.to_string())
95
- .unwrap_or(message)
96
- }
97
-
98
- // ---------------------------------------------------------------------------
99
- // Parent choice
100
- // ---------------------------------------------------------------------------
101
-
102
- /// `parent_choice` selects the state to start from, and has exactly two values.
103
- ///
104
- /// The field is a `bool`, so there is no third choice and no "unknown parent
105
- /// choice" for application to reject — the type forecloses it. The ancestor is
106
- /// not selectable.
107
- #[test]
108
- fn parent_choice_selects_the_starting_state() {
109
- let scenario = Scenario::new(r#"{"a":1,"b":1}"#, r#"{"a":2,"b":1}"#, r#"{"a":1,"b":2}"#);
110
-
111
- assert_eq!(scenario.applied(&scenario.plan(LEFT)), json!({"a":2,"b":1}));
112
- assert_eq!(
113
- scenario.applied(&scenario.plan(RIGHT)),
114
- json!({"a":1,"b":2})
115
- );
116
-
117
- // With no overrides the result is exactly the selected branch's state, not
118
- // a merge of the two. Independent changes on the branch not selected are
119
- // not carried over: the plan did not ask for them.
120
- assert_ne!(
121
- scenario.applied(&scenario.plan(LEFT)),
122
- json!({"a":2,"b":2}),
123
- "application never merges on its own"
124
- );
125
- }
126
-
127
- // ---------------------------------------------------------------------------
128
- // Override semantics
129
- // ---------------------------------------------------------------------------
130
-
131
- #[test]
132
- fn an_override_replaces_a_value() {
133
- let scenario = Scenario::new(r#"{"a":1}"#, r#"{"a":2}"#, r#"{"a":3}"#);
134
- let plan = scenario.plan(LEFT).with_override(vec![key("a")], json!(9));
135
- assert_eq!(scenario.applied(&plan), json!({"a":9}));
136
- }
137
-
138
- #[test]
139
- fn an_override_may_add_a_member_to_an_existing_object() {
140
- // Assigning a new member creates no structure, so it is addressable. This
141
- // is how a plan takes a member the other branch added.
142
- let scenario = Scenario::new(r#"{"o":{}}"#, r#"{"o":{}}"#, r#"{"o":{"added":1}}"#);
143
- let plan = scenario
144
- .plan(LEFT)
145
- .with_override(vec![key("o"), key("added")], json!(1));
146
- assert_eq!(scenario.applied(&plan), json!({"o":{"added":1}}));
147
- }
148
-
149
- #[test]
150
- fn an_override_can_set_null_but_cannot_express_removal() {
151
- let scenario = Scenario::new(r#"{"a":1,"b":2}"#, r#"{"a":1,"b":2}"#, r#"{"a":1}"#);
152
-
153
- // Null is a value, and setting it is an ordinary override.
154
- let nulled = scenario
155
- .plan(LEFT)
156
- .with_override(vec![key("b")], Value::Null);
157
- assert_eq!(scenario.applied(&nulled), json!({"a":1,"b":null}));
158
-
159
- // `path_overrides` maps a path to a `Value`, and a `Value` cannot be
160
- // absent. Removing a member means overriding the container that holds it.
161
- let removed = scenario.plan(LEFT).with_override(vec![], json!({"a":1}));
162
- assert_eq!(scenario.applied(&removed), json!({"a":1}));
163
- }
164
-
165
- #[test]
166
- fn overrides_reach_nested_paths() {
167
- let scenario = Scenario::new(
168
- r#"{"o":{"m":{"deep":1}}}"#,
169
- r#"{"o":{"m":{"deep":2}}}"#,
170
- r#"{"o":{"m":{"deep":3}}}"#,
171
- );
172
- let plan = scenario
173
- .plan(LEFT)
174
- .with_override(vec![key("o"), key("m"), key("deep")], json!(9));
175
- assert_eq!(scenario.applied(&plan), json!({"o":{"m":{"deep":9}}}));
176
- }
177
-
178
- #[test]
179
- fn override_paths_are_structural_not_textual() {
180
- // `a.b` and `a.bc` share a string prefix and are different locations.
181
- let scenario = Scenario::new(
182
- r#"{"a":{"b":1,"bc":1}}"#,
183
- r#"{"a":{"b":2,"bc":1}}"#,
184
- r#"{"a":{"b":1,"bc":2}}"#,
185
- );
186
- let plan = scenario
187
- .plan(LEFT)
188
- .with_override(vec![key("a"), key("bc")], json!(9));
189
- assert_eq!(scenario.applied(&plan), json!({"a":{"b":2,"bc":9}}));
190
-
191
- // And a key named "0" is not the first element of an array.
192
- let mixed = Scenario::new(r#"{"x":{"0":1}}"#, r#"{"x":{"0":2}}"#, r#"{"x":{"0":3}}"#);
193
- let by_key = mixed
194
- .plan(LEFT)
195
- .with_override(vec![key("x"), key("0")], json!(9));
196
- assert_eq!(mixed.applied(&by_key), json!({"x":{"0":9}}));
197
- let by_index = mixed
198
- .plan(LEFT)
199
- .with_override(vec![key("x"), PathComponent::Index(0)], json!(9));
200
- assert_eq!(
201
- code(mixed.apply(&by_index)),
202
- RECONCILE_OVERRIDE_UNADDRESSABLE
203
- );
204
- }
205
-
206
- /// A repeated override path cannot exist: `path_overrides` is a map.
207
- #[test]
208
- fn a_repeated_override_path_is_impossible_by_construction() {
209
- let scenario = Scenario::new(r#"{"a":1}"#, r#"{"a":2}"#, r#"{"a":3}"#);
210
- let plan = scenario
211
- .plan(LEFT)
212
- .with_override(vec![key("a")], json!(8))
213
- .with_override(vec![key("a")], json!(9));
214
- assert_eq!(plan.path_overrides.len(), 1, "the map holds one entry");
215
- assert_eq!(scenario.applied(&plan), json!({"a":9}), "the later one");
216
- }
217
-
218
- // ---------------------------------------------------------------------------
219
- // Root path
220
- // ---------------------------------------------------------------------------
221
-
222
- #[test]
223
- fn the_root_path_replaces_the_whole_state() {
224
- let scenario = Scenario::new(r#"{"o":{"a":1}}"#, r#"{"o":7}"#, r#"{"o":{"a":2}}"#);
225
- for replacement in [
226
- json!({"whole":"object"}),
227
- json!([1, 2, 3]),
228
- json!("scalar"),
229
- json!(42),
230
- Value::Null,
231
- ] {
232
- let plan = scenario
233
- .plan(LEFT)
234
- .with_override(vec![], replacement.clone());
235
- assert_eq!(
236
- scenario.applied(&plan),
237
- replacement,
238
- "the empty path is the state itself"
239
- );
240
- }
241
- }
242
-
243
- /// A root override and any other override overlap, so the plan is ambiguous.
244
- #[test]
245
- fn a_root_override_excludes_every_other_override() {
246
- let scenario = Scenario::new(r#"{"a":1}"#, r#"{"a":2}"#, r#"{"a":3}"#);
247
- let plan = scenario
248
- .plan(LEFT)
249
- .with_override(vec![], json!({"a":8}))
250
- .with_override(vec![key("a")], json!(9));
251
- assert_eq!(
252
- code(scenario.apply(&plan)),
253
- RECONCILE_OVERRIDE_PATHS_OVERLAP
254
- );
255
- }
256
-
257
- // ---------------------------------------------------------------------------
258
- // Parent/child overrides
259
- // ---------------------------------------------------------------------------
260
-
261
- /// Overlapping override paths are rejected rather than given an invented
262
- /// precedence.
263
- ///
264
- /// The plan is a map from path to value with no stated ordering, so a plan
265
- /// holding both `o` and `o.a` does not say whether the inner override refines
266
- /// the outer value or the outer one was meant to stand. Rather than pick one,
267
- /// application refuses. Overlap is the same structural relation conflict
268
- /// classification uses.
269
- #[test]
270
- fn overlapping_override_paths_are_rejected() {
271
- let scenario = Scenario::new(r#"{"o":{"a":1}}"#, r#"{"o":{"a":2}}"#, r#"{"o":{"a":3}}"#);
272
- for (one, two) in [
273
- (vec![key("o")], vec![key("o"), key("a")]),
274
- (vec![key("o"), key("a")], vec![key("o")]),
275
- (vec![], vec![key("o")]),
276
- ] {
277
- let plan = scenario
278
- .plan(LEFT)
279
- .with_override(one, json!({"a": 8}))
280
- .with_override(two, json!(9));
281
- assert_eq!(
282
- code(scenario.apply(&plan)),
283
- RECONCILE_OVERRIDE_PATHS_OVERLAP
284
- );
285
- }
286
-
287
- // Disjoint paths under a shared, un-overridden parent are fine.
288
- let siblings = Scenario::new(
289
- r#"{"o":{"a":1,"b":1}}"#,
290
- r#"{"o":{"a":2,"b":1}}"#,
291
- r#"{"o":{"a":1,"b":2}}"#,
292
- );
293
- let plan = siblings
294
- .plan(LEFT)
295
- .with_override(vec![key("o"), key("a")], json!(8))
296
- .with_override(vec![key("o"), key("b")], json!(9));
297
- assert_eq!(siblings.applied(&plan), json!({"o":{"a":8,"b":9}}));
298
- }
299
-
300
- // ---------------------------------------------------------------------------
301
- // Arrays
302
- // ---------------------------------------------------------------------------
303
-
304
- #[test]
305
- fn array_overrides_are_positional() {
306
- let scenario = Scenario::new(
307
- r#"{"xs":[1,2,3]}"#,
308
- r#"{"xs":[9,2,3]}"#,
309
- r#"{"xs":[1,8,3]}"#,
310
- );
311
-
312
- // Replacing one element leaves the others alone.
313
- let replaced = scenario
314
- .plan(LEFT)
315
- .with_override(vec![key("xs"), PathComponent::Index(1)], json!(8));
316
- assert_eq!(scenario.applied(&replaced), json!({"xs":[9,8,3]}));
317
-
318
- // Two different indexes are disjoint paths, so both survive.
319
- let both = scenario
320
- .plan(LEFT)
321
- .with_override(vec![key("xs"), PathComponent::Index(0)], json!(7))
322
- .with_override(vec![key("xs"), PathComponent::Index(2)], json!(6));
323
- assert_eq!(scenario.applied(&both), json!({"xs":[7,2,6]}));
324
-
325
- // Length changes go through the array itself. There is no separate array
326
- // merge algorithm: the plan is authoritative.
327
- for (label, value) in [
328
- ("append", json!([1, 2, 3, 4])),
329
- ("remove", json!([1, 2])),
330
- ("prepend", json!([0, 1, 2, 3])),
331
- ("empty", json!([])),
332
- ("to an object", json!({"0":1})),
333
- ("to a scalar", json!("flat")),
334
- ] {
335
- let plan = scenario
336
- .plan(LEFT)
337
- .with_override(vec![key("xs")], value.clone());
338
- assert_eq!(
339
- scenario.applied(&plan),
340
- json!({ "xs": value }),
341
- "{label} is expressed by overriding the array"
342
- );
343
- }
344
- }
345
-
346
- #[test]
347
- fn an_index_past_the_end_is_rejected() {
348
- let scenario = Scenario::new(r#"{"xs":[1,2]}"#, r#"{"xs":[9,2]}"#, r#"{"xs":[1,8]}"#);
349
- for index in [2usize, 3, 99] {
350
- let plan = scenario
351
- .plan(LEFT)
352
- .with_override(vec![key("xs"), PathComponent::Index(index)], json!(0));
353
- assert_eq!(
354
- code(scenario.apply(&plan)),
355
- RECONCILE_OVERRIDE_UNADDRESSABLE,
356
- "index {index} would require inventing the elements before it"
357
- );
358
- }
359
- }
360
-
361
- #[test]
362
- fn overrides_reach_inside_array_elements() {
363
- let scenario = Scenario::new(
364
- r#"{"xs":[{"a":1},{"b":[1]}]}"#,
365
- r#"{"xs":[{"a":2},{"b":[1]}]}"#,
366
- r#"{"xs":[{"a":1},{"b":[2]}]}"#,
367
- );
368
- let plan = scenario.plan(LEFT).with_override(
369
- vec![
370
- key("xs"),
371
- PathComponent::Index(1),
372
- key("b"),
373
- PathComponent::Index(0),
374
- ],
375
- json!(9),
376
- );
377
- assert_eq!(scenario.applied(&plan), json!({"xs":[{"a":2},{"b":[9]}]}));
378
- }
379
-
380
- // ---------------------------------------------------------------------------
381
- // Canonical output
382
- // ---------------------------------------------------------------------------
383
-
384
- /// The result is canonical, whatever the selected parent's content looked like.
385
- ///
386
- /// Content identity is the hash of the content string, so two revisions holding
387
- /// the same state with differently ordered members are different revisions.
388
- /// Applying an equivalent plan to either produces the *same* canonical content,
389
- /// and therefore the same identity.
390
- #[test]
391
- fn the_result_is_canonical_regardless_of_the_parents_representation() {
392
- let sorted = Scenario::new(r#"{"a":1,"b":1}"#, r#"{"a":2,"b":1}"#, r#"{"a":1,"b":2}"#);
393
- let unsorted = Scenario::new(r#"{"b":1,"a":1}"#, r#"{"b":1,"a":2}"#, r#"{"a":1,"b":2}"#);
394
-
395
- assert_ne!(
396
- sorted.left.id, unsorted.left.id,
397
- "differently ordered content is a different revision"
398
- );
399
-
400
- let one = sorted.apply(&sorted.plan(LEFT)).unwrap();
401
- let two = unsorted.apply(&unsorted.plan(LEFT)).unwrap();
402
- assert_eq!(one, two, "both applications produce canonical content");
403
- assert_eq!(one, r#"{"a":2,"b":1}"#);
404
- assert_eq!(
405
- StateId::compute(&one),
406
- StateId::compute(&two),
407
- "so they have the same content identity"
408
- );
409
- }
410
-
411
- /// Repeated application produces the same bytes, and the result is a fixed
412
- /// point of the diff.
413
- #[test]
414
- fn application_is_repeatable_and_the_result_is_a_fixed_point() {
415
- let scenario = Scenario::new(
416
- r#"{"o":{"a":1},"xs":[1,2]}"#,
417
- r#"{"o":{"a":2},"xs":[9,2]}"#,
418
- r#"{"o":{"a":3},"xs":[1,8]}"#,
419
- );
420
- let plan = scenario
421
- .plan(LEFT)
422
- .with_override(vec![key("o"), key("a")], json!(5))
423
- .with_override(vec![key("xs"), PathComponent::Index(1)], json!(8));
424
-
425
- let first = scenario.apply(&plan).expect("applies");
426
- for _ in 0..8 {
427
- assert_eq!(scenario.apply(&plan).expect("applies"), first);
428
- }
429
-
430
- let result: Value = serde_json::from_str(&first).unwrap();
431
- assert!(
432
- SemanticDiff::compute(&result, &result).changes.is_empty(),
433
- "the result does not differ from itself"
434
- );
435
- }
436
-
437
- // ---------------------------------------------------------------------------
438
- // Immutability
439
- // ---------------------------------------------------------------------------
440
-
441
- /// Applying a plan mutates nothing it was given.
442
- #[test]
443
- fn application_mutates_none_of_its_inputs() {
444
- let scenario = Scenario::new(r#"{"o":{"a":1}}"#, r#"{"o":{"a":2}}"#, r#"{"o":{"a":3}}"#);
445
- let plan = scenario
446
- .plan(LEFT)
447
- .with_override(vec![key("o"), key("a")], json!(9));
448
-
449
- let before = (
450
- serde_json::to_string(&scenario.base).unwrap(),
451
- serde_json::to_string(&scenario.left).unwrap(),
452
- serde_json::to_string(&scenario.right).unwrap(),
453
- format!("{plan:?}"),
454
- );
455
-
456
- let result = reconcile(
457
- &scenario.base,
458
- &scenario.left,
459
- &scenario.right,
460
- &plan,
461
- "reconciler".into(),
462
- )
463
- .expect("applies");
464
-
465
- assert_eq!(serde_json::to_string(&scenario.base).unwrap(), before.0);
466
- assert_eq!(serde_json::to_string(&scenario.left).unwrap(), before.1);
467
- assert_eq!(serde_json::to_string(&scenario.right).unwrap(), before.2);
468
- assert_eq!(format!("{plan:?}"), before.3);
469
- assert_eq!(
470
- result.materialized_state.content, r#"{"o":{"a":9}}"#,
471
- "the result is newly materialized"
472
- );
473
- // And the result is a different revision from all three inputs.
474
- for existing in [&scenario.base, &scenario.left, &scenario.right] {
475
- assert_ne!(result.materialized_state.id, existing.id);
476
- }
477
- }
478
-
479
- // ---------------------------------------------------------------------------
480
- // Materialization
481
- // ---------------------------------------------------------------------------
482
-
483
- /// The materialized revision uses the existing identity and provenance model.
484
- #[test]
485
- fn the_materialized_revision_uses_the_existing_model() {
486
- let scenario = Scenario::new(r#"{"a":1}"#, r#"{"a":2}"#, r#"{"a":3}"#);
487
-
488
- for (choice, expected_parent) in [(LEFT, &scenario.left), (RIGHT, &scenario.right)] {
489
- let plan = scenario.plan(choice);
490
- let result = reconcile(
491
- &scenario.base,
492
- &scenario.left,
493
- &scenario.right,
494
- &plan,
495
- "reconciler".into(),
496
- )
497
- .expect("applies");
498
- let revision = &result.materialized_state;
499
-
500
- assert!(
501
- revision.verify_integrity(),
502
- "both identities match what they are computed from"
503
- );
504
- assert_eq!(
505
- revision.content_id,
506
- StateId::compute(&revision.content),
507
- "no new content-identity algorithm"
508
- );
509
- assert_ne!(
510
- revision.id, revision.content_id,
511
- "the revision identity names the occurrence, not the state"
512
- );
513
- assert_eq!(
514
- revision.parent_id.as_ref(),
515
- Some(&expected_parent.id),
516
- "the selected branch is the parent"
517
- );
518
- assert_eq!(revision.authority, "reconciler");
519
- // The plan travels with the result, so the decision stays attached to
520
- // the state it produced.
521
- assert_eq!(result.plan.parent_choice, choice);
522
- }
523
- }
524
-
525
- /// A materialized revision can be committed through the existing store, and
526
- /// nothing about branches moves.
527
- #[test]
528
- fn a_materialized_revision_commits_through_the_existing_store() {
529
- let store = StateStore::new_volatile();
530
- let base = store
531
- .create("docs:1", r#"{"a":1,"b":1}"#.to_string(), "auth".into())
532
- .expect("create");
533
- let left = store
534
- .commit(r#"{"a":2,"b":1}"#.to_string(), &base, "auth".into())
535
- .expect("left");
536
- let right = store
537
- .commit(r#"{"a":1,"b":2}"#.to_string(), &base, "auth".into())
538
- .expect("right");
539
-
540
- let plan = ReconciliationPlan::new(left.id.clone(), right.id.clone(), base.id.clone(), LEFT)
541
- .with_override(vec![key("b")], json!(2));
542
-
543
- let result = reconcile(&base, &left, &right, &plan, "reconciler".into()).expect("applies");
544
- let committed = store
545
- .commit(
546
- result.materialized_state.content.clone(),
547
- &left,
548
- "reconciler".into(),
549
- )
550
- .expect("commit");
551
-
552
- assert_eq!(committed.content, r#"{"a":2,"b":2}"#);
553
- assert_eq!(committed.id, result.materialized_state.id);
554
-
555
- // Applying a plan disturbed nothing it started from: both source revisions
556
- // are exactly as retrievable as before. There is no branch head to move,
557
- // because the store keeps none.
558
- assert_eq!(store.get(&left.id).unwrap().content, left.content);
559
- assert_eq!(store.get(&right.id).unwrap().content, right.content);
560
- }
561
-
562
- // ---------------------------------------------------------------------------
563
- // Rejection
564
- // ---------------------------------------------------------------------------
565
-
566
- #[test]
567
- fn an_invalid_plan_is_rejected_rather_than_producing_state() {
568
- let scenario = Scenario::new(r#"{"a":1}"#, r#"{"a":2}"#, r#"{"a":3}"#);
569
-
570
- // A plan whose ids are empty does not reference three revisions.
571
- let mut empty = scenario.plan(LEFT);
572
- empty.base_id = StateId::from_hex(String::new());
573
- assert_eq!(code(scenario.apply(&empty)), RECONCILE_PLAN_INVALID);
574
-
575
- // A plan that names revisions other than the ones supplied.
576
- let other = revision(r#"{"a":99}"#);
577
- for (base, left, right) in [
578
- (&other, &scenario.left, &scenario.right),
579
- (&scenario.base, &other, &scenario.right),
580
- (&scenario.base, &scenario.left, &other),
581
- ] {
582
- assert_eq!(
583
- code(apply_reconciliation_plan(
584
- base,
585
- left,
586
- right,
587
- &scenario.plan(LEFT)
588
- )),
589
- RECONCILE_REVISION_MISMATCH
590
- );
591
- }
592
-
593
- // Content that is not JSON. Classification reads such content as JSON null
594
- // because it only describes; materializing a state from it would be
595
- // producing arbitrary state, so this refuses.
596
- let broken = Scenario::new("{}", "not json", "{}");
597
- assert_eq!(
598
- code(broken.apply(&broken.plan(LEFT))),
599
- RECONCILE_CONTENT_UNPARSEABLE
600
- );
601
- // The branch that is not selected is not parsed, so it cannot fail the plan.
602
- assert_eq!(broken.applied(&broken.plan(RIGHT)), json!({}));
603
-
604
- // Override paths that name nothing assignable.
605
- let scenario = Scenario::new(r#"{"o":{"a":1}}"#, r#"{"o":7}"#, r#"{"o":{"a":2}}"#);
606
- for path in [
607
- vec![key("o"), key("a")], // `o` is a scalar in the left branch
608
- vec![key("missing"), key("deep")], // the prefix does not resolve
609
- vec![key("o"), PathComponent::Index(0)], // a scalar is not an array
610
- ] {
611
- let plan = scenario.plan(LEFT).with_override(path, json!(1));
612
- assert_eq!(
613
- code(scenario.apply(&plan)),
614
- RECONCILE_OVERRIDE_UNADDRESSABLE
615
- );
616
- }
617
- }
618
-
619
- /// Application does not require every conflict to have an override, and says so.
620
- ///
621
- /// `unresolved_conflicts` reports which conflicting paths a plan does not
622
- /// address, so a caller that wants each one decided individually can require it
623
- /// to be empty. Application itself does not: leaving a conflict unaddressed
624
- /// resolves it to the selected parent's value, and selecting that parent is
625
- /// itself an explicit decision.
626
- #[test]
627
- fn unresolved_conflicts_are_reported_but_not_required() {
628
- let scenario = Scenario::new(r#"{"a":1,"b":1}"#, r#"{"a":2,"b":2}"#, r#"{"a":3,"b":3}"#);
629
- let classification = scenario.classification();
630
- assert_eq!(classification.overall, ConflictClass::Conflict);
631
-
632
- let bare = scenario.plan(LEFT);
633
- let unresolved = bare.unresolved_conflicts(&classification);
634
- assert_eq!(unresolved.len(), 2, "neither conflict is addressed");
635
-
636
- // Applying anyway is allowed: the conflicts resolve to the chosen branch.
637
- assert_eq!(scenario.applied(&bare), json!({"a":2,"b":2}));
638
-
639
- let partly = scenario.plan(LEFT).with_override(vec![key("a")], json!(9));
640
- assert_eq!(partly.unresolved_conflicts(&classification).len(), 1);
641
-
642
- let fully = scenario
643
- .plan(LEFT)
644
- .with_override(vec![key("a")], json!(9))
645
- .with_override(vec![key("b")], json!(8));
646
- assert!(fully.unresolved_conflicts(&classification).is_empty());
647
- assert_eq!(scenario.applied(&fully), json!({"a":9,"b":8}));
648
- }
649
-
650
- // ---------------------------------------------------------------------------
651
- // The overlap regression from the corrected classifier
652
- // ---------------------------------------------------------------------------
653
-
654
- /// The ancestor/descendant case the corrected classifier now flags.
655
- ///
656
- /// ```text
657
- /// base: {"o":{"a":1}}
658
- /// left: {"o":7}
659
- /// right: {"o":{"a":2}}
660
- /// ```
661
- ///
662
- /// The two changes cannot both be applied. The classifier says so, and
663
- /// application does not quietly merge them into something that keeps both: the
664
- /// result is exactly the branch the plan chose, or exactly the override it
665
- /// supplied.
666
- #[test]
667
- fn an_overlapping_conflict_is_resolved_only_as_the_plan_says() {
668
- let scenario = Scenario::new(r#"{"o":{"a":1}}"#, r#"{"o":7}"#, r#"{"o":{"a":2}}"#);
669
-
670
- let classification = scenario.classification();
671
- assert_eq!(
672
- classification.overall,
673
- ConflictClass::Conflict,
674
- "the corrected classifier identifies the overlap"
675
- );
676
-
677
- // Choosing left takes the scalar, whole.
678
- assert_eq!(scenario.applied(&scenario.plan(LEFT)), json!({"o":7}));
679
- // Choosing right takes the object, whole.
680
- assert_eq!(
681
- scenario.applied(&scenario.plan(RIGHT)),
682
- json!({"o":{"a":2}})
683
- );
684
- // An explicit override takes exactly what it says.
685
- let explicit = scenario
686
- .plan(LEFT)
687
- .with_override(vec![key("o")], json!({"a":3}));
688
- assert_eq!(scenario.applied(&explicit), json!({"o":{"a":3}}));
689
-
690
- // Nothing produces a state carrying both the scalar and the member: these
691
- // structures cannot be combined, and application does not try.
692
- for plan in [scenario.plan(LEFT), scenario.plan(RIGHT), explicit] {
693
- let result = scenario.applied(&plan);
694
- let o = &result["o"];
695
- assert!(
696
- o.is_number() ^ o.is_object(),
697
- "the result is one shape or the other, never an invented combination"
698
- );
699
- }
700
- }
701
-
702
- // ---------------------------------------------------------------------------
703
- // End to end
704
- // ---------------------------------------------------------------------------
705
-
706
- /// The whole causal path, with independent changes and one real conflict.
707
- ///
708
- /// ```text
709
- /// base → left, right → SemanticDiff → ConflictClassification
710
- /// → explicit ReconciliationPlan → apply → result revision
711
- /// ```
712
- #[test]
713
- fn the_state_model_reconciles_an_explicit_decision_end_to_end() {
714
- let store = StateStore::new_volatile();
715
-
716
- // Two branches: each edits a different member independently, and both edit
717
- // `shared` differently, which is the conflict.
718
- let base = store
719
- .create(
720
- "docs:1",
721
- r#"{"only_left":1,"only_right":1,"shared":1,"untouched":"keep"}"#.to_string(),
722
- "auth".into(),
723
- )
724
- .expect("create");
725
- let left = store
726
- .commit(
727
- r#"{"only_left":2,"only_right":1,"shared":10,"untouched":"keep"}"#.to_string(),
728
- &base,
729
- "alice".into(),
730
- )
731
- .expect("left");
732
- let right = store
733
- .commit(
734
- r#"{"only_left":1,"only_right":2,"shared":20,"untouched":"keep"}"#.to_string(),
735
- &base,
736
- "bob".into(),
737
- )
738
- .expect("right");
739
-
740
- // The diffs are what classification is defined over.
741
- let base_value: Value = serde_json::from_str(&base.content).unwrap();
742
- let left_value: Value = serde_json::from_str(&left.content).unwrap();
743
- let right_value: Value = serde_json::from_str(&right.content).unwrap();
744
- assert_eq!(
745
- SemanticDiff::compute(&base_value, &left_value)
746
- .changes
747
- .len(),
748
- 2
749
- );
750
- assert_eq!(
751
- SemanticDiff::compute(&base_value, &right_value)
752
- .changes
753
- .len(),
754
- 2
755
- );
756
-
757
- // Classification separates what can coexist from what cannot.
758
- let classification = ConflictClassification::classify(&base, &left, &right);
759
- assert_eq!(classification.overall, ConflictClass::Conflict);
760
- let conflicting: Vec<_> = classification
761
- .path_conflicts
762
- .iter()
763
- .filter(|entry| entry.classification == ConflictClass::Conflict)
764
- .collect();
765
- assert_eq!(conflicting.len(), 1, "exactly one path conflicts");
766
- assert_eq!(conflicting[0].path, vec![key("shared")]);
767
- assert_eq!(conflicting[0].base_value, Some(json!(1)));
768
- assert_eq!(conflicting[0].left_value, Some(json!(10)));
769
- assert_eq!(conflicting[0].right_value, Some(json!(20)));
770
-
771
- // The caller decides: take the left branch, keep the right branch's
772
- // independent change, and resolve the conflict to a third value neither
773
- // branch proposed.
774
- let plan = ReconciliationPlan::new(left.id.clone(), right.id.clone(), base.id.clone(), LEFT)
775
- .with_override(vec![key("only_right")], json!(2))
776
- .with_override(vec![key("shared")], json!(99));
777
- assert!(
778
- plan.unresolved_conflicts(&classification).is_empty(),
779
- "every conflict is explicitly addressed"
780
- );
781
-
782
- let before = (
783
- base.content.clone(),
784
- left.content.clone(),
785
- right.content.clone(),
786
- );
787
- let result = reconcile(&base, &left, &right, &plan, "reconciler".into()).expect("applies");
788
- let reconciled: Value = serde_json::from_str(&result.materialized_state.content).unwrap();
789
-
790
- // 1. the independent change on the selected branch survives
791
- assert_eq!(reconciled["only_left"], json!(2));
792
- // 2. the independent change on the other branch survives, because the plan
793
- // asked for it
794
- assert_eq!(reconciled["only_right"], json!(2));
795
- // 3. the chosen resolution survives, and neither branch's value does
796
- assert_eq!(reconciled["shared"], json!(99));
797
- assert_ne!(reconciled["shared"], json!(10));
798
- assert_ne!(reconciled["shared"], json!(20));
799
- // 4. unrelated state is preserved
800
- assert_eq!(reconciled["untouched"], json!("keep"));
801
- // 5. the result is canonical
802
- assert_eq!(
803
- result.materialized_state.content,
804
- serde_json::to_string(&reconciled).unwrap()
805
- );
806
- assert!(result.materialized_state.verify_integrity());
807
- // 6. the inputs are unchanged
808
- assert_eq!(
809
- (
810
- base.content.clone(),
811
- left.content.clone(),
812
- right.content.clone()
813
- ),
814
- before
815
- );
816
-
817
- // The result is a revision like any other, committable through the store.
818
- let committed = store
819
- .commit(
820
- result.materialized_state.content.clone(),
821
- &left,
822
- "reconciler".into(),
823
- )
824
- .expect("commit");
825
- assert_eq!(committed.id, result.materialized_state.id);
826
- assert_eq!(committed.parent_id.as_ref(), Some(&left.id));
827
-
828
- // And the reconciled state has no remaining difference from itself.
829
- let final_value: Value = serde_json::from_str(&committed.content).unwrap();
830
- assert!(SemanticDiff::compute(&final_value, &reconciled)
831
- .changes
832
- .is_empty());
833
- }
834
-
835
- // ---------------------------------------------------------------------------
836
- // A recorded limitation of the plan type
837
- // ---------------------------------------------------------------------------
838
-
839
- /// A plan carrying overrides cannot be serialized to JSON.
840
- ///
841
- /// `path_overrides` is keyed by `Vec<PathComponent>`, and JSON object keys must
842
- /// be strings, so `serde_json` refuses the map the moment it holds an entry.
843
- /// The type derives `Serialize`, which makes this look supported; it is not.
844
- ///
845
- /// This is recorded, not fixed. A plan is presently constructed and applied
846
- /// in-process, which this PR is what establishes; nothing transmits one. Giving
847
- /// the path a string encoding would be inventing a second path syntax — exactly
848
- /// the flattened rendering the conflict work went out of its way to avoid —
849
- /// and it is not needed by any caller that exists. The test exists so that the
850
- /// day a caller does need to transmit a plan, the limitation is already stated
851
- /// rather than discovered.
852
- #[test]
853
- fn a_plan_with_overrides_is_not_json_serializable() {
854
- let scenario = Scenario::new(r#"{"a":1}"#, r#"{"a":2}"#, r#"{"a":3}"#);
855
-
856
- // Without overrides the plan serializes.
857
- let plain = scenario.plan(LEFT);
858
- assert!(serde_json::to_string(&plain).is_ok());
859
-
860
- // With one, it does not, and application is unaffected either way.
861
- let with_override = plain.clone().with_override(vec![key("a")], json!(9));
862
- let error = serde_json::to_string(&with_override).expect_err("non-string key");
863
- assert!(
864
- error.to_string().contains("key must be a string"),
865
- "unexpected error: {error}"
866
- );
867
- assert_eq!(scenario.apply(&with_override).unwrap(), r#"{"a":9}"#);
868
- }