hecks 4.0.0 → 6.0.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 (1281) hide show
  1. checksums.yaml +4 -4
  2. data/examples/pizzas/bluebook/pizzas.bluebook +1 -0
  3. data/exe/hecks +15 -3
  4. data/lib/hecks/adapters/driven/claude_code.rb +1 -1
  5. data/lib/hecks/adapters/driven/d1.rb +2 -1
  6. data/lib/hecks/adapters/driven/folder.rb +35 -28
  7. data/lib/hecks/adapters/driven/folder_overlay.rb +51 -0
  8. data/lib/hecks/adapters/driven/google_authentication.adapter +3 -0
  9. data/lib/hecks/adapters/driven/google_authentication.rb +99 -0
  10. data/lib/hecks/adapters/driven/governance_authorization.rb +32 -36
  11. data/lib/hecks/adapters/driven/heki/journal.rb +45 -0
  12. data/lib/hecks/adapters/driven/identity_registry.rb +1 -1
  13. data/lib/hecks/adapters/driven/in_memory_ordering.rb +3 -2
  14. data/lib/hecks/adapters/driven/key_keeper.rb +42 -0
  15. data/lib/hecks/adapters/driven/lambda/client.rb +8 -2
  16. data/lib/hecks/adapters/driven/lambda/remote_dispatcher.rb +185 -0
  17. data/lib/hecks/adapters/driven/lambda.rb +14 -2
  18. data/lib/hecks/adapters/driven/memory/dispatch_claims.rb +25 -0
  19. data/lib/hecks/adapters/driven/memory.rb +15 -4
  20. data/lib/hecks/adapters/driven/postgres/codec.rb +1 -1
  21. data/lib/hecks/adapters/driven/postgres/journal.rb +46 -0
  22. data/lib/hecks/adapters/driven/postgres/outbox.rb +31 -5
  23. data/lib/hecks/adapters/driven/postgres/projection.rb +43 -0
  24. data/lib/hecks/adapters/driven/postgres/repository.rb +1 -1
  25. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +2 -1
  26. data/lib/hecks/adapters/driven/postgres/set_dialect.rb +44 -0
  27. data/lib/hecks/adapters/driven/postgres.adapter +8 -0
  28. data/lib/hecks/adapters/driven/postgres.rb +8 -3
  29. data/lib/hecks/adapters/driven/postgres_dump/connection.rb +57 -0
  30. data/lib/hecks/adapters/driven/postgres_dump.rb +117 -0
  31. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/boot.rb +6 -3
  32. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/class_methods.rb +1 -1
  33. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/dialect.rb +1 -1
  34. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/chain_sql.rb +8 -4
  35. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/domain_rename.rb +2 -2
  36. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/era_integrity.rb +2 -2
  37. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/era_store.rb +3 -3
  38. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/mint_transaction.rb +2 -2
  39. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/sql_templates.rb +6 -2
  40. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/tail_merge.rb +1 -1
  41. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage.rb +1 -1
  42. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage_manager/context.rb +15 -5
  43. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage_manager/coverage_check.rb +13 -11
  44. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage_manager/era_resolver.rb +23 -6
  45. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage_manager/merge_coordinator.rb +2 -2
  46. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage_manager/minter.rb +12 -9
  47. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage_manager/refusals.rb +13 -1
  48. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage_manager.rb +6 -6
  49. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/query_phases.rb +8 -2
  50. data/lib/hecks/adapters/driven/postgres_era.adapter +7 -1
  51. data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era.rb +14 -13
  52. data/lib/hecks/adapters/driven/prism.rb +4 -3
  53. data/lib/hecks/adapters/driven/projection/read_model_walk.rb +126 -0
  54. data/lib/hecks/adapters/driven/projection/row_decoding.rb +57 -0
  55. data/lib/hecks/adapters/driven/set_transform/assignments.rb +101 -0
  56. data/lib/hecks/adapters/driven/set_transform/expressions.rb +138 -0
  57. data/lib/hecks/adapters/driven/set_transform.rb +81 -0
  58. data/lib/hecks/adapters/driven/sql_keyset.rb +61 -0
  59. data/lib/hecks/adapters/driven/sql_query_builder.rb +28 -9
  60. data/lib/hecks/adapters/driven/sqlite/codec.rb +1 -1
  61. data/lib/hecks/adapters/driven/sqlite/ddl.rb +7 -0
  62. data/lib/hecks/adapters/driven/sqlite/dialect.rb +2 -1
  63. data/lib/hecks/adapters/driven/sqlite/dispatch_claims.rb +28 -0
  64. data/lib/hecks/adapters/driven/sqlite/journal.rb +46 -0
  65. data/lib/hecks/adapters/driven/sqlite/outbox.rb +3 -2
  66. data/lib/hecks/adapters/driven/sqlite/projection.rb +9 -135
  67. data/lib/hecks/adapters/driven/sqlite/repository.rb +1 -1
  68. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +16 -4
  69. data/lib/hecks/adapters/driven/sqlite/set_dialect.rb +39 -0
  70. data/lib/hecks/adapters/driven/sqlite.adapter +2 -0
  71. data/lib/hecks/adapters/driven/sqlite.rb +11 -2
  72. data/lib/hecks/adapters/driven.rb +8 -3
  73. data/lib/hecks/adapters/driving/cli.rb +1 -1
  74. data/lib/hecks/adapters/driving/cli_runner/answers.rb +23 -16
  75. data/lib/hecks/adapters/driving/cli_runner/resolution.rb +25 -24
  76. data/lib/hecks/adapters/driving/cli_runner/settling.rb +9 -5
  77. data/lib/hecks/adapters/driving/cli_runner/streaming.rb +1 -1
  78. data/lib/hecks/adapters/driving/cli_runner/suggestions.rb +15 -7
  79. data/lib/hecks/adapters/driving/cli_runner/verb_names.rb +49 -0
  80. data/lib/hecks/adapters/driving/cli_runner.rb +4 -2
  81. data/lib/hecks/adapters/driving/github_webhook.rb +2 -1
  82. data/lib/hecks/adapters/driving/handle/verb_methods.rb +46 -0
  83. data/lib/hecks/adapters/driving/handle.rb +9 -17
  84. data/lib/hecks/adapters/driving/json.rb +19 -17
  85. data/lib/hecks/adapters/driving/launcher_options.rb +37 -32
  86. data/lib/hecks/adapters/driving/mcp_scope/env_problems.rb +3 -1
  87. data/lib/hecks/adapters/driving/mcp_scope.rb +8 -7
  88. data/lib/hecks/adapters/driving/newcomer.rb +6 -5
  89. data/lib/hecks/adapters/driving/ruby/aggregate_module.rb +13 -12
  90. data/lib/hecks/adapters/driving/ruby/bulk_verbs.rb +127 -0
  91. data/lib/hecks/adapters/driving/ruby/{chapter.rb → declaration.rb} +27 -27
  92. data/lib/hecks/adapters/driving/ruby/query_rows.rb +27 -0
  93. data/lib/hecks/adapters/driving/ruby.rb +25 -24
  94. data/lib/hecks/adapters/driving/usage_cache.rb +14 -3
  95. data/lib/hecks/assembly.rb +101 -0
  96. data/lib/hecks/behaviors/dsl.rb +13 -3
  97. data/lib/hecks/behaviors/expectations/boot.rb +7 -6
  98. data/lib/hecks/behaviors/expectations/checks.rb +1 -1
  99. data/lib/hecks/behaviors/expectations/dispatching.rb +10 -9
  100. data/lib/hecks/behaviors/expectations/qualify.rb +9 -9
  101. data/lib/hecks/behaviors/expectations.rb +24 -10
  102. data/lib/hecks/behaviors/ir.rb +4 -2
  103. data/lib/hecks/behaviors/rspec.rb +1 -1
  104. data/lib/hecks/behaviors/runner.rb +31 -4
  105. data/lib/hecks/bench/environment.rb +6 -2
  106. data/lib/hecks/bench/postgres_probe.rb +3 -1
  107. data/lib/hecks/bench/report.rb +4 -1
  108. data/lib/hecks/bench/suite.rb +9 -3
  109. data/lib/hecks/bluebook/acknowledgements.rb +81 -0
  110. data/lib/hecks/bluebook/aggregate.rb +13 -4
  111. data/lib/hecks/bluebook/ask_resolution.rb +17 -17
  112. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +8 -5
  113. data/lib/hecks/bluebook/assembly/contracts/structure.rb +11 -7
  114. data/lib/hecks/bluebook/assembly/marks.rb +10 -1
  115. data/lib/hecks/bluebook/assembly/scalars.rb +8 -2
  116. data/lib/hecks/bluebook/assembly/specializer.rb +2 -2
  117. data/lib/hecks/bluebook/assembly.rb +10 -10
  118. data/lib/hecks/bluebook/behaviour/aggregate.rb +2 -1
  119. data/lib/hecks/bluebook/behaviour/attribute.rb +12 -0
  120. data/lib/hecks/bluebook/behaviour/command.rb +1 -1
  121. data/lib/hecks/bluebook/behaviour/{chapter.rb → declaration.rb} +17 -17
  122. data/lib/hecks/bluebook/behaviour/domain_port.rb +1 -1
  123. data/lib/hecks/bluebook/behaviour/entity.rb +1 -0
  124. data/lib/hecks/bluebook/behaviour/hecksagon.rb +8 -1
  125. data/lib/hecks/bluebook/behaviour/process_manager.rb +4 -1
  126. data/lib/hecks/bluebook/behaviour/query.rb +5 -0
  127. data/lib/hecks/bluebook/behaviour/read_model.rb +4 -1
  128. data/lib/hecks/bluebook/behaviour/traits.rb +13 -0
  129. data/lib/hecks/bluebook/capabilities/attribute_values.rb +17 -16
  130. data/lib/hecks/bluebook/capabilities/package_declared.rb +65 -0
  131. data/lib/hecks/bluebook/capabilities.rb +32 -132
  132. data/lib/hecks/bluebook/command.rb +22 -7
  133. data/lib/hecks/bluebook/context_map.rb +104 -0
  134. data/lib/hecks/bluebook/{chapter.rb → declaration.rb} +13 -13
  135. data/lib/hecks/bluebook/dsl/adapter_builder.rb +28 -4
  136. data/lib/hecks/bluebook/dsl/aggregate_builder/draining.rb +2 -2
  137. data/lib/hecks/bluebook/dsl/aggregate_builder/one_of_reference.rb +54 -0
  138. data/lib/hecks/bluebook/dsl/aggregate_builder/query_field_sealing.rb +11 -2
  139. data/lib/hecks/bluebook/dsl/aggregate_builder/query_sealing.rb +15 -5
  140. data/lib/hecks/bluebook/dsl/aggregate_builder/relationships.rb +21 -3
  141. data/lib/hecks/bluebook/dsl/aggregate_builder/rules.rb +22 -22
  142. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +2 -2
  143. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +27 -22
  144. data/lib/hecks/bluebook/dsl/bluebook_builder/pending_givens.rb +22 -22
  145. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/computed_sources.rb +27 -21
  146. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/correlation_keys.rb +3 -5
  147. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/event_shapes.rb +5 -4
  148. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/fan_out_rows.rb +3 -3
  149. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/hop_tails.rb +13 -5
  150. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/projected_fields.rb +4 -4
  151. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/provisions.rb +38 -84
  152. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/query_hops.rb +5 -5
  153. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/references.rb +6 -6
  154. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/with_projections.rb +6 -6
  155. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +3 -3
  156. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +67 -67
  157. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -0
  158. data/lib/hecks/bluebook/dsl/command_builder/folded_sets.rb +48 -0
  159. data/lib/hecks/bluebook/dsl/command_builder.rb +28 -15
  160. data/lib/hecks/bluebook/dsl/context_map_builder.rb +73 -0
  161. data/lib/hecks/bluebook/dsl/entity_builder/rules.rb +17 -17
  162. data/lib/hecks/bluebook/dsl/entity_builder/scope.rb +3 -3
  163. data/lib/hecks/bluebook/dsl/entity_builder/sealing.rb +2 -2
  164. data/lib/hecks/bluebook/dsl/entity_builder.rb +3 -3
  165. data/lib/hecks/bluebook/dsl/hecksagon_builder/{chapter_scope.rb → bluebook_scope.rb} +14 -14
  166. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +33 -27
  167. data/lib/hecks/bluebook/dsl/identity_declaration.rb +6 -2
  168. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +4 -1
  169. data/lib/hecks/bluebook/dsl/query_builder.rb +14 -8
  170. data/lib/hecks/bluebook/dsl/read_model_builder/sealing.rb +6 -11
  171. data/lib/hecks/bluebook/dsl/read_model_builder.rb +1 -2
  172. data/lib/hecks/bluebook/dsl/word_gate.rb +2 -1
  173. data/lib/hecks/bluebook/dsl/world_builder.rb +2 -2
  174. data/lib/hecks/bluebook/dsl.rb +3 -2
  175. data/lib/hecks/bluebook/expression/ast_json/emitters.rb +4 -0
  176. data/lib/hecks/bluebook/expression/ast_json.rb +5 -3
  177. data/lib/hecks/bluebook/expression/ast_reader/blocks.rb +41 -0
  178. data/lib/hecks/bluebook/expression/ast_reader.rb +2 -10
  179. data/lib/hecks/bluebook/expression/canonical_form.rb +9 -4
  180. data/lib/hecks/bluebook/expression/evaluator/parser.rb +4 -1
  181. data/lib/hecks/bluebook/expression/evaluator.rb +13 -9
  182. data/lib/hecks/bluebook/expression/projection.json +6 -0
  183. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +50 -2
  184. data/lib/hecks/bluebook/expression/resolver/lookups.rb +2 -1
  185. data/lib/hecks/bluebook/expression/resolver/operations.rb +1 -0
  186. data/lib/hecks/bluebook/expression/resolver/parsing.rb +21 -15
  187. data/lib/hecks/bluebook/expression/resolver/scanning.rb +4 -1
  188. data/lib/hecks/bluebook/expression.rb +1 -1
  189. data/lib/hecks/bluebook/hecksagon.rb +48 -23
  190. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +4 -1
  191. data/lib/hecks/bluebook/meta_validator/judge/cells.rb +1 -1
  192. data/lib/hecks/bluebook/meta_validator/judge/identity.rb +2 -1
  193. data/lib/hecks/bluebook/meta_validator/modes.rb +27 -27
  194. data/lib/hecks/bluebook/meta_validator/plan.rb +5 -5
  195. data/lib/hecks/bluebook/meta_validator/readings/fields.rb +3 -3
  196. data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +11 -3
  197. data/lib/hecks/bluebook/meta_validator/reconstruction/reading.rb +1 -1
  198. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +19 -19
  199. data/lib/hecks/bluebook/meta_validator/shapes.rb +6 -1
  200. data/lib/hecks/bluebook/meta_validator/syntax_boot/disk_cache.rb +18 -15
  201. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +21 -15
  202. data/lib/hecks/bluebook/meta_validator/verdict_cache/prebuilt.rb +4 -1
  203. data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +11 -10
  204. data/lib/hecks/bluebook/meta_validator.rb +25 -22
  205. data/lib/hecks/bluebook/model_check/aggregate_checks.rb +68 -0
  206. data/lib/hecks/bluebook/model_check/ask_checks.rb +6 -5
  207. data/lib/hecks/bluebook/model_check/client_profile.rb +8 -42
  208. data/lib/hecks/bluebook/model_check/context_checks.rb +97 -0
  209. data/lib/hecks/bluebook/model_check/context_map_checks.rb +158 -0
  210. data/lib/hecks/bluebook/model_check/lifecycle_checks.rb +27 -5
  211. data/lib/hecks/bluebook/model_check/one_of_checks.rb +106 -0
  212. data/lib/hecks/bluebook/model_check/orphan_checks.rb +129 -0
  213. data/lib/hecks/bluebook/model_check/redundant_relationships.rb +38 -0
  214. data/lib/hecks/bluebook/model_check/reference_checks.rb +11 -1
  215. data/lib/hecks/bluebook/model_check/saga_checks.rb +8 -3
  216. data/lib/hecks/bluebook/model_check/verbs.rb +4 -4
  217. data/lib/hecks/bluebook/model_check.rb +32 -12
  218. data/lib/hecks/bluebook/named_pattern.rb +12 -1
  219. data/lib/hecks/bluebook/one_of_union.rb +35 -0
  220. data/lib/hecks/bluebook/pattern_subset.rb +12 -3
  221. data/lib/hecks/bluebook/project_loader.rb +3 -2
  222. data/lib/hecks/bluebook/project_register.rb +21 -5
  223. data/lib/hecks/bluebook/query.rb +4 -1
  224. data/lib/hecks/bluebook/read_model.rb +5 -5
  225. data/lib/hecks/bluebook/reference.rb +3 -3
  226. data/lib/hecks/bluebook/smoke_test.rb +15 -13
  227. data/lib/hecks/bluebook/synthesizer.rb +48 -13
  228. data/lib/hecks/bluebook/translation_expression/parser.rb +14 -5
  229. data/lib/hecks/bluebook.rb +6 -3
  230. data/lib/hecks/bulk/bluebook/adapters/row_sweeper.adapter +7 -0
  231. data/lib/hecks/bulk/bluebook/bulk.behaviors +18 -0
  232. data/lib/hecks/bulk/bluebook/bulk.bluebook +126 -0
  233. data/lib/hecks/bulk/bluebook/bulk.hecksagon +11 -0
  234. data/lib/hecks/bulk/bluebook/bulk.ports.hecksagon +13 -0
  235. data/lib/hecks/bulk/plan.rb +60 -0
  236. data/lib/hecks/bulk/row_sweeper.rb +34 -0
  237. data/lib/hecks/bulk/set_arguments.rb +68 -0
  238. data/lib/hecks/bulk/set_eligibility.rb +50 -0
  239. data/lib/hecks/bulk/set_planner.rb +100 -0
  240. data/lib/hecks/bulk/set_sweep.rb +78 -0
  241. data/lib/hecks/bulk/sweep.rb +67 -0
  242. data/lib/hecks/cli/check_era.rb +6 -6
  243. data/lib/hecks/cli/command_table.rb +2 -2
  244. data/lib/hecks/cli/console.rb +11 -6
  245. data/lib/hecks/cli/document.rb +14 -9
  246. data/lib/hecks/cli/domain_writer.rb +4 -1
  247. data/lib/hecks/cli/expected_era.rb +139 -0
  248. data/lib/hecks/cli/expression_projection.rb +2 -2
  249. data/lib/hecks/cli/follow.rb +3 -3
  250. data/lib/hecks/cli/history.rb +4 -1
  251. data/lib/hecks/cli/interview_draft.rb +8 -1
  252. data/lib/hecks/cli/interview_record.rb +7 -3
  253. data/lib/hecks/cli/interview_run.rb +7 -5
  254. data/lib/hecks/cli/interview_session/proposals.rb +5 -2
  255. data/lib/hecks/cli/interview_session.rb +3 -2
  256. data/lib/hecks/cli/interview_shape/lifecycle.rb +92 -0
  257. data/lib/hecks/cli/interview_shape.rb +19 -73
  258. data/lib/hecks/cli/model_check/examination.rb +63 -14
  259. data/lib/hecks/cli/model_check/sources.rb +4 -2
  260. data/lib/hecks/cli/model_check.rb +1 -1
  261. data/lib/hecks/cli/persistence_notice.rb +45 -0
  262. data/lib/hecks/cli/present.rb +1 -1
  263. data/lib/hecks/cli/project_cli/launcher_source.rb +12 -10
  264. data/lib/hecks/cli/project_cli/templates.rb +24 -3
  265. data/lib/hecks/cli/project_cli.rb +11 -13
  266. data/lib/hecks/cli/project_diagrams.rb +20 -20
  267. data/lib/hecks/cli/project_glossary.rb +14 -14
  268. data/lib/hecks/cli/reference.rb +1 -1
  269. data/lib/hecks/cli/refresh_projections.rb +16 -11
  270. data/lib/hecks/cli/run.rb +27 -5
  271. data/lib/hecks/cli/seed_semantics_corpus.rb +14 -4
  272. data/lib/hecks/cli/shape.rb +3 -4
  273. data/lib/hecks/cli/smoke_http/transport.rb +3 -2
  274. data/lib/hecks/cli/statements.rb +7 -7
  275. data/lib/hecks/cli/stores.rb +70 -2
  276. data/lib/hecks/cli/stress_concurrency_specs/reporting.rb +4 -1
  277. data/lib/hecks/codemod.rb +3 -3
  278. data/lib/hecks/composer/adapters/claude_composer.rb +3 -2
  279. data/lib/hecks/composer/agent.rb +1 -1
  280. data/lib/hecks/composer/assistant.rb +2 -2
  281. data/lib/hecks/composer/behavior_run.rb +7 -1
  282. data/lib/hecks/composer/bluebook/composer.bluebook +18 -25
  283. data/lib/hecks/composer/bluebook/composer.hecksagon +1 -1
  284. data/lib/hecks/composer/board.rb +6 -6
  285. data/lib/hecks/composer/board_edit/command_text.rb +4 -1
  286. data/lib/hecks/composer/board_edit/source.rb +18 -9
  287. data/lib/hecks/composer/board_edit.rb +2 -2
  288. data/lib/hecks/composer/check.rb +22 -22
  289. data/lib/hecks/composer/domains.rb +1 -1
  290. data/lib/hecks/composer/live.rb +19 -13
  291. data/lib/hecks/composer/outline.rb +4 -1
  292. data/lib/hecks/composer/page.html +1 -1
  293. data/lib/hecks/composer/panels.rb +6 -3
  294. data/lib/hecks/composer/parse.rb +53 -36
  295. data/lib/hecks/composer/session.rb +22 -16
  296. data/lib/hecks/composer/toolbox.rb +9 -6
  297. data/lib/hecks/composer/web.rb +3 -2
  298. data/lib/hecks/composer/workbench.rb +12 -9
  299. data/lib/hecks/composer.rb +1 -1
  300. data/lib/hecks/construct.rb +7 -7
  301. data/lib/hecks/corpus/adapters.rb +2 -1
  302. data/lib/hecks/corpus/routes.rb +25 -5
  303. data/lib/hecks/corpus/rust.rb +59 -24
  304. data/lib/hecks/corpus/sources.rb +12 -11
  305. data/lib/hecks/corpus/worlds.rb +2 -1
  306. data/lib/hecks/corpus.rb +30 -22
  307. data/lib/hecks/deploy/bluebook/deploy.bluebook +304 -518
  308. data/lib/hecks/doc/reference/harvester.rb +4 -1
  309. data/lib/hecks/doc/reference/readme.rb +11 -6
  310. data/lib/hecks/doc/reference/rendering.rb +11 -5
  311. data/lib/hecks/doc/reference.rb +7 -5
  312. data/lib/hecks/embryonaut_bluebook/lock.rb +5 -1
  313. data/lib/hecks/embryonaut_bluebook/manifest.rb +7 -1
  314. data/lib/hecks/embryonaut_bluebook/registry/changelog.rb +4 -1
  315. data/lib/hecks/embryonaut_bluebook/registry/versions.rb +13 -2
  316. data/lib/hecks/embryonaut_bluebook/registry.rb +5 -2
  317. data/lib/hecks/embryonaut_bluebook/shape.rb +1 -1
  318. data/lib/hecks/embryonaut_bluebook/vendor.rb +5 -2
  319. data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +15 -1
  320. data/lib/hecks/embryonaut_bluebook.rb +26 -12
  321. data/lib/hecks/engine_agreement.rb +4 -1
  322. data/lib/hecks/forms/app/commands.rb +35 -2
  323. data/lib/hecks/forms/app/records.rb +12 -12
  324. data/lib/hecks/forms/app/responses.rb +4 -2
  325. data/lib/hecks/forms/app.rb +22 -19
  326. data/lib/hecks/forms/command_form_renderer.rb +2 -2
  327. data/lib/hecks/forms/field_controls.rb +6 -1
  328. data/lib/hecks/forms/field_shape.rb +14 -6
  329. data/lib/hecks/forms/html.rb +8 -7
  330. data/lib/hecks/forms/html_pattern.rb +80 -0
  331. data/lib/hecks/forms/index_renderer.rb +11 -11
  332. data/lib/hecks/forms/params.rb +2 -1
  333. data/lib/hecks/forms/port_argument.rb +4 -1
  334. data/lib/hecks/forms/query_form_renderer.rb +1 -1
  335. data/lib/hecks/forms/record_renderer.rb +2 -2
  336. data/lib/hecks/forms/record_table.rb +1 -1
  337. data/lib/hecks/forms/reference_options.rb +2 -1
  338. data/lib/hecks/forms/text_shape.rb +4 -1
  339. data/lib/hecks/forms/value_object_shape.rb +1 -1
  340. data/lib/hecks/forms.rb +7 -7
  341. data/lib/hecks/fqn.rb +8 -2
  342. data/lib/hecks/framework/bluebook/change.bluebook +10 -15
  343. data/lib/hecks/framework/bluebook/change.hecksagon +1 -1
  344. data/lib/hecks/framework/bluebook/compliance.bluebook +27 -126
  345. data/lib/hecks/framework/bluebook/console_settings.bluebook +199 -114
  346. data/lib/hecks/framework/bluebook/framework.hecksagon +7 -0
  347. data/lib/hecks/framework/bluebook/governance.bluebook +36 -14
  348. data/lib/hecks/framework/bluebook/identity.bluebook +1 -1
  349. data/lib/hecks/framework/bluebook/privacy.bluebook +123 -19
  350. data/lib/hecks/framework/bluebook/privacy.key_keeper.adapter +7 -0
  351. data/lib/hecks/framework/bluebook/privacy.ports.hecksagon +19 -0
  352. data/lib/hecks/framework/compliance.oidc.json +6 -36
  353. data/lib/hecks/framework/console_settings.oidc.json +39 -24
  354. data/lib/hecks/framework/privacy.oidc.json +25 -0
  355. data/lib/hecks/framework.rb +24 -8
  356. data/lib/hecks/framework_bluebooks.rb +156 -0
  357. data/lib/hecks/freezer.rb +7 -1
  358. data/lib/hecks/fuzzing/combination_miner.rb +9 -6
  359. data/lib/hecks/fuzzing/concurrent_dispatch/world_copy.rb +3 -1
  360. data/lib/hecks/fuzzing/concurrent_dispatch.rb +2 -2
  361. data/lib/hecks/fuzzing/domain_generator.rb +8 -3
  362. data/lib/hecks/fuzzing/era_boundary.rb +1 -1
  363. data/lib/hecks/fuzzing/failure_triage.rb +13 -6
  364. data/lib/hecks/fuzzing/form_census/{chapter_forms.rb → bluebook_forms.rb} +12 -12
  365. data/lib/hecks/fuzzing/form_census.rb +14 -12
  366. data/lib/hecks/fuzzing/generated_domain_check.rb +2 -1
  367. data/lib/hecks/fuzzing/isolated_boot/postgres_scratch.rb +5 -5
  368. data/lib/hecks/fuzzing/isolated_boot/scratch_schema.rb +19 -9
  369. data/lib/hecks/fuzzing/isolated_boot/worlds.rb +13 -8
  370. data/lib/hecks/fuzzing/isolated_boot.rb +4 -5
  371. data/lib/hecks/fuzzing/mutation/checks.rb +2 -1
  372. data/lib/hecks/fuzzing/mutation/finders.rb +25 -8
  373. data/lib/hecks/fuzzing/mutation/observation.rb +3 -1
  374. data/lib/hecks/fuzzing/mutation/source_map.rb +3 -1
  375. data/lib/hecks/fuzzing/nondeterministic.rb +10 -4
  376. data/lib/hecks/fuzzing/outside_world.rb +1 -1
  377. data/lib/hecks/fuzzing/projection_agreement/binding.rb +63 -0
  378. data/lib/hecks/fuzzing/projection_agreement/comparison.rb +104 -0
  379. data/lib/hecks/fuzzing/projection_agreement.rb +68 -0
  380. data/lib/hecks/fuzzing/properties/aggregation_recompute.rb +1 -1
  381. data/lib/hecks/fuzzing/properties/bulk_parity.rb +118 -0
  382. data/lib/hecks/fuzzing/properties/catalog.rb +7 -0
  383. data/lib/hecks/fuzzing/properties/cursor_walks.rb +56 -0
  384. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +39 -1
  385. data/lib/hecks/fuzzing/properties/group_by_recompute.rb +6 -2
  386. data/lib/hecks/fuzzing/properties/grouped_reduction_recompute.rb +88 -0
  387. data/lib/hecks/fuzzing/properties/guards.rb +4 -1
  388. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +12 -2
  389. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +2 -1
  390. data/lib/hecks/fuzzing/properties/mutation_recompute.rb +3 -2
  391. data/lib/hecks/fuzzing/properties/piece_invariants.rb +3 -2
  392. data/lib/hecks/fuzzing/properties/query_rows.rb +5 -3
  393. data/lib/hecks/fuzzing/properties/querying.rb +6 -3
  394. data/lib/hecks/fuzzing/properties/read_model_names.rb +8 -2
  395. data/lib/hecks/fuzzing/properties/read_models.rb +2 -1
  396. data/lib/hecks/fuzzing/properties/refusals.rb +35 -0
  397. data/lib/hecks/fuzzing/properties/role_checks.rb +3 -2
  398. data/lib/hecks/fuzzing/properties/set_transform_parity.rb +73 -0
  399. data/lib/hecks/fuzzing/properties/tenant_scope.rb +3 -2
  400. data/lib/hecks/fuzzing/properties.rb +6 -0
  401. data/lib/hecks/fuzzing/qa_settings.rb +4 -4
  402. data/lib/hecks/fuzzing/replay/fan_out.rb +4 -3
  403. data/lib/hecks/fuzzing/replay/filters.rb +2 -2
  404. data/lib/hecks/fuzzing/replay/guard_check.rb +2 -2
  405. data/lib/hecks/fuzzing/replay/mutation_trace.rb +23 -10
  406. data/lib/hecks/fuzzing/replay/observation.rb +11 -7
  407. data/lib/hecks/fuzzing/replay/read_only_steps.rb +40 -3
  408. data/lib/hecks/fuzzing/replay/refusal_trace.rb +44 -0
  409. data/lib/hecks/fuzzing/replay/role_check.rb +2 -2
  410. data/lib/hecks/fuzzing/replay/session.rb +38 -18
  411. data/lib/hecks/fuzzing/replay/validation_reports.rb +61 -0
  412. data/lib/hecks/fuzzing/replay.rb +2 -2
  413. data/lib/hecks/fuzzing/rust_gap_manifest.rb +3 -3
  414. data/lib/hecks/fuzzing/self_consistency/saga_redelivery.rb +18 -8
  415. data/lib/hecks/fuzzing/self_consistency/sagas.rb +2 -2
  416. data/lib/hecks/fuzzing/self_consistency/value_objects.rb +9 -2
  417. data/lib/hecks/fuzzing/self_consistency.rb +4 -8
  418. data/lib/hecks/fuzzing/sequence_generator/adversary/argument_mutations.rb +15 -5
  419. data/lib/hecks/fuzzing/sequence_generator/adversary/caller_draw.rb +14 -10
  420. data/lib/hecks/fuzzing/sequence_generator/adversary/precedence_mutations.rb +1 -1
  421. data/lib/hecks/fuzzing/sequence_generator/argument_drawing.rb +2 -1
  422. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +21 -13
  423. data/lib/hecks/fuzzing/sequence_generator/picker.rb +1 -1
  424. data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +3 -3
  425. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +2 -2
  426. data/lib/hecks/fuzzing/sequence_generator.rb +1 -2
  427. data/lib/hecks/fuzzing/shrinker.rb +4 -1
  428. data/lib/hecks/fuzzing/value_generator.rb +14 -8
  429. data/lib/hecks/fuzzing.rb +1 -0
  430. data/lib/hecks/grammar/dialects.rb +56 -0
  431. data/lib/hecks/grammar/evolve/arguments.rb +1 -1
  432. data/lib/hecks/grammar/evolve/keywords.rb +1 -1
  433. data/lib/hecks/grammar/evolve/seeds.rb +34 -8
  434. data/lib/hecks/grammar/expression.bluebook +3 -0
  435. data/lib/hecks/grammar/expression_operators.json +1071 -1
  436. data/lib/hecks/grammar/operators.rb +45 -22
  437. data/lib/hecks/grammar/translation.bluebook +2 -0
  438. data/lib/hecks/grammar/translation_ledger.rb +45 -0
  439. data/lib/hecks/grammar/translation_rules.json +646 -0
  440. data/lib/hecks/grammar.rb +27 -23
  441. data/lib/hecks/hecks/adapters/claude_agents.adapter +7 -0
  442. data/lib/hecks/hecks/adapters/claude_agents.rb +119 -0
  443. data/lib/hecks/hecks/adapters/claude_transcript.rb +71 -0
  444. data/lib/hecks/hecks/adapters/codebase/cache_clearing.rb +32 -0
  445. data/lib/hecks/hecks/adapters/codebase/cargo_tests.rb +50 -0
  446. data/lib/hecks/hecks/adapters/codebase/composing.rb +3 -2
  447. data/lib/hecks/hecks/adapters/codebase/conformance.rb +17 -1
  448. data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +1 -1
  449. data/lib/hecks/hecks/adapters/codebase/declarations.rb +144 -0
  450. data/lib/hecks/hecks/adapters/codebase/inspection.rb +5 -4
  451. data/lib/hecks/hecks/adapters/codebase/language.rb +1 -0
  452. data/lib/hecks/hecks/adapters/codebase/npm_registry.rb +7 -1
  453. data/lib/hecks/hecks/adapters/codebase/previewing.rb +62 -0
  454. data/lib/hecks/hecks/adapters/codebase/promotion/gate.rb +1 -1
  455. data/lib/hecks/hecks/adapters/codebase/reading.rb +94 -0
  456. data/lib/hecks/hecks/adapters/codebase/regeneration.rb +6 -2
  457. data/lib/hecks/hecks/adapters/codebase/release_facts.rb +1 -1
  458. data/lib/hecks/hecks/adapters/codebase/ruby_child.rb +5 -2
  459. data/lib/hecks/hecks/adapters/codebase/search.rb +91 -0
  460. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +37 -2
  461. data/lib/hecks/hecks/adapters/codebase/static_directory.rb +59 -0
  462. data/lib/hecks/hecks/adapters/codebase/style.rb +2 -2
  463. data/lib/hecks/hecks/adapters/codebase/test_runner.rb +30 -11
  464. data/lib/hecks/hecks/adapters/codebase/test_suite.rb +41 -3
  465. data/lib/hecks/hecks/adapters/console_capture.rb +27 -0
  466. data/lib/hecks/hecks/adapters/deploy_plans.rb +12 -3
  467. data/lib/hecks/hecks/adapters/deploy_toolchain/answers.rb +14 -5
  468. data/lib/hecks/hecks/adapters/deploy_toolchain/scripts.rb +6 -3
  469. data/lib/hecks/hecks/adapters/deploy_toolchain.rb +3 -3
  470. data/lib/hecks/hecks/adapters/desktop_alert/alert.rb +50 -0
  471. data/lib/hecks/hecks/adapters/desktop_alert.adapter +7 -0
  472. data/lib/hecks/hecks/adapters/desktop_alert.rb +99 -0
  473. data/lib/hecks/hecks/adapters/finding_github.rb +4 -1
  474. data/lib/hecks/hecks/adapters/first_admin.rb +19 -12
  475. data/lib/hecks/hecks/adapters/git.rb +5 -5
  476. data/lib/hecks/hecks/adapters/git_checkout.adapter +7 -0
  477. data/lib/hecks/hecks/adapters/git_checkout.rb +99 -0
  478. data/lib/hecks/hecks/adapters/git_landing/merger.rb +85 -0
  479. data/lib/hecks/hecks/adapters/git_landing.adapter +7 -0
  480. data/lib/hecks/hecks/adapters/git_landing.rb +103 -0
  481. data/lib/hecks/hecks/adapters/git_launcher.adapter +7 -0
  482. data/lib/hecks/hecks/adapters/git_launcher.rb +82 -0
  483. data/lib/hecks/hecks/adapters/git_refs.rb +8 -4
  484. data/lib/hecks/hecks/adapters/git_train/bench.rb +76 -0
  485. data/lib/hecks/hecks/adapters/git_train/carriage.rb +116 -0
  486. data/lib/hecks/hecks/adapters/git_train/local_branch.rb +69 -0
  487. data/lib/hecks/hecks/adapters/git_train/reading.rb +26 -0
  488. data/lib/hecks/hecks/adapters/git_train/survey.rb +60 -0
  489. data/lib/hecks/hecks/adapters/git_train.adapter +7 -0
  490. data/lib/hecks/hecks/adapters/git_train.rb +88 -0
  491. data/lib/hecks/hecks/adapters/git_worktrees.rb +81 -0
  492. data/lib/hecks/hecks/adapters/host_http.rb +5 -4
  493. data/lib/hecks/hecks/adapters/in_process_access.rb +3 -2
  494. data/lib/hecks/hecks/adapters/in_process_boot.rb +62 -35
  495. data/lib/hecks/hecks/adapters/in_process_operations.rb +30 -4
  496. data/lib/hecks/hecks/adapters/journal_store/changes.rb +7 -7
  497. data/lib/hecks/hecks/adapters/journal_store/compaction.rb +14 -7
  498. data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +9 -7
  499. data/lib/hecks/hecks/adapters/journal_store/examination.rb +3 -3
  500. data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +17 -5
  501. data/lib/hecks/hecks/adapters/journal_store/readings/attestation.rb +3 -3
  502. data/lib/hecks/hecks/adapters/journal_store/readings/scaffolding.rb +7 -4
  503. data/lib/hecks/hecks/adapters/journal_store/readings.rb +2 -2
  504. data/lib/hecks/hecks/adapters/journal_store.rb +2 -2
  505. data/lib/hecks/hecks/adapters/ledger/chain.rb +63 -0
  506. data/lib/hecks/hecks/adapters/ledger/journal.rb +105 -0
  507. data/lib/hecks/hecks/adapters/ledger.adapter +7 -0
  508. data/lib/hecks/hecks/adapters/ledger.rb +89 -0
  509. data/lib/hecks/hecks/adapters/local_files.rb +15 -1
  510. data/lib/hecks/hecks/adapters/pg_admin.rb +7 -1
  511. data/lib/hecks/hecks/adapters/rails_app_files.rb +64 -0
  512. data/lib/hecks/hecks/adapters/rehearsal/audit_report.rb +4 -1
  513. data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +9 -9
  514. data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +2 -2
  515. data/lib/hecks/hecks/adapters/rehearsal/postgres_census.rb +4 -4
  516. data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +3 -3
  517. data/lib/hecks/hecks/adapters/rehearsal/postgres_store.rb +4 -1
  518. data/lib/hecks/hecks/adapters/rehearsal/rust_audit.rb +1 -1
  519. data/lib/hecks/hecks/adapters/rehearsal/subject.rb +34 -5
  520. data/lib/hecks/hecks/adapters/rust_toolchain.rb +5 -3
  521. data/lib/hecks/hecks/adapters/settings_reading.adapter +7 -0
  522. data/lib/hecks/hecks/adapters/settings_reading.rb +142 -0
  523. data/lib/hecks/hecks/adapters/shell.rb +3 -0
  524. data/lib/hecks/hecks/adapters/site_toolchain.rb +8 -8
  525. data/lib/hecks/hecks/adapters/world_clearing.rb +2 -1
  526. data/lib/hecks/hecks/agents.bluebook +246 -0
  527. data/lib/hecks/hecks/codebase.behaviors +13 -1
  528. data/lib/hecks/hecks/codebase.bluebook +554 -586
  529. data/lib/hecks/hecks/composer.behaviors +10 -10
  530. data/lib/hecks/hecks/context_map.hecksagon +7 -6
  531. data/lib/hecks/hecks/custodian.behaviors +96 -1
  532. data/lib/hecks/hecks/custodian.bluebook +553 -157
  533. data/lib/hecks/hecks/deploy.behaviors +1 -1
  534. data/lib/hecks/hecks/deploy.world +3 -4
  535. data/lib/hecks/hecks/hecks.behaviors +2 -2
  536. data/lib/hecks/hecks/hecks.bluebook +9 -14
  537. data/lib/hecks/hecks/hecks.contextmap +25 -0
  538. data/lib/hecks/hecks/hecks.hecksagon +139 -6
  539. data/lib/hecks/hecks/hecks.world +22 -21
  540. data/lib/hecks/hecks/ledger.bluebook +190 -0
  541. data/lib/hecks/hecks/rehearsal.bluebook +43 -77
  542. data/lib/hecks/hecks/shapes.json +17 -0
  543. data/lib/hecks/hecks/site.behaviors +1 -1
  544. data/lib/hecks/hecks/site.world +3 -3
  545. data/lib/hecks/hecks/tooling.bluebook +170 -174
  546. data/lib/hecks/hecks/translations/2-148748.bluebook +2 -0
  547. data/lib/hecks/hecks/translations/2-15a2e4.bluebook +2 -0
  548. data/lib/hecks/hecks/translations/2-1b447c.bluebook +5 -0
  549. data/lib/hecks/hecks/translations/2-240474.bluebook +2 -0
  550. data/lib/hecks/hecks/translations/2-44446d.bluebook +2 -0
  551. data/lib/hecks/hecks/translations/2-46ca26.bluebook +2 -0
  552. data/lib/hecks/hecks/translations/2-473eab.bluebook +2 -0
  553. data/lib/hecks/hecks/translations/2-75cc28.bluebook +2 -0
  554. data/lib/hecks/hecks/translations/2-781c07.bluebook +2 -0
  555. data/lib/hecks/hecks/translations/2-84c18b.bluebook +2 -0
  556. data/lib/hecks/hecks/translations/2-aa5fff.bluebook +2 -0
  557. data/lib/hecks/hecks/translations/2-bfdb48.bluebook +2 -0
  558. data/lib/hecks/hecks/translations/2-cd96d8.bluebook +10 -0
  559. data/lib/hecks/hecks/translations/2-d43f71.bluebook +2 -0
  560. data/lib/hecks/hecks/translations/2-eea577.bluebook +2 -0
  561. data/lib/hecks/hecks/translations/2-f53248.bluebook +2 -0
  562. data/lib/hecks/hecks/translations/2-fbb677.bluebook +2 -0
  563. data/lib/hecks/hecks/translations/2-ffdf12.bluebook +6 -0
  564. data/lib/hecks/ir.rb +8 -1
  565. data/lib/hecks/kernel_capabilities/templates.rb +4 -3
  566. data/lib/hecks/kernel_capabilities.rb +6 -6
  567. data/lib/hecks/language/adapter.bluebook +61 -0
  568. data/lib/hecks/language/bluebook/aggregate.bluebook +36 -6
  569. data/lib/hecks/language/bluebook/attaches/paging.bluebook +1 -1
  570. data/lib/hecks/language/bluebook/bluebook.behaviors +6 -6
  571. data/lib/hecks/language/bluebook/bluebook.bluebook +28 -51
  572. data/lib/hecks/language/bluebook/bluebook.hecksagon +1 -1
  573. data/lib/hecks/language/bluebook/command.bluebook +34 -2
  574. data/lib/hecks/language/bluebook/entity.bluebook +2 -2
  575. data/lib/hecks/language/bluebook/policy.behaviors +2 -2
  576. data/lib/hecks/language/bluebook/policy.bluebook +3 -3
  577. data/lib/hecks/language/bluebook/process_manager.bluebook +3 -3
  578. data/lib/hecks/language/bluebook/projection.bluebook +11 -10
  579. data/lib/hecks/language/bluebook/query.bluebook +1 -0
  580. data/lib/hecks/language/bluebook/shape.bluebook +1 -1
  581. data/lib/hecks/language/bluebook/syntax.bluebook +3 -1
  582. data/lib/hecks/language/bluebook/vocabulary.bluebook +287 -6
  583. data/lib/hecks/language/context_map/context_map.bluebook +90 -0
  584. data/lib/hecks/language/hecksagon/domain_port.bluebook +2 -2
  585. data/lib/hecks/language/hecksagon/hecksagon.bluebook +8 -8
  586. data/lib/hecks/language/oidc.json +10 -0
  587. data/lib/hecks/language/port.bluebook +1 -1
  588. data/lib/hecks/language/sentence/sentence.bluebook +210 -0
  589. data/lib/hecks/language/world/world.bluebook +6 -6
  590. data/lib/hecks/literal.rb +8 -2
  591. data/lib/hecks/naming.rb +44 -14
  592. data/lib/hecks/persistence_legacy_fixture/postgres_stores.rb +7 -6
  593. data/lib/hecks/persistence_legacy_fixture/regenerate.rb +6 -2
  594. data/lib/hecks/ports/access_control.rb +2 -2
  595. data/lib/hecks/ports/agent/answers.rb +2 -2
  596. data/lib/hecks/ports/agent.rb +2 -2
  597. data/lib/hecks/ports/authentication.port +6 -0
  598. data/lib/hecks/ports/authentication.rb +67 -0
  599. data/lib/hecks/ports/authorization.rb +3 -3
  600. data/lib/hecks/ports/dispatch.port +5 -0
  601. data/lib/hecks/ports/dispatch.rb +28 -0
  602. data/lib/hecks/ports/key_vault.rb +9 -6
  603. data/lib/hecks/ports/loading.rb +23 -7
  604. data/lib/hecks/ports/persistence/append_only.rb +18 -1
  605. data/lib/hecks/ports/persistence/binding_policy.rb +70 -11
  606. data/lib/hecks/ports/persistence/codec_boundary.rb +3 -2
  607. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +10 -10
  608. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +4 -5
  609. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +11 -11
  610. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +1 -1
  611. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +6 -6
  612. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -3
  613. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +1 -1
  614. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +32 -19
  615. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +2 -2
  616. data/lib/hecks/ports/persistence/plugins/era.rb +0 -1
  617. data/lib/hecks/ports/persistence/remote_runtime.rb +3 -2
  618. data/lib/hecks/ports/persistence/set_statement.rb +29 -0
  619. data/lib/hecks/ports/persistence/set_transforming.rb +30 -0
  620. data/lib/hecks/ports/persistence/state_codec.rb +3 -3
  621. data/lib/hecks/ports/persistence.rb +0 -4
  622. data/lib/hecks/ports/projection.rb +4 -1
  623. data/lib/hecks/ports/query/cursor.rb +131 -0
  624. data/lib/hecks/ports/query/in_memory.rb +17 -10
  625. data/lib/hecks/ports/query.rb +4 -2
  626. data/lib/hecks/ports.rb +1 -0
  627. data/lib/hecks/projection_files/expression_tables.rb +3 -3
  628. data/lib/hecks/projection_files/sentence_table.rb +17 -0
  629. data/lib/hecks/projection_files.rb +16 -13
  630. data/lib/hecks/projections/bootstrap_table.rb +3 -3
  631. data/lib/hecks/projections/command_schema/shape.rb +95 -0
  632. data/lib/hecks/projections/command_schema.rb +74 -0
  633. data/lib/hecks/projections/context_map_diagram.rb +135 -0
  634. data/lib/hecks/projections/context_relationships.rb +125 -0
  635. data/lib/hecks/projections/deploy/box/access.rb +7 -4
  636. data/lib/hecks/projections/deploy/box/caddy.rb +2 -1
  637. data/lib/hecks/projections/deploy/box/compose.rb +4 -8
  638. data/lib/hecks/projections/deploy/box/edge.rb +2 -1
  639. data/lib/hecks/projections/deploy/box/hosting.rb +4 -2
  640. data/lib/hecks/projections/deploy/box/hosting_parts.rb +27 -12
  641. data/lib/hecks/projections/deploy/box/logging.rb +2 -3
  642. data/lib/hecks/projections/deploy/box/roll_recipe.rb +1 -3
  643. data/lib/hecks/projections/deploy/box/roll_scripts.rb +1 -16
  644. data/lib/hecks/projections/deploy/box/settings/containers.rb +29 -15
  645. data/lib/hecks/projections/deploy/box/settings/patterns.rb +5 -6
  646. data/lib/hecks/projections/deploy/box/settings/types.rb +5 -10
  647. data/lib/hecks/projections/deploy/box/settings.rb +9 -8
  648. data/lib/hecks/projections/deploy/box/stack_yaml.rb +10 -14
  649. data/lib/hecks/projections/deploy/box/templates/box.yaml.tmpl +0 -1
  650. data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +16 -3
  651. data/lib/hecks/projections/deploy/box/templates/hosting.mk.tmpl +7 -6
  652. data/lib/hecks/projections/deploy/box/templates/render-compose-taskdef.sh.tmpl +2 -6
  653. data/lib/hecks/projections/deploy/box/templates/render-compose.sh.tmpl +2 -6
  654. data/lib/hecks/projections/deploy/box.rb +16 -9
  655. data/lib/hecks/projections/deploy/fargate/assembly.rb +7 -2
  656. data/lib/hecks/projections/deploy/fargate/cdn/options.rb +4 -1
  657. data/lib/hecks/projections/deploy/fargate/check.rb +3 -1
  658. data/lib/hecks/projections/deploy/fargate/containers/rendering.rb +8 -5
  659. data/lib/hecks/projections/deploy/fargate/containers/routing.rb +2 -1
  660. data/lib/hecks/projections/deploy/fargate/monitoring/reading.rb +4 -1
  661. data/lib/hecks/projections/deploy/fargate/sections.rb +7 -4
  662. data/lib/hecks/projections/deploy/fargate/settings/ids.rb +4 -1
  663. data/lib/hecks/projections/deploy/fargate/settings/tuning.rb +13 -4
  664. data/lib/hecks/projections/deploy/fargate/stack/blocks.rb +6 -1
  665. data/lib/hecks/projections/deploy/fargate/stack/readers.rb +2 -1
  666. data/lib/hecks/projections/deploy/fargate/stack.rb +11 -6
  667. data/lib/hecks/projections/deploy/fargate/yaml.rb +10 -3
  668. data/lib/hecks/projections/deploy/fargate.rb +8 -3
  669. data/lib/hecks/projections/deploy/lambda/stack/blocks.rb +4 -2
  670. data/lib/hecks/projections/deploy/lambda/stack/deploy_chain.rb +2 -1
  671. data/lib/hecks/projections/deploy/lambda/stack/reading.rb +12 -9
  672. data/lib/hecks/projections/deploy/lambda/stack/reading_web.rb +7 -2
  673. data/lib/hecks/projections/deploy/lambda/stack/web.rb +2 -1
  674. data/lib/hecks/projections/deploy/lambda.rb +8 -3
  675. data/lib/hecks/projections/deploy/preview/containers.rb +14 -1
  676. data/lib/hecks/projections/deploy/preview/preview.sh.erb +2 -2
  677. data/lib/hecks/projections/deploy/preview/routing_yaml.rb +8 -6
  678. data/lib/hecks/projections/deploy/preview/settings.rb +23 -5
  679. data/lib/hecks/projections/deploy/preview/task_yaml.rb +1 -1
  680. data/lib/hecks/projections/deploy/preview/yaml_text.rb +2 -1
  681. data/lib/hecks/projections/deploy/scripts/settings.rb +5 -2
  682. data/lib/hecks/projections/deploy/shared.rb +12 -1
  683. data/lib/hecks/projections/deploy/shared_database_instance.rb +1 -1
  684. data/lib/hecks/projections/deploy/smoke.rb +3 -1
  685. data/lib/hecks/projections/deploy/template_diff/normalizer.rb +4 -1
  686. data/lib/hecks/projections/deploy/templates/lambda/mint_era_shared.tmpl +2 -2
  687. data/lib/hecks/projections/deploy/templates/lambda/nat_comment_oauth.tmpl +1 -1
  688. data/lib/hecks/projections/deploy/vercel.rb +1 -1
  689. data/lib/hecks/projections/diagrams/dispatch.rb +5 -3
  690. data/lib/hecks/projections/diagrams/statecharts.rb +8 -3
  691. data/lib/hecks/projections/diagrams/structure.rb +9 -6
  692. data/lib/hecks/projections/diagrams.rb +9 -7
  693. data/lib/hecks/projections/glossary/entries.rb +4 -4
  694. data/lib/hecks/projections/glossary/html/page.rb +4 -1
  695. data/lib/hecks/projections/glossary/html/parser.rb +13 -4
  696. data/lib/hecks/projections/glossary/html/sections.rb +4 -1
  697. data/lib/hecks/projections/glossary/html.rb +10 -3
  698. data/lib/hecks/projections/glossary/markdown.rb +4 -3
  699. data/lib/hecks/projections/glossary/mermaid.rb +8 -5
  700. data/lib/hecks/projections/glossary/sections.rb +1 -1
  701. data/lib/hecks/projections/glossary/sensitivity.rb +11 -4
  702. data/lib/hecks/projections/glossary/sentences/actions.rb +5 -2
  703. data/lib/hecks/projections/glossary/sentences/queries.rb +2 -1
  704. data/lib/hecks/projections/glossary/sentences/value_objects.rb +15 -6
  705. data/lib/hecks/projections/glossary/sentences.rb +6 -3
  706. data/lib/hecks/projections/glossary/slugs.rb +8 -1
  707. data/lib/hecks/projections/glossary.rb +3 -3
  708. data/lib/hecks/projections/model.rb +10 -6
  709. data/lib/hecks/projections/oidc.rb +19 -7
  710. data/lib/hecks/projections/parser_table.rb +10 -10
  711. data/lib/hecks/projections/phrase_table.rb +105 -0
  712. data/lib/hecks/projections/plain_english.rb +14 -4
  713. data/lib/hecks/projections/rails_app.rb +77 -0
  714. data/lib/hecks/projections/reference.rb +2 -2
  715. data/lib/hecks/projections/rust_vocabulary/arguments.rb +1 -1
  716. data/lib/hecks/projections/rust_vocabulary/tables.rb +20 -0
  717. data/lib/hecks/projections/rust_vocabulary.rb +14 -17
  718. data/lib/hecks/projections/site/admin.rb +7 -7
  719. data/lib/hecks/projections/site/admin_cms.rb +1 -1
  720. data/lib/hecks/projections/site/cms_editor/blocks.rb +5 -2
  721. data/lib/hecks/projections/site/cms_editor/checks.rb +9 -6
  722. data/lib/hecks/projections/site/cms_editor/color.rb +4 -1
  723. data/lib/hecks/projections/site/cms_editor/composition.rb +20 -18
  724. data/lib/hecks/projections/site/cms_editor/destructive.rb +4 -1
  725. data/lib/hecks/projections/site/cms_editor/drafts.rb +6 -2
  726. data/lib/hecks/projections/site/cms_editor/key_kinds.rb +4 -1
  727. data/lib/hecks/projections/site/cms_editor/limits.rb +1 -1
  728. data/lib/hecks/projections/site/cms_editor/media.rb +1 -1
  729. data/lib/hecks/projections/site/cms_editor/moment.rb +3 -1
  730. data/lib/hecks/projections/site/cms_editor/pickers.rb +1 -1
  731. data/lib/hecks/projections/site/cms_editor/preview.rb +10 -4
  732. data/lib/hecks/projections/site/cms_editor/relations.rb +3 -2
  733. data/lib/hecks/projections/site/cms_editor/scale_checks.rb +32 -25
  734. data/lib/hecks/projections/site/cms_editor/scheduling.rb +1 -1
  735. data/lib/hecks/projections/site/cms_editor/schema.rb +31 -29
  736. data/lib/hecks/projections/site/cms_editor/setting.rb +34 -27
  737. data/lib/hecks/projections/site/cms_editor/targets.rb +9 -6
  738. data/lib/hecks/projections/site/cms_editor/templates/src/app.ts.tmpl +3 -3
  739. data/lib/hecks/projections/site/cms_editor/templates/src/commands.ts.tmpl +2 -2
  740. data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +8 -3
  741. data/lib/hecks/projections/site/cms_editor/templates/src/media/handler.ts.tmpl +4 -4
  742. data/lib/hecks/projections/site/cms_editor/templates/src/names.ts.tmpl +14 -14
  743. data/lib/hecks/projections/site/cms_editor/templates/src/schema.ts.tmpl +12 -12
  744. data/lib/hecks/projections/site/cms_editor/templates/src/ui/pages.ts.tmpl +12 -12
  745. data/lib/hecks/projections/site/cms_editor/templates/src/ui/pickers.ts.tmpl +1 -1
  746. data/lib/hecks/projections/site/cms_editor/templates/src/ui/schedule.ts.tmpl +2 -2
  747. data/lib/hecks/projections/site/cms_editor/templates/src/ui/shell.ts.tmpl +11 -11
  748. data/lib/hecks/projections/site/cms_editor/templates/src/ui/world.ts.tmpl +3 -3
  749. data/lib/hecks/projections/site/cms_editor/theme_checks.rb +8 -2
  750. data/lib/hecks/projections/site/cms_editor.rb +19 -16
  751. data/lib/hecks/projections/site/edge/checks.rb +1 -1
  752. data/lib/hecks/projections/site/edge/function_checks.rb +1 -1
  753. data/lib/hecks/projections/site/edge/function_code.rb +4 -1
  754. data/lib/hecks/projections/site/edge/pattern.rb +4 -1
  755. data/lib/hecks/projections/site/edge/route_checks.rb +2 -2
  756. data/lib/hecks/projections/site/edge/rules.rb +1 -1
  757. data/lib/hecks/projections/site/edge.rb +14 -8
  758. data/lib/hecks/projections/site/live_edge.rb +2 -2
  759. data/lib/hecks/projections/site/payload/fields/definitions.rb +1 -1
  760. data/lib/hecks/projections/site/payload/lifecycle/text.rb +1 -1
  761. data/lib/hecks/projections/site/payload/model.rb +16 -12
  762. data/lib/hecks/projections/site/payload/specs.rb +4 -2
  763. data/lib/hecks/projections/site/payload_driver.rb +9 -8
  764. data/lib/hecks/projections/site/probe.rb +12 -3
  765. data/lib/hecks/projections/site/regions.rb +9 -1
  766. data/lib/hecks/projections/site/role_probe.rb +7 -7
  767. data/lib/hecks/projections/site/root_rows.rb +4 -4
  768. data/lib/hecks/projections/site/routes_ts/listings.rb +13 -6
  769. data/lib/hecks/projections/site/routes_ts.rb +6 -3
  770. data/lib/hecks/projections/site/site_admin.rb +1 -1
  771. data/lib/hecks/projections/site/site_cdn.rb +3 -2
  772. data/lib/hecks/projections/site/site_host.rb +1 -1
  773. data/lib/hecks/projections/site/site_root.rb +4 -4
  774. data/lib/hecks/projections/site/table/checks.rb +4 -1
  775. data/lib/hecks/projections/site/table/navigation_checks.rb +2 -2
  776. data/lib/hecks/projections/site/table/row_builder.rb +7 -7
  777. data/lib/hecks/projections/site/table/row_defaults.rb +1 -1
  778. data/lib/hecks/projections/site/table.rb +32 -26
  779. data/lib/hecks/projections/statements/actions.rb +4 -1
  780. data/lib/hecks/projections/statements/relationships.rb +15 -2
  781. data/lib/hecks/projections/statements.rb +2 -2
  782. data/lib/hecks/projections/vocabulary.rb +6 -6
  783. data/lib/hecks/projections/world_skeleton.rb +68 -0
  784. data/lib/hecks/projections.rb +5 -0
  785. data/lib/hecks/projector/cli_audience.rb +12 -9
  786. data/lib/hecks/projector/cli_projector/claims.rb +8 -8
  787. data/lib/hecks/projector/cli_projector/command_specs.rb +9 -5
  788. data/lib/hecks/projector/cli_projector/listing.rb +20 -8
  789. data/lib/hecks/projector/cli_projector/option_specs.rb +1 -1
  790. data/lib/hecks/projector/cli_projector/query_specs.rb +1 -1
  791. data/lib/hecks/projector/cli_projector/usage.rb +12 -8
  792. data/lib/hecks/projector/cli_projector.rb +2 -2
  793. data/lib/hecks/projector/docs_projector/behaviour.rb +5 -4
  794. data/lib/hecks/projector/docs_projector/closing.rb +3 -3
  795. data/lib/hecks/projector/docs_projector.rb +12 -7
  796. data/lib/hecks/projector/exporter/adapters.rb +15 -2
  797. data/lib/hecks/projector/exporter/bindings.rb +39 -134
  798. data/lib/hecks/projector/exporter/plugins.rb +55 -0
  799. data/lib/hecks/projector/exporter.rb +2 -0
  800. data/lib/hecks/projector/narrate_projector/commands.rb +4 -3
  801. data/lib/hecks/projector/narrate_projector/reactions.rb +2 -2
  802. data/lib/hecks/projector/narrate_projector.rb +9 -8
  803. data/lib/hecks/projector/target.rb +8 -8
  804. data/lib/hecks/projector.rb +9 -9
  805. data/lib/hecks/quality_control/adapters/agent.rb +2 -1
  806. data/lib/hecks/quality_control/adapters/github_checks.adapter +1 -1
  807. data/lib/hecks/quality_control/adapters/github_issues.adapter +1 -1
  808. data/lib/hecks/quality_control/adapters/github_issues.rb +6 -2
  809. data/lib/hecks/quality_control/adapters/target_tools.rb +9 -3
  810. data/lib/hecks/quality_control/cli/child.rb +1 -0
  811. data/lib/hecks/quality_control/cli/qa_domain_novelty/verdict.rb +1 -1
  812. data/lib/hecks/quality_control/cli/qa_generated_domains/evaluation.rb +12 -2
  813. data/lib/hecks/quality_control/cli/qa_generated_domains/promotion.rb +4 -1
  814. data/lib/hecks/quality_control/cli/qa_generated_domains.rb +4 -1
  815. data/lib/hecks/quality_control/cli/qa_log_bug/proof.rb +16 -3
  816. data/lib/hecks/quality_control/cli/qa_mine_combinations/booting.rb +4 -2
  817. data/lib/hecks/quality_control/cli/qa_mutate.rb +161 -0
  818. data/lib/hecks/quality_control/cli/qa_sweep/all_mode/report.rb +4 -1
  819. data/lib/hecks/quality_control/cli/qa_sweep/parsing.rb +2 -1
  820. data/lib/hecks/quality_control/cli/qa_sweep.rb +4 -1
  821. data/lib/hecks/quality_control/cli/qa_tick/output.rb +28 -26
  822. data/lib/hecks/quality_control/cli/qa_tick/preflight.rb +12 -3
  823. data/lib/hecks/quality_control/cli/qa_tick.rb +24 -8
  824. data/lib/hecks/quality_control/quality_control.bluebook +93 -92
  825. data/lib/hecks/quality_control/quality_control.hecksagon +3 -3
  826. data/lib/hecks/quality_control/quality_control.ports.hecksagon +3 -2
  827. data/lib/hecks/query_ir/rules.rb +9 -9
  828. data/lib/hecks/query_ir/touchpoints.rb +5 -2
  829. data/lib/hecks/query_ir.rb +3 -3
  830. data/lib/hecks/query_specification/common/comparison.rb +1 -1
  831. data/lib/hecks/query_specification/common/dsl.rb +3 -4
  832. data/lib/hecks/query_specification/hop_path.rb +7 -4
  833. data/lib/hecks/rails_app/bluebook/rails_app.bluebook +60 -0
  834. data/lib/hecks/rails_app/bluebook/rails_app.hecksagon +4 -0
  835. data/lib/hecks/refused.rb +104 -0
  836. data/lib/hecks/release/approval.rb +9 -2
  837. data/lib/hecks/release/gem_pin.rb +7 -3
  838. data/lib/hecks/release/lane.rb +1 -1
  839. data/lib/hecks/release/runner/clean_tree.rb +4 -1
  840. data/lib/hecks/release/runner/console.rb +2 -1
  841. data/lib/hecks/router/aggregate_reader.rb +3 -4
  842. data/lib/hecks/router/namespace_installer.rb +31 -17
  843. data/lib/hecks/router.rb +2 -10
  844. data/lib/hecks/runtime/adapter_lookup.rb +30 -7
  845. data/lib/hecks/runtime/adapter_namespace.rb +21 -0
  846. data/lib/hecks/runtime/asking.rb +30 -0
  847. data/lib/hecks/runtime/caller.rb +22 -0
  848. data/lib/hecks/runtime/capability_graph.rb +2 -1
  849. data/lib/hecks/runtime/collecting.rb +86 -0
  850. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +13 -8
  851. data/lib/hecks/runtime/command_interpreter/delegation.rb +46 -17
  852. data/lib/hecks/runtime/command_interpreter/hydration.rb +17 -21
  853. data/lib/hecks/runtime/command_interpreter/list_mutations.rb +2 -1
  854. data/lib/hecks/runtime/command_interpreter/persisting.rb +13 -12
  855. data/lib/hecks/runtime/command_interpreter.rb +17 -10
  856. data/lib/hecks/runtime/command_rules/admissibility.rb +17 -10
  857. data/lib/hecks/runtime/command_rules/authorization.rb +8 -6
  858. data/lib/hecks/runtime/command_rules/guard_state.rb +10 -10
  859. data/lib/hecks/runtime/command_rules/invariants.rb +3 -1
  860. data/lib/hecks/runtime/command_rules/missing_reference.rb +25 -0
  861. data/lib/hecks/runtime/command_rules/one_of_references.rb +44 -0
  862. data/lib/hecks/runtime/command_rules/references.rb +14 -14
  863. data/lib/hecks/runtime/command_rules.rb +2 -0
  864. data/lib/hecks/runtime/declarations.rb +66 -0
  865. data/lib/hecks/runtime/dependency_planning/analyzer.rb +2 -1
  866. data/lib/hecks/runtime/dependency_planning/expression_reads.rb +3 -2
  867. data/lib/hecks/runtime/dependency_planning/mutation_analysis.rb +2 -1
  868. data/lib/hecks/runtime/dispatch_claim.rb +72 -0
  869. data/lib/hecks/runtime/dispatch_context.rb +41 -0
  870. data/lib/hecks/runtime/dispatcher/queries.rb +17 -0
  871. data/lib/hecks/runtime/dispatcher/routing.rb +34 -12
  872. data/lib/hecks/runtime/dispatcher/validating.rb +73 -0
  873. data/lib/hecks/runtime/dispatcher.rb +39 -23
  874. data/lib/hecks/runtime/entity_element/location.rb +15 -7
  875. data/lib/hecks/runtime/entity_element/mutations.rb +4 -1
  876. data/lib/hecks/runtime/entity_interpreter/enforcement.rb +1 -1
  877. data/lib/hecks/runtime/entity_interpreter/locating.rb +1 -1
  878. data/lib/hecks/runtime/entity_interpreter/saving.rb +5 -5
  879. data/lib/hecks/runtime/entity_interpreter.rb +11 -4
  880. data/lib/hecks/runtime/errors.rb +30 -17
  881. data/lib/hecks/runtime/identity.rb +9 -0
  882. data/lib/hecks/runtime/instance.rb +3 -2
  883. data/lib/hecks/runtime/interpreting.rb +63 -6
  884. data/lib/hecks/runtime/invocation_builder.rb +7 -4
  885. data/lib/hecks/runtime/invocation_facts.rb +13 -8
  886. data/lib/hecks/runtime/loader/boot_steps.rb +3 -2
  887. data/lib/hecks/runtime/loader/described.rb +4 -0
  888. data/lib/hecks/runtime/loader.rb +52 -62
  889. data/lib/hecks/runtime/outbox/redrive.rb +105 -0
  890. data/lib/hecks/runtime/outbox/relay.rb +11 -39
  891. data/lib/hecks/runtime/outbox/runnable.rb +60 -0
  892. data/lib/hecks/runtime/outbox.rb +15 -1
  893. data/lib/hecks/runtime/policy_interpreter/arguments.rb +7 -1
  894. data/lib/hecks/runtime/policy_interpreter/fan_out.rb +8 -35
  895. data/lib/hecks/runtime/policy_interpreter.rb +8 -0
  896. data/lib/hecks/runtime/port_operation_interpreter.rb +27 -13
  897. data/lib/hecks/runtime/query_interpreter/outside_answer.rb +1 -1
  898. data/lib/hecks/runtime/query_interpreter/paging.rb +42 -0
  899. data/lib/hecks/runtime/query_interpreter/plugin_answer.rb +54 -0
  900. data/lib/hecks/runtime/query_interpreter/store_specification.rb +32 -0
  901. data/lib/hecks/runtime/query_interpreter.rb +16 -9
  902. data/lib/hecks/runtime/reaction_invocation/target_resolution.rb +10 -4
  903. data/lib/hecks/runtime/reaction_invocation.rb +5 -2
  904. data/lib/hecks/runtime/read_model_interpreter/head_ordering.rb +1 -1
  905. data/lib/hecks/runtime/read_model_interpreter/reductions.rb +23 -15
  906. data/lib/hecks/runtime/read_model_interpreter/shaping.rb +18 -7
  907. data/lib/hecks/runtime/read_model_interpreter.rb +5 -2
  908. data/lib/hecks/runtime/reference_hop.rb +3 -2
  909. data/lib/hecks/runtime/refusal_wording.rb +15 -6
  910. data/lib/hecks/runtime/registry/adapter_capabilities.rb +43 -0
  911. data/lib/hecks/runtime/registry/ask_verification.rb +15 -4
  912. data/lib/hecks/runtime/registry/{chapter_verification.rb → bluebook_verification.rb} +23 -23
  913. data/lib/hecks/runtime/registry/context_maps.rb +30 -0
  914. data/lib/hecks/runtime/registry/declarations.rb +8 -6
  915. data/lib/hecks/runtime/registry/durability_warnings.rb +18 -2
  916. data/lib/hecks/runtime/registry/hecksagon_rules.rb +27 -24
  917. data/lib/hecks/runtime/registry/plugin_query_verification.rb +45 -0
  918. data/lib/hecks/runtime/registry/providers.rb +34 -99
  919. data/lib/hecks/runtime/registry/query_verification.rb +30 -21
  920. data/lib/hecks/runtime/registry/runtime_state.rb +4 -3
  921. data/lib/hecks/runtime/registry/saga_persistence.rb +7 -16
  922. data/lib/hecks/runtime/registry/verification.rb +9 -5
  923. data/lib/hecks/runtime/registry/world_defaults.rb +8 -5
  924. data/lib/hecks/runtime/registry.rb +52 -27
  925. data/lib/hecks/runtime/row_sweep.rb +104 -0
  926. data/lib/hecks/runtime/saga_interpreter/compensation.rb +5 -4
  927. data/lib/hecks/runtime/saga_interpreter/delivery.rb +13 -10
  928. data/lib/hecks/runtime/saga_interpreter/redrive.rb +141 -0
  929. data/lib/hecks/runtime/saga_interpreter/transitions.rb +35 -6
  930. data/lib/hecks/runtime/saga_interpreter.rb +36 -4
  931. data/lib/hecks/runtime/saga_pending_dispatch.rb +4 -2
  932. data/lib/hecks/runtime/scalar_field.rb +73 -0
  933. data/lib/hecks/runtime/tenant_scope.rb +2 -1
  934. data/lib/hecks/runtime/validation.rb +24 -0
  935. data/lib/hecks/runtime/value/admission.rb +11 -11
  936. data/lib/hecks/runtime/value/coercion.rb +7 -7
  937. data/lib/hecks/runtime/value/field_checks.rb +13 -13
  938. data/lib/hecks/runtime/value/invariant_violation.rb +4 -1
  939. data/lib/hecks/runtime/value/references.rb +7 -4
  940. data/lib/hecks/runtime/value/unwrapping.rb +60 -0
  941. data/lib/hecks/runtime/value/validation.rb +7 -4
  942. data/lib/hecks/runtime/value.rb +3 -27
  943. data/lib/hecks/runtime/wasm_plugin.rb +98 -0
  944. data/lib/hecks/runtime.rb +14 -14
  945. data/lib/hecks/rust_build/command_schemas.rb +27 -0
  946. data/lib/hecks/rust_build/conformance/surface.rb +50 -7
  947. data/lib/hecks/rust_build/conformance.rb +25 -3
  948. data/lib/hecks/rust_build/conformance_fuzz.rb +2 -2
  949. data/lib/hecks/rust_build/domain_name.rb +4 -1
  950. data/lib/hecks/rust_build/host.rb +15 -9
  951. data/lib/hecks/rust_build/native_build.rb +2 -1
  952. data/lib/hecks/rust_build/prebuilt_binary.rb +25 -5
  953. data/lib/hecks/rust_build/project_rust/codegen_run.rb +11 -10
  954. data/lib/hecks/rust_build/project_rust/domain_loader.rb +15 -2
  955. data/lib/hecks/rust_build/project_rust/root_mod/notes.rb +1 -1
  956. data/lib/hecks/rust_build/project_rust/target_ir.rb +12 -3
  957. data/lib/hecks/rust_build/project_rust.rb +39 -19
  958. data/lib/hecks/rust_build/target_dir.rb +40 -0
  959. data/lib/hecks/rust_build/wasm.rb +46 -17
  960. data/lib/hecks/rust_build/wasm_browser.rb +8 -2
  961. data/lib/hecks/rust_build.rb +29 -2
  962. data/lib/hecks/sentence_form.rb +212 -0
  963. data/lib/hecks/served_options.rb +2 -1
  964. data/lib/hecks/shape_ledger.rb +149 -0
  965. data/lib/hecks/site/bluebook/site.bluebook +39 -62
  966. data/lib/hecks/site/bluebook/site.ports.hecksagon +1 -1
  967. data/lib/hecks/sme/bluebook/sme.bluebook +7 -1
  968. data/lib/hecks/storehouse/audit.rb +4 -1
  969. data/lib/hecks/storehouse/bluebook_words.rb +35 -0
  970. data/lib/hecks/storehouse/dispatching.rb +6 -3
  971. data/lib/hecks/storehouse/introspection.rb +4 -4
  972. data/lib/hecks/storehouse/querying.rb +2 -2
  973. data/lib/hecks/storehouse.rb +14 -4
  974. data/lib/hecks/switches.rb +21 -0
  975. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +5 -9
  976. data/lib/hecks/text.rb +57 -0
  977. data/lib/hecks/tickets/bluebook/tickets.bluebook +30 -51
  978. data/lib/hecks/tickets/bluebook/tickets.hecksagon +1 -1
  979. data/lib/hecks/tickets/bluebook/tickets.ports.hecksagon +1 -1
  980. data/lib/hecks/tools/argument_gate_matrix/dispatching.rb +1 -1
  981. data/lib/hecks/tools/argument_gate_matrix/rows.rb +7 -7
  982. data/lib/hecks/tools/argument_gate_matrix/violations.rb +1 -1
  983. data/lib/hecks/tools/argument_gate_matrix.rb +3 -3
  984. data/lib/hecks/tools/ci_gate_decision.rb +5 -2
  985. data/lib/hecks/tools/ci_gates.rb +1 -1
  986. data/lib/hecks/tools/comment_style/caps_words.rb +11 -8
  987. data/lib/hecks/tools/comment_style/dictionary.rb +7 -2
  988. data/lib/hecks/tools/comment_style/fixing.rb +10 -5
  989. data/lib/hecks/tools/comment_style/long_blocks.rb +1 -1
  990. data/lib/hecks/tools/comment_style/masking.rb +5 -5
  991. data/lib/hecks/tools/comment_style/run.rb +4 -1
  992. data/lib/hecks/tools/comment_style/source_file.rb +10 -3
  993. data/lib/hecks/tools/comment_style/structure_checks.rb +13 -4
  994. data/lib/hecks/tools/comment_style/text_checks.rb +1 -1
  995. data/lib/hecks/tools/comment_style.rb +25 -2
  996. data/lib/hecks/tools/deploy_recipe/generation.rb +1 -1
  997. data/lib/hecks/tools/deploy_recipe/targets.rb +1 -1
  998. data/lib/hecks/tools/deploy_recipe/worlds.rb +15 -9
  999. data/lib/hecks/tools/deploy_recipe.rb +17 -10
  1000. data/lib/hecks/tools/deploy_recipe_lint/fixtures.rb +1 -1
  1001. data/lib/hecks/tools/deploy_recipe_lint/makefile.rb +7 -1
  1002. data/lib/hecks/tools/drop_implicit_append_fields.rb +4 -1
  1003. data/lib/hecks/tools/era_projection_backfill.rb +11 -6
  1004. data/lib/hecks/tools/era_reattest.rb +11 -6
  1005. data/lib/hecks/tools/fuzz_sweep/findings.rb +18 -2
  1006. data/lib/hecks/tools/fuzz_sweep/options.rb +5 -1
  1007. data/lib/hecks/tools/fuzz_sweep.rb +17 -5
  1008. data/lib/hecks/tools/gate.rb +27 -5
  1009. data/lib/hecks/tools/hoist_local_givens/rewriting.rb +3 -2
  1010. data/lib/hecks/tools/hoist_local_givens.rb +2 -4
  1011. data/lib/hecks/tools/lanes/prebuilt_workflow.rb +1 -1
  1012. data/lib/hecks/tools/lanes/rows.rb +9 -3
  1013. data/lib/hecks/tools/lanes/ruleset.rb +1 -1
  1014. data/lib/hecks/tools/lanes/watch_workflow.rb +1 -1
  1015. data/lib/hecks/tools/lanes/workflow.rb +1 -1
  1016. data/lib/hecks/tools/lanes.rb +1 -1
  1017. data/lib/hecks/tools/oidc_manifests.rb +16 -19
  1018. data/lib/hecks/tools/regeneration_run.rb +22 -17
  1019. data/lib/hecks/tools/release_only.rb +5 -2
  1020. data/lib/hecks/tools/rust_comment_style/caps_words.rb +5 -5
  1021. data/lib/hecks/tools/rust_comment_style/fixing.rb +1 -1
  1022. data/lib/hecks/tools/rust_comment_style/masking.rb +7 -4
  1023. data/lib/hecks/tools/rust_comment_style/source_file.rb +7 -2
  1024. data/lib/hecks/tools/rust_comment_style/text_checks.rb +1 -1
  1025. data/lib/hecks/tools/rust_comment_style/tokenizer.rb +4 -1
  1026. data/lib/hecks/tools/site_routes/editor_files.rb +16 -13
  1027. data/lib/hecks/tools/site_routes/root_files.rb +14 -13
  1028. data/lib/hecks/tools/site_routes/templates.rb +5 -4
  1029. data/lib/hecks/tools/site_routes.rb +18 -15
  1030. data/lib/hecks/tools/tail_merge.rb +3 -3
  1031. data/lib/hecks/tools/tenant_provisioning.rb +6 -6
  1032. data/lib/hecks/tools/tools_doc.rb +16 -16
  1033. data/lib/hecks/tools/translation_audit/domain_loading.rb +3 -3
  1034. data/lib/hecks/tools/translation_audit.rb +15 -4
  1035. data/lib/hecks/tools/translation_scaffold/reporting.rb +4 -1
  1036. data/lib/hecks/tools/translation_scaffold.rb +4 -14
  1037. data/lib/hecks/vendoring/git_source.rb +7 -4
  1038. data/lib/hecks/version.rb +1 -1
  1039. data/lib/hecks/vocabulary.rb +110 -3
  1040. data/lib/hecks.rb +37 -18
  1041. data/lib/rubocop/cop/hecks/inline_regexp.rb +29 -0
  1042. data/lib/rubocop/cop/hecks/no_transport_in_runtime.rb +78 -0
  1043. data/lib/rubocop/cop/hecks/rails_model_rules.rb +46 -0
  1044. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +1 -1
  1045. data/lib/rubocop/cop/hecks/train_wreck.rb +69 -0
  1046. data/lib/rubocop/cop/hecks/unnamed_pipeline.rb +51 -0
  1047. data/prebuilt/31cf81e12f16236ad26249be590cfea3799c9db67432e6a4ef54b801c355b6ae.marshal +0 -0
  1048. data/prebuilt/68ecb2bd3abb1a221007db4f2b0375834c13aaccac8f2b99778e8517f11aa113.marshal +0 -0
  1049. data/prebuilt/verdicts-0328c2403383837d780ad5ed81d1da351c56436b894cf9015b5d56b8f1f1bf10.json +1 -0
  1050. data/qa/settings.yml +9 -0
  1051. data/rust/Cargo.lock +8 -1
  1052. data/rust/Cargo.toml +6 -7
  1053. data/rust/build/src/binding_pass.rs +13 -17
  1054. data/rust/build/src/cargo_sync.rs +1 -1
  1055. data/rust/build/src/lineage_pass.rs +4 -71
  1056. data/rust/build/src/pipeline.rs +99 -70
  1057. data/rust/build/src/resolve.rs +85 -29
  1058. data/rust/build/src/seams_pass.rs +117 -99
  1059. data/rust/build/src/world_pass.rs +21 -12
  1060. data/rust/codegen/src/domain_generator.rs +30 -2
  1061. data/rust/codegen/src/expr_emitter.rs +6 -0
  1062. data/rust/codegen/src/json_codec.rs +48 -10
  1063. data/rust/codegen/src/main.rs +41 -34
  1064. data/rust/codegen/src/manifest.rs +1 -1
  1065. data/rust/codegen/src/naming.rs +5 -5
  1066. data/rust/codegen/src/one_of_references.rs +124 -0
  1067. data/rust/codegen/src/presenters.rs +335 -0
  1068. data/rust/codegen/src/queries.rs +56 -7
  1069. data/rust/codegen/src/reactions.rs +19 -14
  1070. data/rust/codegen/src/read_models.rs +20 -71
  1071. data/rust/codegen/src/registry.rs +89 -15
  1072. data/rust/codegen/src/sidecars.rs +1 -1
  1073. data/rust/expr/Cargo.lock +54 -0
  1074. data/rust/expr/Cargo.toml +52 -0
  1075. data/rust/{src/kernel → expr/src}/attribute_shapes/composite.rs +45 -14
  1076. data/rust/{src/kernel → expr/src}/attribute_shapes/list.rs +1 -1
  1077. data/rust/{src/kernel → expr/src}/attribute_shapes/mod.rs +2 -2
  1078. data/rust/{src/kernel → expr/src}/attribute_shapes/optional.rs +1 -1
  1079. data/rust/{src/kernel → expr/src}/attribute_shapes/scalar.rs +3 -3
  1080. data/rust/{src/kernel → expr/src}/expr.rs +30 -8
  1081. data/rust/{src/kernel → expr/src}/expression_operators/arithmetic.rs +41 -18
  1082. data/rust/{src/kernel → expr/src}/expression_operators/comparison.rs +54 -8
  1083. data/rust/{src/kernel → expr/src}/expression_operators/enumeration.rs +73 -6
  1084. data/rust/{src/kernel → expr/src}/expression_operators/logical.rs +2 -2
  1085. data/rust/expr/src/expression_operators/membership.rs +33 -0
  1086. data/rust/{src/kernel → expr/src}/expression_operators/mod.rs +2 -2
  1087. data/rust/expr/src/expression_operators/pattern_match.rs +179 -0
  1088. data/rust/{src/kernel → expr/src}/expression_operators/positional.rs +8 -6
  1089. data/rust/{src/kernel → expr/src}/expression_operators/presence.rs +7 -6
  1090. data/rust/{src/kernel → expr/src}/expression_operators/sign_test.rs +14 -4
  1091. data/rust/{src/kernel → expr/src}/expression_operators/sized.rs +16 -9
  1092. data/rust/{src/kernel → expr/src}/expression_operators/text.rs +30 -17
  1093. data/rust/{src/kernel → expr/src}/expression_operators/to_string.rs +8 -6
  1094. data/rust/expr/src/lib.rs +17 -0
  1095. data/rust/expr/src/refusal.rs +62 -0
  1096. data/rust/expr/src/rendering.rs +135 -0
  1097. data/rust/{src/kernel → expr/src}/ruby_float.rs +7 -6
  1098. data/rust/host/Cargo.lock +137 -123
  1099. data/rust/host/Cargo.toml +7 -3
  1100. data/rust/host/HECKS_RELEASE +1 -1
  1101. data/rust/host/core/Cargo.toml +64 -0
  1102. data/rust/host/core/clippy.toml +4 -0
  1103. data/rust/host/core/src/arg_limits.rs +48 -0
  1104. data/rust/host/core/src/clock.rs +146 -0
  1105. data/rust/host/core/src/dispatch.rs +920 -0
  1106. data/rust/host/core/src/expr_json.rs +1137 -0
  1107. data/rust/host/{src → core/src}/host.rs +13 -14
  1108. data/rust/host/{src → core/src}/ir.rs +227 -18
  1109. data/rust/host/core/src/journal.rs +126 -0
  1110. data/rust/host/{src → core/src}/lambda_client.rs +27 -96
  1111. data/rust/host/core/src/lib.rs +37 -0
  1112. data/rust/host/{src → core/src}/needs.rs +66 -20
  1113. data/rust/host/core/src/plugins.rs +269 -0
  1114. data/rust/host/core/src/presentation.rs +471 -0
  1115. data/rust/host/{src → core/src}/presentation_write.rs +75 -43
  1116. data/rust/host/{src → core/src}/query_step.rs +54 -16
  1117. data/rust/host/{src → core/src}/reference_validate.rs +6 -6
  1118. data/rust/host/{src → core/src}/storage_shape.rs +2 -2
  1119. data/rust/host/core/src/store/memory.rs +635 -0
  1120. data/rust/host/core/src/store.rs +418 -0
  1121. data/rust/host/{src → core/src}/ui_schema.rs +8 -8
  1122. data/rust/host/core/src/validation.rs +129 -0
  1123. data/rust/host/core/src/wasm_runner.rs +410 -0
  1124. data/rust/host/fuzz/.gitignore +4 -0
  1125. data/rust/host/fuzz/Cargo.lock +4479 -0
  1126. data/rust/host/fuzz/Cargo.toml +42 -0
  1127. data/rust/host/fuzz/README.md +55 -0
  1128. data/rust/host/fuzz/fuzz_targets/postgres_equivalence.rs +24 -0
  1129. data/rust/host/fuzz/src/backend.rs +273 -0
  1130. data/rust/host/fuzz/src/equivalence.rs +195 -0
  1131. data/rust/host/fuzz/src/footprint.rs +182 -0
  1132. data/rust/host/fuzz/src/lib.rs +17 -0
  1133. data/rust/host/fuzz/src/ops.rs +345 -0
  1134. data/rust/host/fuzz/tests/smoke.rs +50 -0
  1135. data/rust/host/src/api.rs +316 -105
  1136. data/rust/host/src/auth/google_signin.rs +183 -0
  1137. data/rust/host/src/auth.rs +527 -370
  1138. data/rust/host/src/aws_lambda_invoker.rs +61 -0
  1139. data/rust/host/src/bin/expr_harness.rs +9 -4
  1140. data/rust/host/src/bin/ledger.rs +84 -0
  1141. data/rust/host/src/bin/lineage_harness.rs +17 -21
  1142. data/rust/host/src/bin/mint_harness.rs +11 -36
  1143. data/rust/host/src/bin/validate_harness.rs +1 -6
  1144. data/rust/host/src/boot.rs +465 -174
  1145. data/rust/host/src/boundary_fuzz/ir.rs +10 -5
  1146. data/rust/host/src/boundary_fuzz/server.rs +6 -5
  1147. data/rust/host/src/checkout.rs +4 -1
  1148. data/rust/host/src/commerce_ir.rs +126 -105
  1149. data/rust/host/src/{dispatch.rs → dispatch_tests.rs} +391 -824
  1150. data/rust/host/src/extension.rs +3 -4
  1151. data/rust/host/src/hecksagon_boundary_tests.rs +149 -0
  1152. data/rust/host/src/ledger.rs +683 -0
  1153. data/rust/host/src/lib.rs +21 -28
  1154. data/rust/host/src/payments/tests.rs +136 -38
  1155. data/rust/host/src/payments.rs +36 -32
  1156. data/rust/host/src/platform_signin/extension.rs +1 -1
  1157. data/rust/host/src/platform_signin/mapping_tests.rs +12 -9
  1158. data/rust/host/src/platform_signin/session.rs +7 -14
  1159. data/rust/host/src/platform_signin/verify.rs +0 -2
  1160. data/rust/host/src/presentation_tests.rs +382 -0
  1161. data/rust/host/src/rate_limit.rs +18 -2
  1162. data/rust/host/src/server.rs +287 -36
  1163. data/rust/host/src/{journal.rs → store/journal_sql.rs} +21 -127
  1164. data/rust/host/src/{mint.rs → store/mint.rs} +12 -12
  1165. data/rust/host/src/store/postgres.rs +493 -0
  1166. data/rust/host/src/store.rs +97 -0
  1167. data/rust/host/src/store_conformance.rs +1265 -0
  1168. data/rust/host/src/web/commerce.rs +10 -18
  1169. data/rust/host/src/web/newsletter/cooldown.rs +107 -22
  1170. data/rust/host/src/web/newsletter/naming.rs +104 -0
  1171. data/rust/host/src/web/newsletter/tests.rs +53 -0
  1172. data/rust/host/src/web/newsletter.rs +73 -50
  1173. data/rust/host/src/web/newsletter_send.rs +12 -13
  1174. data/rust/host/src/web/registration_receipt.rs +2 -2
  1175. data/rust/host/src/web/registrations.rs +754 -375
  1176. data/rust/host/src/web/resend_webhook.rs +3 -3
  1177. data/rust/host/src/web/signin_tests.rs +131 -0
  1178. data/rust/host/src/web.rs +1479 -149
  1179. data/rust/lsp/README.md +27 -27
  1180. data/rust/lsp/src/diagnostics.rs +75 -15
  1181. data/rust/lsp/src/main.rs +101 -5
  1182. data/rust/parser/fuzz/Cargo.toml +2 -2
  1183. data/rust/parser/fuzz/README.md +3 -3
  1184. data/rust/parser/fuzz/fuzz_targets/{chapter.rs → bluebook.rs} +4 -4
  1185. data/rust/parser/fuzz/fuzz_targets/resolve.rs +2 -2
  1186. data/rust/parser/src/build/named_pattern.rs +22 -0
  1187. data/rust/parser/src/build/references.rs +52 -0
  1188. data/rust/parser/src/diag.rs +22 -1
  1189. data/rust/parser/src/emit.rs +68 -4
  1190. data/rust/parser/src/expr/ast_json.rs +10 -0
  1191. data/rust/parser/src/expr/resolver.rs +49 -9
  1192. data/rust/parser/src/ir.rs +34 -7
  1193. data/rust/parser/src/keywords.rs +454 -457
  1194. data/rust/parser/src/lex.rs +115 -87
  1195. data/rust/parser/src/main.rs +180 -32
  1196. data/rust/parser/src/parse/adapter.rs +28 -0
  1197. data/rust/parser/src/parse/aggregate.rs +55 -35
  1198. data/rust/parser/src/parse/command.rs +100 -15
  1199. data/rust/parser/src/parse/{chapter.rs → declaration.rs} +85 -54
  1200. data/rust/parser/src/parse/domain_port.rs +60 -9
  1201. data/rust/parser/src/parse/entity.rs +35 -32
  1202. data/rust/parser/src/parse/file.rs +1 -1
  1203. data/rust/parser/src/parse/hecksagon.rs +15 -5
  1204. data/rust/parser/src/parse/mod.rs +84 -2
  1205. data/rust/parser/src/parse/process_manager.rs +1 -1
  1206. data/rust/parser/src/parse/query.rs +13 -3
  1207. data/rust/parser/src/parse/read_model.rs +2 -1
  1208. data/rust/parser/src/phrases.rs +1258 -0
  1209. data/rust/parser/src/render_ruby.rs +540 -0
  1210. data/rust/parser/src/sentence/changes.rs +437 -0
  1211. data/rust/parser/src/sentence/command_step.rs +206 -0
  1212. data/rust/parser/src/sentence/complete.rs +276 -0
  1213. data/rust/parser/src/sentence/entity.rs +41 -0
  1214. data/rust/parser/src/sentence/examples.rs +1012 -0
  1215. data/rust/parser/src/sentence/format.rs +882 -0
  1216. data/rust/parser/src/sentence/lower.rs +1366 -0
  1217. data/rust/parser/src/sentence/mod.rs +444 -0
  1218. data/rust/parser/src/sentence/names.rs +69 -0
  1219. data/rust/parser/src/sentence/options.rs +154 -0
  1220. data/rust/parser/src/sentence/pattern.rs +433 -0
  1221. data/rust/parser/src/sentence/process.rs +196 -0
  1222. data/rust/parser/src/sentence/queries.rs +431 -0
  1223. data/rust/parser/src/sentence/report.rs +283 -0
  1224. data/rust/parser/src/sentence/token.rs +106 -0
  1225. data/rust/parser/src/sentence/values.rs +498 -0
  1226. data/rust/src/exemplar/queries.rs +1 -0
  1227. data/rust/src/exemplar/reactions.rs +1 -1
  1228. data/rust/src/exemplar/registry.rs +30 -0
  1229. data/rust/src/kernel/cli.rs +233 -20
  1230. data/rust/src/kernel/collecting.rs +174 -0
  1231. data/rust/src/kernel/dispatch.rs +55 -27
  1232. data/rust/src/kernel/json.rs +15 -1
  1233. data/rust/src/kernel/mod.rs +8 -67
  1234. data/rust/src/kernel/named_query.rs +225 -11
  1235. data/rust/src/kernel/orchestrate.rs +213 -25
  1236. data/rust/src/kernel/orchestrate_guard_tests.rs +3 -0
  1237. data/rust/src/kernel/present.rs +261 -0
  1238. data/rust/src/kernel/query_cursor.rs +251 -0
  1239. data/rust/src/kernel/query_ordering.rs +6 -6
  1240. data/rust/src/kernel/read_model.rs +162 -17
  1241. data/rust/src/kernel/repository.rs +107 -17
  1242. data/rust/src/kernel/vocab/mod.rs +4 -0
  1243. data/rust/src/kernel/vocab/reducer.rs +109 -0
  1244. data/rust/src/kernel/vocab/refusal_class.rs +124 -0
  1245. data/rust/src/kernel/vocab/refusal_template.rs +2 -2
  1246. data/rust/src/lib.rs +1 -1
  1247. data/rust/web/Cargo.lock +47 -0
  1248. metadata +331 -93
  1249. data/lib/hecks/chapters.rb +0 -147
  1250. data/lib/hecks/gate/stages.yml +0 -234
  1251. data/lib/hecks/hecks/governance.world +0 -7
  1252. data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +0 -137
  1253. data/lib/hecks/ports/persistence/postgres_dump/connection.rb +0 -59
  1254. data/lib/hecks/ports/persistence/postgres_dump.rb +0 -100
  1255. data/lib/hecks/projections/deploy/box/templates/tunnel-egress.yaml.tmpl +0 -11
  1256. data/lib/hecks/projector/exporter/capability_values.rb +0 -55
  1257. data/lib/hecks/runtime/remote_dispatcher.rb +0 -176
  1258. data/prebuilt/02c09c73b272c1fb5a9ae6d87bb5e5e529208e00f6b4b597dc2c0fd70a9c0964.marshal +0 -0
  1259. data/prebuilt/bb84a835cf6a15136bad5ca03aa5bf93a5b92f5f72444618d3ef2412b987fb32.marshal +0 -0
  1260. data/prebuilt/verdicts-f1f10da14c8e8f01b4c9b7e3e2d74be8cd2bf4676364ed17fa16975d2a076d75.json +0 -1
  1261. data/rust/host/src/clock.rs +0 -8
  1262. data/rust/host/src/expr_json.rs +0 -1983
  1263. data/rust/host/src/presentation.rs +0 -842
  1264. data/rust/host/src/wasm_runner.rs +0 -206
  1265. data/rust/src/kernel/expression_operators/membership.rs +0 -20
  1266. data/rust/src/kernel/expression_operators/pattern_match.rs +0 -108
  1267. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/events.rb +0 -0
  1268. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/head_writes.rb +0 -0
  1269. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/journal.rb +0 -0
  1270. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/base_sql.rb +0 -0
  1271. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/field_cache.rb +0 -0
  1272. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/head_compiler.rb +0 -0
  1273. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/merge_writes.rb +0 -0
  1274. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/provisioning.rb +0 -0
  1275. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/resumable_backfill.rb +0 -0
  1276. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/transform_installer.rb +0 -0
  1277. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/transform_sql.rb +0 -0
  1278. /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/sagas.rb +0 -0
  1279. /data/rust/host/{src → core/src}/field_hints.rs +0 -0
  1280. /data/rust/host/{src → core/src}/log.rs +0 -0
  1281. /data/rust/host/{src → core/src}/reference_transform.rs +0 -0
@@ -0,0 +1,1265 @@
1
+ //! What any journal backend must do for the host to be correct, stated against a small harness
2
+ //! trait and run against every backend that implements it.
3
+ //!
4
+ //! The cases pin the commit clause of the semantics standard (C7.2, C8.4): a refused or failed
5
+ //! command leaves nothing behind, an accepted one lands in one atomic scope, and the domain lock
6
+ //! serializes writers while reads take none. Each case drives the real dispatch path and the real
7
+ //! compiled kernel, and asserts only what a caller or a second reader could observe.
8
+ //!
9
+ //! A backend joins by implementing [`Backend`] and listing the cases in `conformance_suite!`.
10
+ //! Everything a case needs from outside the dispatch path (reading the stores, planting a damaged
11
+ //! snapshot, injecting a failed write, holding the domain lock from a competing writer) is a
12
+ //! harness method, so the cases never name a table.
13
+
14
+ use crate::dispatch::{self, Caller, Outcome};
15
+ use crate::dispatch_tests::tests::{
16
+ provision_lineage, register, scratch_db, test_config, wasm_path, RecordingInvoker,
17
+ };
18
+ use crate::host::Host;
19
+ use crate::store::journal_sql::{self as journal, LineageConfig};
20
+ use crate::store::{Accepted, MemoryFault, MemoryStore, Store};
21
+ #[allow(unused_imports)]
22
+ use crate::store::{StoreExt, StoreFixtures};
23
+ use serde_json::{json, Value};
24
+ use std::time::Duration;
25
+ use tokio_postgres::{Client, NoTls};
26
+
27
+ const DOMAIN: &str = "Banking";
28
+ const REGISTER: &str = "Banking::Customer.Register";
29
+
30
+ #[derive(Debug, Clone, PartialEq)]
31
+ struct JournalRow {
32
+ ordinal: i64,
33
+ verb: String,
34
+ args: Value,
35
+ }
36
+
37
+ #[derive(Debug, Clone, PartialEq)]
38
+ struct SnapshotRow {
39
+ ordinal: i64,
40
+ seed: Value,
41
+ sagas_backfilled: bool,
42
+ }
43
+
44
+ #[derive(Debug, Clone, PartialEq)]
45
+ struct SagaRow {
46
+ process_manager: String,
47
+ correlation: String,
48
+ state: String,
49
+ }
50
+
51
+ /// Everything a backend holds for the domain, as a second reader would see it.
52
+ #[derive(Debug, Clone, PartialEq)]
53
+ struct Observed {
54
+ journal: Vec<JournalRow>,
55
+ snapshot: Option<SnapshotRow>,
56
+ sagas: Vec<SagaRow>,
57
+ /// Rows mirrored into the lineage journal; `None` on a backend without lineage.
58
+ lineage_rows: Option<i64>,
59
+ }
60
+
61
+ /// A write that a backend is made to refuse partway through a commit.
62
+ #[derive(Clone, Copy)]
63
+ enum Fault {
64
+ /// The snapshot write, which follows the journal append in the same scope.
65
+ SnapshotWrite,
66
+ /// The lineage mutation write, which follows the saga writes in the same scope.
67
+ LineageWrite,
68
+ }
69
+
70
+ /// One backend holding the Banking domain's journal, provisioned empty.
71
+ #[async_trait::async_trait]
72
+ trait Backend: Sized + Sync {
73
+ /// What a competing writer holds while it owns the domain lock.
74
+ type Held: Send;
75
+
76
+ /// An empty store named `name`, unique to the case.
77
+ async fn fresh(name: &str) -> Self;
78
+ /// A second handle on the same data with its own connection, as a second process would have.
79
+ async fn sibling(&self) -> Self;
80
+ async fn dispatch_as(
81
+ &self,
82
+ verb: &str,
83
+ args: Value,
84
+ caller: Caller<'_>,
85
+ ) -> anyhow::Result<Outcome>;
86
+ async fn read(&self) -> anyhow::Result<Value>;
87
+ async fn observe(&self) -> Observed;
88
+ /// The store itself, for cases that drive a unit of work without the kernel.
89
+ fn store(&self) -> &Store;
90
+ /// Appends a journal row the snapshot does not cover, leaving a gap after the snapshot.
91
+ async fn append_unsnapshotted(&self, verb: &str, args: &Value);
92
+ /// Rewrites the snapshot into a shape the kernel refuses to load.
93
+ async fn damage_snapshot(&self);
94
+ /// Resets the store to what a snapshot written before saga durability looks like.
95
+ async fn forget_saga_backfill(&self);
96
+ async fn inject(&self, fault: Fault);
97
+ async fn clear_faults(&self);
98
+ /// Holds the next era of the Banking domain under `label` and answers its ordinal.
99
+ async fn hold_era(&self, label: &str) -> i32;
100
+ async fn hold_domain_lock(&self) -> Self::Held;
101
+ async fn release_domain_lock(&self, held: Self::Held);
102
+ }
103
+
104
+ async fn dispatch<B: Backend>(backend: &B, verb: &str, args: Value) -> Outcome {
105
+ backend
106
+ .dispatch_as(verb, args, Caller::default())
107
+ .await
108
+ .unwrap_or_else(|e| panic!("{verb} should not fail: {e:#}"))
109
+ }
110
+
111
+ fn account_open(tag: &str, which: &str) -> Value {
112
+ json!({
113
+ "number": { "value": format!("{which}-{tag}") }, "kind": { "name": "current" },
114
+ "daily_limit": { "cents": 50000 }, "customer": format!("CUST-{tag}"),
115
+ })
116
+ }
117
+
118
+ /// A customer with a funded source account and an empty destination account.
119
+ async fn open_accounts<B: Backend>(backend: &B, tag: &str) {
120
+ dispatch(backend, REGISTER, register(&format!("CUST-{tag}"))).await;
121
+ dispatch(backend, "Banking::Account.Open", account_open(tag, "src")).await;
122
+ dispatch(backend, "Banking::Account.Open", account_open(tag, "dst")).await;
123
+ let credit = json!({
124
+ "number": format!("src-{tag}"), "amount": { "cents": 1000 },
125
+ "narrative": { "text": "opening balance" },
126
+ });
127
+ dispatch(backend, "Banking::Account.Credit", credit).await;
128
+ }
129
+
130
+ /// Freezing the destination makes the credit leg refuse, which leaves the transfer's saga
131
+ /// instance stored as `reversed`.
132
+ async fn freeze_destination<B: Backend>(backend: &B, tag: &str) {
133
+ let args = json!({ "number": { "value": format!("dst-{tag}") } });
134
+ dispatch(backend, "Banking::Account.FreezeAccount", args).await;
135
+ }
136
+
137
+ async fn request_transfer<B: Backend>(backend: &B, tag: &str) -> anyhow::Result<Outcome> {
138
+ let args = json!({
139
+ "reference": { "value": format!("tr-{tag}") }, "amount": { "cents": 200 },
140
+ "narrative": { "text": "conformance" },
141
+ "source": format!("src-{tag}"), "destination": format!("dst-{tag}"),
142
+ });
143
+ backend
144
+ .dispatch_as("Banking::Transfer.Request", args, Caller::default())
145
+ .await
146
+ }
147
+
148
+ /// The kernel's `instances` for `rows` replayed in full from an empty seed.
149
+ async fn replay_from_empty(rows: &[JournalRow]) -> Value {
150
+ let steps: Vec<Value> = rows
151
+ .iter()
152
+ .map(|r| json!({ "verb": r.verb, "args": r.args }))
153
+ .collect();
154
+ let input = json!({ "steps": steps }).to_string();
155
+ let output = tokio::task::spawn_blocking(move || crate::wasm_runner::run(&wasm_path(), &input))
156
+ .await
157
+ .expect("replay task")
158
+ .expect("kernel run");
159
+ let answer: Value = serde_json::from_str(&output).expect("kernel output is JSON");
160
+ assert!(
161
+ answer.get("error").is_none(),
162
+ "full replay failed: {answer}"
163
+ );
164
+ answer["instances"].clone()
165
+ }
166
+
167
+ fn settlement(tag: &str, state: &str) -> SagaRow {
168
+ SagaRow {
169
+ process_manager: "Settlement".to_string(),
170
+ correlation: format!("tr-{tag}"),
171
+ state: state.to_string(),
172
+ }
173
+ }
174
+
175
+ #[cfg(test)]
176
+ mod cases {
177
+ use super::*;
178
+ #[allow(unused_imports)]
179
+ use crate::store::StoreFixtures;
180
+
181
+ pub(super) async fn an_accepted_command_appends_one_journal_row<B: Backend>(db: &str) {
182
+ let backend = B::fresh(db).await;
183
+ for (done, reference) in ["CUST-0001", "CUST-0002", "CUST-0003"].iter().enumerate() {
184
+ let outcome = dispatch(&backend, REGISTER, register(reference)).await;
185
+ assert!(outcome.accepted, "{:?}", outcome.result);
186
+
187
+ let seen = backend.observe().await;
188
+ assert_eq!(
189
+ seen.journal.len(),
190
+ done + 1,
191
+ "exactly one row per accepted command"
192
+ );
193
+ let snapshot = seen
194
+ .snapshot
195
+ .clone()
196
+ .expect("an accepted command leaves a snapshot");
197
+ let newest = seen.journal.last().map(|row| row.ordinal);
198
+ assert_eq!(
199
+ Some(snapshot.ordinal),
200
+ newest,
201
+ "the snapshot is paired with the newest row"
202
+ );
203
+ let mirrored = i64::try_from(done + 1).unwrap();
204
+ assert!(
205
+ seen.lineage_rows.is_none_or(|rows| rows == mirrored),
206
+ "{seen:?}"
207
+ );
208
+ }
209
+ }
210
+
211
+ pub(super) async fn a_snapshot_and_a_gap_replay_to_the_state_of_a_full_replay<B: Backend>(
212
+ db: &str,
213
+ ) {
214
+ let backend = B::fresh(db).await;
215
+ open_accounts(&backend, "0004").await;
216
+
217
+ let seen = backend.observe().await;
218
+ let seed = seen.snapshot.as_ref().expect("snapshot").seed.clone();
219
+ assert_eq!(
220
+ seed,
221
+ replay_from_empty(&seen.journal).await,
222
+ "snapshot == empty seed + all steps"
223
+ );
224
+
225
+ backend
226
+ .append_unsnapshotted(REGISTER, &register("CUST-0099"))
227
+ .await;
228
+ let seen = backend.observe().await;
229
+ let full = replay_from_empty(&seen.journal).await;
230
+ let read = backend.read().await.unwrap();
231
+ assert_eq!(
232
+ read["instances"], full,
233
+ "snapshot + gap == empty seed + all steps"
234
+ );
235
+
236
+ dispatch(&backend, REGISTER, register("CUST-0098")).await;
237
+ let seen = backend.observe().await;
238
+ let seed = seen.snapshot.expect("snapshot").seed;
239
+ assert_eq!(
240
+ seed,
241
+ replay_from_empty(&seen.journal).await,
242
+ "the next snapshot absorbs the gap"
243
+ );
244
+ }
245
+
246
+ pub(super) async fn the_journal_keeps_arguments_as_recorded_and_never_restamps<B: Backend>(
247
+ db: &str,
248
+ ) {
249
+ let backend = B::fresh(db).await;
250
+ let caller = Caller {
251
+ role: Some("Branch clerk"),
252
+ actor_id: None,
253
+ };
254
+ let first = backend
255
+ .dispatch_as(REGISTER, register("CUST-0005"), caller)
256
+ .await
257
+ .unwrap();
258
+ assert!(first.accepted, "{:?}", first.result);
259
+
260
+ let recorded = backend.observe().await.journal;
261
+ assert_eq!(recorded[0].verb, REGISTER);
262
+ assert_eq!(
263
+ recorded[0].args,
264
+ register("CUST-0005"),
265
+ "the caller's stamps are not argument data"
266
+ );
267
+
268
+ dispatch(&backend, REGISTER, register("CUST-0006")).await;
269
+ let later = backend.observe().await.journal;
270
+ assert_eq!(
271
+ later[..1],
272
+ recorded[..],
273
+ "a later command leaves an earlier row byte-identical"
274
+ );
275
+ }
276
+
277
+ pub(super) async fn a_refused_command_changes_nothing<B: Backend>(db: &str) {
278
+ let backend = B::fresh(db).await;
279
+ open_accounts(&backend, "0007").await;
280
+ freeze_destination(&backend, "0007").await;
281
+ request_transfer(&backend, "0007").await.unwrap();
282
+ let before = backend.observe().await;
283
+
284
+ let duplicate = dispatch(&backend, REGISTER, register("CUST-0007")).await;
285
+
286
+ assert!(!duplicate.accepted, "{:?}", duplicate.result);
287
+ assert_eq!(
288
+ backend.observe().await,
289
+ before,
290
+ "journal, snapshot, sagas and lineage"
291
+ );
292
+ }
293
+
294
+ pub(super) async fn a_kernel_failure_is_an_error_and_changes_nothing<B: Backend>(db: &str) {
295
+ let backend = B::fresh(db).await;
296
+ open_accounts(&backend, "0008").await;
297
+ freeze_destination(&backend, "0008").await;
298
+ request_transfer(&backend, "0008").await.unwrap();
299
+ backend.damage_snapshot().await;
300
+ let before = backend.observe().await;
301
+
302
+ let failure = backend
303
+ .dispatch_as(REGISTER, register("CUST-0090"), Caller::default())
304
+ .await;
305
+
306
+ let message = format!("{:#}", failure.err().expect("a kernel failure is an error"));
307
+ assert!(message.contains("invalid seed"), "{message}");
308
+ assert_eq!(
309
+ backend.observe().await,
310
+ before,
311
+ "journal, snapshot, sagas and lineage"
312
+ );
313
+ }
314
+
315
+ pub(super) async fn a_snapshot_write_that_fails_leaves_the_append_invisible<B: Backend>(
316
+ db: &str,
317
+ ) {
318
+ let backend = B::fresh(db).await;
319
+ dispatch(&backend, REGISTER, register("CUST-0010")).await;
320
+ let before = backend.observe().await;
321
+
322
+ backend.inject(Fault::SnapshotWrite).await;
323
+ let failure = backend
324
+ .dispatch_as(REGISTER, register("CUST-0011"), Caller::default())
325
+ .await;
326
+
327
+ assert!(failure.is_err(), "the failed write fails the command");
328
+ assert_eq!(
329
+ backend.observe().await,
330
+ before,
331
+ "neither the journal row nor a snapshot is visible"
332
+ );
333
+
334
+ backend.clear_faults().await;
335
+ let retried = dispatch(&backend, REGISTER, register("CUST-0011")).await;
336
+ assert!(
337
+ retried.accepted,
338
+ "the store is usable again: {:?}",
339
+ retried.result
340
+ );
341
+ assert_eq!(backend.observe().await.journal.len(), 2);
342
+ }
343
+
344
+ pub(super) async fn a_saga_is_stored_with_the_command_that_produced_it<B: Backend>(db: &str) {
345
+ let backend = B::fresh(db).await;
346
+ open_accounts(&backend, "0012").await;
347
+ freeze_destination(&backend, "0012").await;
348
+ let before = backend.observe().await;
349
+ assert!(before.sagas.is_empty());
350
+
351
+ let outcome = request_transfer(&backend, "0012").await.unwrap();
352
+
353
+ assert!(outcome.accepted, "{:?}", outcome.result);
354
+ let seen = backend.observe().await;
355
+ assert_eq!(seen.sagas, vec![settlement("0012", "reversed")]);
356
+ assert_eq!(
357
+ seen.journal.len(),
358
+ before.journal.len() + 1,
359
+ "in the same commit as its row"
360
+ );
361
+ }
362
+
363
+ pub(super) async fn a_saga_write_rolls_back_with_a_command_that_fails<B: Backend>(db: &str) {
364
+ let backend = B::fresh(db).await;
365
+ open_accounts(&backend, "0013").await;
366
+ freeze_destination(&backend, "0013").await;
367
+ let before = backend.observe().await;
368
+
369
+ backend.inject(Fault::LineageWrite).await;
370
+ let failure = request_transfer(&backend, "0013").await;
371
+
372
+ assert!(
373
+ failure.is_err(),
374
+ "the lineage write is refused after the saga was written"
375
+ );
376
+ assert_eq!(
377
+ backend.observe().await,
378
+ before,
379
+ "the saga row did not commit alone"
380
+ );
381
+
382
+ backend.clear_faults().await;
383
+ assert!(request_transfer(&backend, "0013").await.unwrap().accepted);
384
+ assert_eq!(
385
+ backend.observe().await.sagas,
386
+ vec![settlement("0013", "reversed")]
387
+ );
388
+ }
389
+
390
+ pub(super) async fn a_missing_backfill_latch_forces_one_full_replay<B: Backend>(db: &str) {
391
+ let backend = B::fresh(db).await;
392
+ open_accounts(&backend, "0014").await;
393
+ freeze_destination(&backend, "0014").await;
394
+ request_transfer(&backend, "0014").await.unwrap();
395
+ backend.forget_saga_backfill().await;
396
+ let reset = backend.observe().await;
397
+ assert!(reset.sagas.is_empty());
398
+ assert!(reset.snapshot.is_some_and(|s| !s.sagas_backfilled));
399
+
400
+ dispatch(&backend, REGISTER, register("CUST-0015")).await;
401
+
402
+ let seen = backend.observe().await;
403
+ assert_eq!(
404
+ seen.sagas,
405
+ vec![settlement("0014", "reversed")],
406
+ "re-derived by the replay"
407
+ );
408
+ assert!(
409
+ seen.snapshot.is_some_and(|s| s.sagas_backfilled),
410
+ "and the latch is set"
411
+ );
412
+ let again = backend.observe().await;
413
+ dispatch(&backend, REGISTER, register("CUST-0016")).await;
414
+ assert_eq!(
415
+ backend.observe().await.sagas,
416
+ again.sagas,
417
+ "once, not on every command"
418
+ );
419
+ }
420
+
421
+ pub(super) async fn a_read_takes_no_lock_and_never_writes<B: Backend>(db: &str) {
422
+ let backend = B::fresh(db).await;
423
+ dispatch(&backend, REGISTER, register("CUST-0017")).await;
424
+ let before = backend.observe().await;
425
+
426
+ let held = backend.hold_domain_lock().await;
427
+ let read = tokio::time::timeout(Duration::from_secs(10), backend.read())
428
+ .await
429
+ .expect("a read must not wait for the domain lock")
430
+ .unwrap();
431
+ backend.release_domain_lock(held).await;
432
+
433
+ let keys: Vec<&String> = read["instances"].as_object().unwrap().keys().collect();
434
+ assert!(keys.iter().any(|k| k.contains("CUST-0017")), "{keys:?}");
435
+ assert_eq!(backend.observe().await, before);
436
+ }
437
+
438
+ pub(super) async fn a_read_replays_the_gap_after_the_snapshot_without_persisting_it<
439
+ B: Backend,
440
+ >(
441
+ db: &str,
442
+ ) {
443
+ let backend = B::fresh(db).await;
444
+ dispatch(&backend, REGISTER, register("CUST-0018")).await;
445
+ backend
446
+ .append_unsnapshotted(REGISTER, &register("CUST-0019"))
447
+ .await;
448
+ let before = backend.observe().await;
449
+
450
+ let read = backend.read().await.unwrap();
451
+
452
+ let keys: Vec<&String> = read["instances"].as_object().unwrap().keys().collect();
453
+ assert!(keys.iter().any(|k| k.contains("CUST-0018")), "{keys:?}");
454
+ assert!(
455
+ keys.iter().any(|k| k.contains("CUST-0019")),
456
+ "the gap row is read: {keys:?}"
457
+ );
458
+ assert_eq!(
459
+ backend.observe().await,
460
+ before,
461
+ "a read never closes the gap"
462
+ );
463
+ }
464
+
465
+ pub(super) async fn a_dispatch_waits_for_the_domain_lock_while_readers_proceed<B: Backend>(
466
+ db: &str,
467
+ ) {
468
+ let backend = B::fresh(db).await;
469
+ let other = backend.sibling().await;
470
+ dispatch(&backend, REGISTER, register("CUST-0020")).await;
471
+
472
+ let held = backend.hold_domain_lock().await;
473
+ let mut pending =
474
+ Box::pin(other.dispatch_as(REGISTER, register("CUST-0021"), Caller::default()));
475
+ let waited = tokio::time::timeout(Duration::from_millis(400), &mut pending).await;
476
+ assert!(
477
+ waited.is_err(),
478
+ "the writer must wait for the holder of the lock"
479
+ );
480
+ assert_eq!(
481
+ backend.observe().await.journal.len(),
482
+ 1,
483
+ "and has written nothing yet"
484
+ );
485
+ backend.read().await.unwrap();
486
+
487
+ backend.release_domain_lock(held).await;
488
+ let outcome = pending.await.unwrap();
489
+ assert!(outcome.accepted, "{:?}", outcome.result);
490
+ assert_eq!(backend.observe().await.journal.len(), 2);
491
+ }
492
+
493
+ pub(super) async fn concurrent_dispatches_on_one_domain_never_interleave<B: Backend>(db: &str) {
494
+ // Without the lock, two writers could both replay against zero prior steps, both see no
495
+ // conflict and both append, breaking the uniqueness the domain enforces: exactly one of
496
+ // two duplicate registrations may be accepted.
497
+ let first = B::fresh(db).await;
498
+ let second = first.sibling().await;
499
+ for round in 0..4 {
500
+ let reference = format!("CUST-RACE-{round}");
501
+ let (a, b) = tokio::join!(
502
+ first.dispatch_as(REGISTER, register(&reference), Caller::default()),
503
+ second.dispatch_as(REGISTER, register(&reference), Caller::default()),
504
+ );
505
+ let (a, b) = (a.unwrap(), b.unwrap());
506
+ assert_ne!(
507
+ a.accepted, b.accepted,
508
+ "round {round}: {:?} / {:?}",
509
+ a.result, b.result
510
+ );
511
+ assert_eq!(first.observe().await.journal.len(), round + 1);
512
+ }
513
+ }
514
+
515
+ pub(super) async fn a_dropped_unit_writes_nothing_and_frees_the_lock<B: Backend>(db: &str) {
516
+ let backend = B::fresh(db).await;
517
+ let other = backend.sibling().await;
518
+ let before = backend.observe().await;
519
+
520
+ let unit = backend.store().begin(DOMAIN).await.unwrap();
521
+ unit.load().await.unwrap();
522
+ drop(unit);
523
+
524
+ assert_eq!(
525
+ backend.observe().await,
526
+ before,
527
+ "a unit never committed wrote nothing"
528
+ );
529
+ let next = other.dispatch_as(REGISTER, register("CUST-0040"), Caller::default());
530
+ let outcome = tokio::time::timeout(Duration::from_secs(10), next)
531
+ .await
532
+ .expect("the dropped unit must have released the domain lock")
533
+ .unwrap();
534
+ assert!(outcome.accepted, "{:?}", outcome.result);
535
+ }
536
+
537
+ pub(super) async fn sagas_load_in_key_order_however_they_were_written<B: Backend>(db: &str) {
538
+ let backend = B::fresh(db).await;
539
+ let saga = |correlation: &str, state: &str| journal::SagaRow {
540
+ process_manager: "Settlement".to_string(),
541
+ correlation: correlation.to_string(),
542
+ state: state.to_string(),
543
+ memory: json!({}),
544
+ completed_compensations: json!([]),
545
+ };
546
+ let written = register("CUST-0043");
547
+ let out_of_order = Accepted {
548
+ verb: REGISTER,
549
+ args: &written,
550
+ new_seed: json!({}),
551
+ saga_upserts: vec![
552
+ saga("tr-b", "requested"),
553
+ saga("tr-c", "requested"),
554
+ saga("tr-a", "requested"),
555
+ ],
556
+ saga_deletes: Vec::new(),
557
+ lineage: None,
558
+ };
559
+ let unit = backend.store().begin(DOMAIN).await.unwrap();
560
+ unit.commit(out_of_order).await.unwrap();
561
+
562
+ // Rewriting a row moves it in a heap, which is how a scan comes to disagree with the keys.
563
+ let later = register("CUST-0044");
564
+ let rewrite = Accepted {
565
+ verb: REGISTER,
566
+ args: &later,
567
+ new_seed: json!({}),
568
+ saga_upserts: vec![saga("tr-a", "settled")],
569
+ saga_deletes: Vec::new(),
570
+ lineage: None,
571
+ };
572
+ let unit = backend.store().begin(DOMAIN).await.unwrap();
573
+ unit.commit(rewrite).await.unwrap();
574
+
575
+ let unit = backend.store().begin(DOMAIN).await.unwrap();
576
+ let loaded = unit.load().await.unwrap().sagas;
577
+ let keys: Vec<&str> = loaded.iter().map(|s| s.correlation.as_str()).collect();
578
+ assert_eq!(keys, ["tr-a", "tr-b", "tr-c"]);
579
+ }
580
+
581
+ fn heads_config(era: Option<i32>) -> LineageConfig {
582
+ LineageConfig {
583
+ domain: DOMAIN.to_string(),
584
+ era,
585
+ mirrored: None,
586
+ seal_every: None,
587
+ }
588
+ }
589
+
590
+ fn member(email: &str, role: &str) -> Value {
591
+ json!({ "email": { "value": email }, "role": { "value": role } })
592
+ }
593
+
594
+ async fn save_head(store: &Store, id: &str, state: &Value) {
595
+ let mutation = journal::Mutation {
596
+ aggregate: "Customer",
597
+ id,
598
+ operation: "save",
599
+ state,
600
+ };
601
+ store
602
+ .put_head(&heads_config(Some(1)), &mutation)
603
+ .await
604
+ .unwrap();
605
+ }
606
+
607
+ pub(super) async fn a_saved_head_is_read_back_by_id_and_in_full<B: Backend>(db: &str) {
608
+ let backend = B::fresh(db).await;
609
+ let store = backend.store();
610
+ assert_eq!(
611
+ store.head_by_id(DOMAIN, "customer", "a").await.unwrap(),
612
+ None
613
+ );
614
+
615
+ save_head(store, "a", &member("a@x.test", "viewer")).await;
616
+ save_head(store, "b", &member("b@x.test", "viewer")).await;
617
+ save_head(store, "a", &member("a@x.test", "admin")).await;
618
+
619
+ assert_eq!(
620
+ store.head_by_id(DOMAIN, "customer", "a").await.unwrap(),
621
+ Some(member("a@x.test", "admin")),
622
+ "a later save of one id replaces its head"
623
+ );
624
+ assert_eq!(
625
+ store.head_all(DOMAIN, "customer").await.unwrap(),
626
+ vec![
627
+ ("a".to_string(), member("a@x.test", "admin")),
628
+ ("b".to_string(), member("b@x.test", "viewer")),
629
+ ]
630
+ );
631
+ }
632
+
633
+ pub(super) async fn a_head_is_found_by_the_fields_it_holds<B: Backend>(db: &str) {
634
+ let backend = B::fresh(db).await;
635
+ let store = backend.store();
636
+ save_head(store, "a", &member("a@x.test", "viewer")).await;
637
+ save_head(store, "b", &member("b@x.test", "admin")).await;
638
+
639
+ let found = |matches: Vec<(&'static str, &'static str)>| async move {
640
+ store
641
+ .head_find(&[DOMAIN], "customer", &matches)
642
+ .await
643
+ .unwrap()
644
+ };
645
+ assert_eq!(
646
+ found(vec![("email", "b@x.test")]).await,
647
+ Some(member("b@x.test", "admin"))
648
+ );
649
+ assert_eq!(
650
+ found(vec![("email", "b@x.test"), ("role", "admin")]).await,
651
+ Some(member("b@x.test", "admin"))
652
+ );
653
+ assert_eq!(
654
+ found(vec![("email", "b@x.test"), ("role", "viewer")]).await,
655
+ None
656
+ );
657
+ assert_eq!(found(vec![("email", "nobody@x.test")]).await, None);
658
+ }
659
+
660
+ pub(super) async fn an_amended_head_is_read_and_written_as_one_step<B: Backend>(db: &str) {
661
+ let backend = B::fresh(db).await;
662
+ let store = backend.store();
663
+ let config = heads_config(Some(1));
664
+ save_head(store, "a", &member("a@x.test", "viewer")).await;
665
+
666
+ let seen = store
667
+ .amend_head(&config, DOMAIN, "Customer", "customer", |rows| {
668
+ (
669
+ rows.len(),
670
+ Some(("a".to_string(), member("a@x.test", "admin"))),
671
+ )
672
+ })
673
+ .await
674
+ .unwrap();
675
+ assert_eq!(seen, 1, "the decision saw the head as committed");
676
+ assert_eq!(
677
+ store.head_by_id(DOMAIN, "customer", "a").await.unwrap(),
678
+ Some(member("a@x.test", "admin"))
679
+ );
680
+
681
+ let declined = store
682
+ .amend_head(&config, DOMAIN, "Customer", "customer", |_| ("no", None))
683
+ .await
684
+ .unwrap();
685
+ assert_eq!(declined, "no");
686
+ assert_eq!(
687
+ store.head_all(DOMAIN, "customer").await.unwrap().len(),
688
+ 1,
689
+ "a decision that writes nothing leaves the heads alone"
690
+ );
691
+ }
692
+
693
+ pub(super) async fn the_first_domain_that_keeps_heads_answers_every_state<B: Backend>(
694
+ db: &str,
695
+ ) {
696
+ let backend = B::fresh(db).await;
697
+ let store = backend.store();
698
+ assert!(store
699
+ .head_states(&[DOMAIN, "Bluebook"], "customer")
700
+ .await
701
+ .unwrap()
702
+ .is_empty());
703
+
704
+ save_head(store, "b", &member("b@x.test", "viewer")).await;
705
+ save_head(store, "a", &member("a@x.test", "admin")).await;
706
+
707
+ assert_eq!(
708
+ store
709
+ .head_states(&["Elsewhere", DOMAIN], "customer")
710
+ .await
711
+ .unwrap_or_default(),
712
+ vec![member("a@x.test", "admin"), member("b@x.test", "viewer")],
713
+ "in id order, from the first domain that has heads"
714
+ );
715
+ }
716
+
717
+ pub(super) async fn held_eras_come_back_oldest_first_with_the_highest_as_current<B: Backend>(
718
+ db: &str,
719
+ ) {
720
+ let backend = B::fresh(db).await;
721
+ let store = backend.store();
722
+ let before = store.held_eras(DOMAIN).await.unwrap();
723
+
724
+ let first = backend.hold_era("shape-x").await;
725
+ let second = backend.hold_era("shape-y").await;
726
+
727
+ assert_eq!(second, first + 1, "each era takes the next ordinal");
728
+ let held = store.held_eras(DOMAIN).await.unwrap();
729
+ assert_eq!(held.len(), before.len() + 2);
730
+ let ordinals: Vec<i32> = held.iter().map(|era| era.ordinal).collect();
731
+ let mut sorted = ordinals.clone();
732
+ sorted.sort_unstable();
733
+ assert_eq!(ordinals, sorted, "oldest first");
734
+ assert_eq!(held.last().unwrap().label.as_deref(), Some("shape-y"));
735
+ assert_eq!(store.current_era(DOMAIN).await.unwrap(), Some(second));
736
+ }
737
+
738
+ pub(super) async fn a_domain_with_no_era_holds_none<B: Backend>(db: &str) {
739
+ let backend = B::fresh(db).await;
740
+ let store = backend.store();
741
+
742
+ assert!(store.held_eras("Nowhere").await.unwrap().is_empty());
743
+ assert_eq!(store.current_era("Nowhere").await.unwrap(), None);
744
+ }
745
+
746
+ pub(super) async fn a_head_write_without_an_era_is_refused_and_leaves_nothing<B: Backend>(
747
+ db: &str,
748
+ ) {
749
+ let backend = B::fresh(db).await;
750
+ let store = backend.store();
751
+ let state = member("a@x.test", "viewer");
752
+ let mutation = journal::Mutation {
753
+ aggregate: "Customer",
754
+ id: "a",
755
+ operation: "save",
756
+ state: &state,
757
+ };
758
+
759
+ assert!(store
760
+ .put_head(&heads_config(None), &mutation)
761
+ .await
762
+ .is_err());
763
+ let deleted = journal::Mutation {
764
+ operation: "delete",
765
+ ..mutation
766
+ };
767
+ assert!(store
768
+ .put_head(&heads_config(Some(1)), &deleted)
769
+ .await
770
+ .is_err());
771
+ assert!(store.head_all(DOMAIN, "customer").await.unwrap().is_empty());
772
+ }
773
+
774
+ pub(super) async fn a_unit_commits_a_saga_and_then_its_deletion<B: Backend>(db: &str) {
775
+ let backend = B::fresh(db).await;
776
+ let saga = |state: &str| journal::SagaRow {
777
+ process_manager: "Settlement".to_string(),
778
+ correlation: "tr-0041".to_string(),
779
+ state: state.to_string(),
780
+ memory: json!({}),
781
+ completed_compensations: json!([]),
782
+ };
783
+ let first = register("CUST-0041");
784
+ let accepted = Accepted {
785
+ verb: REGISTER,
786
+ args: &first,
787
+ new_seed: json!({}),
788
+ saga_upserts: vec![saga("requested")],
789
+ saga_deletes: Vec::new(),
790
+ lineage: None,
791
+ };
792
+ let unit = backend.store().begin(DOMAIN).await.unwrap();
793
+ unit.commit(accepted).await.unwrap();
794
+
795
+ let seen = backend.observe().await;
796
+ assert_eq!(seen.sagas, vec![settlement("0041", "requested")]);
797
+ let paired = seen.journal.last().map(|row| row.ordinal);
798
+ assert_eq!(seen.snapshot.map(|s| s.ordinal), paired);
799
+
800
+ let second = register("CUST-0042");
801
+ let ending = Accepted {
802
+ verb: REGISTER,
803
+ args: &second,
804
+ new_seed: json!({}),
805
+ saga_upserts: Vec::new(),
806
+ saga_deletes: vec![("Settlement".to_string(), "tr-0041".to_string())],
807
+ lineage: None,
808
+ };
809
+ let unit = backend.store().begin(DOMAIN).await.unwrap();
810
+ assert_eq!(
811
+ unit.load().await.unwrap().sagas.len(),
812
+ 1,
813
+ "the unit loads the live saga"
814
+ );
815
+ unit.commit(ending).await.unwrap();
816
+
817
+ let seen = backend.observe().await;
818
+ assert!(
819
+ seen.sagas.is_empty(),
820
+ "the ended saga is deleted with the command that ended it"
821
+ );
822
+ assert_eq!(seen.journal.len(), 2);
823
+ }
824
+ }
825
+
826
+ /// Instantiates every case for one backend as its own test, each against a database of its own.
827
+ macro_rules! conformance_suite {
828
+ ($backend:ty; $($case:ident),* $(,)?) => {
829
+ $(
830
+ #[tokio::test]
831
+ async fn $case() {
832
+ cases::$case::<$backend>(concat!("conf_", stringify!($case))).await;
833
+ }
834
+ )*
835
+ };
836
+ }
837
+
838
+ struct PostgresBackend {
839
+ db: String,
840
+ client: Store,
841
+ invoker: RecordingInvoker,
842
+ }
843
+
844
+ async fn connect(db: &str) -> Client {
845
+ let (client, connection) = tokio_postgres::connect(&crate::test_pg::conninfo(db), NoTls)
846
+ .await
847
+ .expect("connect to the scratch database");
848
+ tokio::spawn(async move {
849
+ let _ = connection.await;
850
+ });
851
+ client
852
+ }
853
+
854
+ impl PostgresBackend {
855
+ async fn rows(&self, sql: &str) -> Vec<tokio_postgres::Row> {
856
+ self.client.lock().await.query(sql, &[]).await.unwrap()
857
+ }
858
+
859
+ async fn run(&self, sql: &str) {
860
+ self.client.lock().await.batch_execute(sql).await.unwrap();
861
+ }
862
+
863
+ fn wrap(db: &str, client: Client) -> Self {
864
+ Self {
865
+ db: db.to_string(),
866
+ client: crate::store::postgres_store(client),
867
+ invoker: RecordingInvoker::new(),
868
+ }
869
+ }
870
+ }
871
+
872
+ /// The trigger body both injected faults share: any write to the table it guards is refused.
873
+ const REFUSE_WRITES: &str = "CREATE OR REPLACE FUNCTION conformance_refuse() RETURNS trigger AS \
874
+ $$ BEGIN RAISE EXCEPTION 'injected write failure'; END $$ LANGUAGE plpgsql";
875
+
876
+ #[async_trait::async_trait]
877
+ impl Backend for PostgresBackend {
878
+ type Held = Client;
879
+
880
+ async fn fresh(name: &str) -> Self {
881
+ // Each trait-level case name is the scratch database; `scratch_db` drops any leftover.
882
+ let client = scratch_db(name).await;
883
+ provision_lineage(
884
+ &*client.lock().await,
885
+ DOMAIN,
886
+ 1,
887
+ &["Customer", "Account", "Transfer"],
888
+ )
889
+ .await;
890
+ // The columns a minted `hecks_eras` carries beyond what the fixture creates.
891
+ client
892
+ .lock()
893
+ .await
894
+ .batch_execute(
895
+ "ALTER TABLE hecks_eras ADD COLUMN IF NOT EXISTS label text, \
896
+ ADD COLUMN IF NOT EXISTS watermark bigint, \
897
+ ADD COLUMN IF NOT EXISTS held_digest text",
898
+ )
899
+ .await
900
+ .unwrap();
901
+ // The head view a minted era leaves over each aggregate's era-1 snapshot table.
902
+ for storage in ["customer", "account", "transfer"] {
903
+ let snapshot = journal::qualified_name(DOMAIN, &format!("{storage}_head_snapshot_1"));
904
+ let view = journal::head_view(DOMAIN, storage);
905
+ client
906
+ .lock()
907
+ .await
908
+ .batch_execute(&format!(
909
+ "CREATE VIEW {} AS SELECT id, state FROM {}",
910
+ journal::quote_ident(&view),
911
+ journal::quote_ident(&snapshot)
912
+ ))
913
+ .await
914
+ .unwrap();
915
+ }
916
+ Self {
917
+ db: name.to_string(),
918
+ client,
919
+ invoker: RecordingInvoker::new(),
920
+ }
921
+ }
922
+
923
+ async fn sibling(&self) -> Self {
924
+ Self::wrap(&self.db, connect(&self.db).await)
925
+ }
926
+
927
+ async fn dispatch_as(
928
+ &self,
929
+ verb: &str,
930
+ args: Value,
931
+ caller: Caller<'_>,
932
+ ) -> anyhow::Result<Outcome> {
933
+ let (wasm, config) = (wasm_path(), test_config(DOMAIN, 1));
934
+ let host = Host::new(&self.client, &wasm, &config, &self.invoker);
935
+ dispatch::handle_as(host, verb, args, caller).await
936
+ }
937
+
938
+ async fn read(&self) -> anyhow::Result<Value> {
939
+ dispatch::read(&self.client, &wasm_path()).await
940
+ }
941
+
942
+ fn store(&self) -> &Store {
943
+ &self.client
944
+ }
945
+
946
+ async fn observe(&self) -> Observed {
947
+ let journal = self
948
+ .rows("SELECT ordinal, verb, args FROM hecks_lambda_journal ORDER BY ordinal")
949
+ .await;
950
+ let snapshot = self
951
+ .rows("SELECT ordinal, seed, sagas_backfilled FROM hecks_lambda_snapshot")
952
+ .await;
953
+ let sagas = self
954
+ .rows(
955
+ "SELECT process_manager, correlation, state FROM hecks_lambda_sagas ORDER BY 1, 2",
956
+ )
957
+ .await;
958
+ let lineage = self
959
+ .rows("SELECT count(*) FROM hecks_journal_banking")
960
+ .await;
961
+ Observed {
962
+ journal: journal
963
+ .iter()
964
+ .map(|r| JournalRow {
965
+ ordinal: r.get(0),
966
+ verb: r.get(1),
967
+ args: r.get(2),
968
+ })
969
+ .collect(),
970
+ snapshot: snapshot.first().map(|r| SnapshotRow {
971
+ ordinal: r.get(0),
972
+ seed: r.get(1),
973
+ sagas_backfilled: r.get(2),
974
+ }),
975
+ sagas: sagas
976
+ .iter()
977
+ .map(|r| SagaRow {
978
+ process_manager: r.get(0),
979
+ correlation: r.get(1),
980
+ state: r.get(2),
981
+ })
982
+ .collect(),
983
+ lineage_rows: Some(lineage[0].get(0)),
984
+ }
985
+ }
986
+
987
+ async fn append_unsnapshotted(&self, verb: &str, args: &Value) {
988
+ let client = self.client.lock().await;
989
+ let sql = "INSERT INTO hecks_lambda_journal (verb, args) VALUES ($1, $2)";
990
+ client.execute(sql, &[&verb, args]).await.unwrap();
991
+ }
992
+
993
+ async fn damage_snapshot(&self) {
994
+ let seed = self
995
+ .observe()
996
+ .await
997
+ .snapshot
998
+ .expect("a snapshot to damage")
999
+ .seed;
1000
+ let mut damaged = seed;
1001
+ let record = damaged
1002
+ .as_object_mut()
1003
+ .unwrap()
1004
+ .values_mut()
1005
+ .next()
1006
+ .unwrap();
1007
+ assert!(
1008
+ record.as_object_mut().unwrap().remove("status").is_some(),
1009
+ "{record}"
1010
+ );
1011
+ let client = self.client.lock().await;
1012
+ client
1013
+ .execute("UPDATE hecks_lambda_snapshot SET seed = $1", &[&damaged])
1014
+ .await
1015
+ .unwrap();
1016
+ }
1017
+
1018
+ async fn forget_saga_backfill(&self) {
1019
+ self.run("UPDATE hecks_lambda_snapshot SET sagas_backfilled = false")
1020
+ .await;
1021
+ self.run("DELETE FROM hecks_lambda_sagas").await;
1022
+ }
1023
+
1024
+ /// A trigger that raises on the guarded table makes that write fail inside the open
1025
+ /// transaction, after the writes that precede it, so the rollback is what the case observes.
1026
+ async fn inject(&self, fault: Fault) {
1027
+ let table = match fault {
1028
+ Fault::SnapshotWrite => "hecks_lambda_snapshot",
1029
+ Fault::LineageWrite => "hecks_journal_banking",
1030
+ };
1031
+ self.run(REFUSE_WRITES).await;
1032
+ self.run(&format!(
1033
+ "CREATE TRIGGER conformance_fault BEFORE INSERT OR UPDATE ON {table} \
1034
+ FOR EACH ROW EXECUTE FUNCTION conformance_refuse()"
1035
+ ))
1036
+ .await;
1037
+ }
1038
+
1039
+ async fn clear_faults(&self) {
1040
+ self.run("DROP TRIGGER IF EXISTS conformance_fault ON hecks_lambda_snapshot")
1041
+ .await;
1042
+ self.run("DROP TRIGGER IF EXISTS conformance_fault ON hecks_journal_banking")
1043
+ .await;
1044
+ }
1045
+
1046
+ /// A second connection takes the same advisory lock a dispatch takes and keeps its
1047
+ /// transaction open.
1048
+ async fn hold_era(&self, label: &str) -> i32 {
1049
+ let row = self
1050
+ .client
1051
+ .lock()
1052
+ .await
1053
+ .query_one(
1054
+ "INSERT INTO hecks_eras (domain, ordinal, held_text, label) \
1055
+ SELECT $1::text, COALESCE(MAX(ordinal), 0) + 1, 'test', $2::text \
1056
+ FROM hecks_eras WHERE domain = $1::text RETURNING ordinal",
1057
+ &[&DOMAIN, &label],
1058
+ )
1059
+ .await
1060
+ .unwrap();
1061
+ row.get(0)
1062
+ }
1063
+
1064
+ async fn hold_domain_lock(&self) -> Client {
1065
+ let competitor = connect(&self.db).await;
1066
+ competitor.batch_execute("BEGIN").await.unwrap();
1067
+ let lock = "SELECT pg_advisory_xact_lock(hashtext('hecks_lambda_journal.' || $1::text))";
1068
+ competitor.execute(lock, &[&DOMAIN]).await.unwrap();
1069
+ competitor
1070
+ }
1071
+
1072
+ async fn release_domain_lock(&self, held: Client) {
1073
+ held.batch_execute("ROLLBACK").await.unwrap();
1074
+ }
1075
+ }
1076
+
1077
+ mod postgres {
1078
+ use super::*;
1079
+ #[allow(unused_imports)]
1080
+ use crate::store::StoreFixtures;
1081
+
1082
+ conformance_suite!(PostgresBackend;
1083
+ sagas_load_in_key_order_however_they_were_written,
1084
+ an_accepted_command_appends_one_journal_row,
1085
+ a_snapshot_and_a_gap_replay_to_the_state_of_a_full_replay,
1086
+ the_journal_keeps_arguments_as_recorded_and_never_restamps,
1087
+ a_refused_command_changes_nothing,
1088
+ a_kernel_failure_is_an_error_and_changes_nothing,
1089
+ a_snapshot_write_that_fails_leaves_the_append_invisible,
1090
+ a_saga_is_stored_with_the_command_that_produced_it,
1091
+ a_saga_write_rolls_back_with_a_command_that_fails,
1092
+ a_missing_backfill_latch_forces_one_full_replay,
1093
+ a_read_takes_no_lock_and_never_writes,
1094
+ a_read_replays_the_gap_after_the_snapshot_without_persisting_it,
1095
+ a_dispatch_waits_for_the_domain_lock_while_readers_proceed,
1096
+ concurrent_dispatches_on_one_domain_never_interleave,
1097
+ a_dropped_unit_writes_nothing_and_frees_the_lock,
1098
+ a_saved_head_is_read_back_by_id_and_in_full,
1099
+ a_head_is_found_by_the_fields_it_holds,
1100
+ an_amended_head_is_read_and_written_as_one_step,
1101
+ the_first_domain_that_keeps_heads_answers_every_state,
1102
+ held_eras_come_back_oldest_first_with_the_highest_as_current,
1103
+ a_domain_with_no_era_holds_none,
1104
+ a_head_write_without_an_era_is_refused_and_leaves_nothing,
1105
+ a_unit_commits_a_saga_and_then_its_deletion,
1106
+ );
1107
+ }
1108
+
1109
+ struct MemoryBackend {
1110
+ memory: MemoryStore,
1111
+ store: Store,
1112
+ invoker: RecordingInvoker,
1113
+ }
1114
+
1115
+ impl MemoryBackend {
1116
+ fn over(memory: MemoryStore) -> Self {
1117
+ Self {
1118
+ store: Store::memory(memory.clone()),
1119
+ memory,
1120
+ invoker: RecordingInvoker::new(),
1121
+ }
1122
+ }
1123
+ }
1124
+
1125
+ #[async_trait::async_trait]
1126
+ impl Backend for MemoryBackend {
1127
+ type Held = tokio::sync::OwnedMutexGuard<()>;
1128
+
1129
+ async fn fresh(_name: &str) -> Self {
1130
+ Self::over(MemoryStore::new())
1131
+ }
1132
+
1133
+ async fn sibling(&self) -> Self {
1134
+ Self::over(self.memory.clone())
1135
+ }
1136
+
1137
+ async fn dispatch_as(
1138
+ &self,
1139
+ verb: &str,
1140
+ args: Value,
1141
+ caller: Caller<'_>,
1142
+ ) -> anyhow::Result<Outcome> {
1143
+ let config = LineageConfig {
1144
+ domain: DOMAIN.to_string(),
1145
+ era: None,
1146
+ mirrored: None,
1147
+ seal_every: None,
1148
+ };
1149
+ let wasm = wasm_path();
1150
+ let host = Host::new(&self.store, &wasm, &config, &self.invoker);
1151
+ dispatch::handle_as(host, verb, args, caller).await
1152
+ }
1153
+
1154
+ async fn read(&self) -> anyhow::Result<Value> {
1155
+ dispatch::read(&self.store, &wasm_path()).await
1156
+ }
1157
+
1158
+ fn store(&self) -> &Store {
1159
+ &self.store
1160
+ }
1161
+
1162
+ async fn observe(&self) -> Observed {
1163
+ let (journal, snapshot, sagas) = self.memory.committed();
1164
+ Observed {
1165
+ journal: journal
1166
+ .into_iter()
1167
+ .map(|(ordinal, verb, args)| JournalRow {
1168
+ ordinal,
1169
+ verb,
1170
+ args,
1171
+ })
1172
+ .collect(),
1173
+ snapshot: snapshot.map(|s| SnapshotRow {
1174
+ ordinal: s.ordinal,
1175
+ seed: s.seed,
1176
+ sagas_backfilled: s.sagas_backfilled,
1177
+ }),
1178
+ sagas: sagas
1179
+ .into_iter()
1180
+ .map(|s| SagaRow {
1181
+ process_manager: s.process_manager,
1182
+ correlation: s.correlation,
1183
+ state: s.state,
1184
+ })
1185
+ .collect(),
1186
+ lineage_rows: None,
1187
+ }
1188
+ }
1189
+
1190
+ async fn append_unsnapshotted(&self, verb: &str, args: &Value) {
1191
+ self.memory.append_unsnapshotted(verb, args);
1192
+ }
1193
+
1194
+ async fn damage_snapshot(&self) {
1195
+ let mut seed = self
1196
+ .observe()
1197
+ .await
1198
+ .snapshot
1199
+ .expect("a snapshot to damage")
1200
+ .seed;
1201
+ let record = seed.as_object_mut().unwrap().values_mut().next().unwrap();
1202
+ let removed = record.as_object_mut().unwrap().remove("status");
1203
+ assert!(removed.is_some(), "{record}");
1204
+ self.memory.replace_seed(seed);
1205
+ }
1206
+
1207
+ async fn forget_saga_backfill(&self) {
1208
+ self.memory.forget_saga_backfill();
1209
+ }
1210
+
1211
+ async fn inject(&self, fault: Fault) {
1212
+ self.memory.inject_fault(match fault {
1213
+ Fault::SnapshotWrite => MemoryFault::Snapshot,
1214
+ Fault::LineageWrite => MemoryFault::AfterSagas,
1215
+ });
1216
+ }
1217
+
1218
+ async fn clear_faults(&self) {
1219
+ self.memory.clear_fault();
1220
+ }
1221
+
1222
+ async fn hold_era(&self, label: &str) -> i32 {
1223
+ self.memory.hold_era(DOMAIN, label)
1224
+ }
1225
+
1226
+ async fn hold_domain_lock(&self) -> Self::Held {
1227
+ self.memory.hold_lock().await
1228
+ }
1229
+
1230
+ async fn release_domain_lock(&self, held: Self::Held) {
1231
+ drop(held);
1232
+ }
1233
+ }
1234
+
1235
+ mod memory {
1236
+ use super::*;
1237
+ #[allow(unused_imports)]
1238
+ use crate::store::StoreFixtures;
1239
+
1240
+ conformance_suite!(MemoryBackend;
1241
+ sagas_load_in_key_order_however_they_were_written,
1242
+ an_accepted_command_appends_one_journal_row,
1243
+ a_snapshot_and_a_gap_replay_to_the_state_of_a_full_replay,
1244
+ the_journal_keeps_arguments_as_recorded_and_never_restamps,
1245
+ a_refused_command_changes_nothing,
1246
+ a_kernel_failure_is_an_error_and_changes_nothing,
1247
+ a_snapshot_write_that_fails_leaves_the_append_invisible,
1248
+ a_saga_is_stored_with_the_command_that_produced_it,
1249
+ a_saga_write_rolls_back_with_a_command_that_fails,
1250
+ a_missing_backfill_latch_forces_one_full_replay,
1251
+ a_read_takes_no_lock_and_never_writes,
1252
+ a_read_replays_the_gap_after_the_snapshot_without_persisting_it,
1253
+ a_dispatch_waits_for_the_domain_lock_while_readers_proceed,
1254
+ concurrent_dispatches_on_one_domain_never_interleave,
1255
+ a_dropped_unit_writes_nothing_and_frees_the_lock,
1256
+ a_saved_head_is_read_back_by_id_and_in_full,
1257
+ a_head_is_found_by_the_fields_it_holds,
1258
+ an_amended_head_is_read_and_written_as_one_step,
1259
+ the_first_domain_that_keeps_heads_answers_every_state,
1260
+ held_eras_come_back_oldest_first_with_the_highest_as_current,
1261
+ a_domain_with_no_era_holds_none,
1262
+ a_head_write_without_an_era_is_refused_and_leaves_nothing,
1263
+ a_unit_commits_a_saga_and_then_its_deletion,
1264
+ );
1265
+ }