hecks 2.7.0 → 2.9.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 (463) hide show
  1. checksums.yaml +4 -4
  2. data/exe/hecks +11 -0
  3. data/lib/hecks/adapters/driven/claude_code.rb +4 -104
  4. data/lib/hecks/adapters/driven/d1.rb +44 -237
  5. data/lib/hecks/adapters/driven/folder.rb +17 -76
  6. data/lib/hecks/adapters/driven/google_authentication.rb +15 -49
  7. data/lib/hecks/adapters/driven/governance_authorization.rb +23 -70
  8. data/lib/hecks/adapters/driven/heki/journal.rb +8 -41
  9. data/lib/hecks/adapters/driven/heki/saga_store.rb +12 -54
  10. data/lib/hecks/adapters/driven/heki/snapshot.rb +4 -12
  11. data/lib/hecks/adapters/driven/heki.rb +13 -42
  12. data/lib/hecks/adapters/driven/identity_registry.rb +3 -14
  13. data/lib/hecks/adapters/driven/in_memory_ordering.rb +9 -26
  14. data/lib/hecks/adapters/driven/in_process_key_vault.rb +2 -14
  15. data/lib/hecks/adapters/driven/lambda/client.rb +10 -51
  16. data/lib/hecks/adapters/driven/lambda.rb +10 -84
  17. data/lib/hecks/adapters/driven/local_storage.rb +5 -40
  18. data/lib/hecks/adapters/driven/memory.rb +18 -99
  19. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +5 -21
  20. data/lib/hecks/adapters/driven/postgres/codec.rb +6 -28
  21. data/lib/hecks/adapters/driven/postgres/outbox.rb +3 -12
  22. data/lib/hecks/adapters/driven/postgres/reconnect.rb +4 -30
  23. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
  24. data/lib/hecks/adapters/driven/postgres.rb +145 -134
  25. data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
  26. data/lib/hecks/adapters/driven/prism.rb +7 -30
  27. data/lib/hecks/adapters/driven/sql_query_builder.rb +21 -121
  28. data/lib/hecks/adapters/driven/sqlite/codec.rb +8 -40
  29. data/lib/hecks/adapters/driven/sqlite/projection.rb +26 -82
  30. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +40 -84
  31. data/lib/hecks/adapters/driven/sqlite.rb +128 -65
  32. data/lib/hecks/adapters/driven/tenant_provisioner.rb +15 -37
  33. data/lib/hecks/adapters/driven.rb +5 -36
  34. data/lib/hecks/adapters/driving/github_webhook.rb +8 -76
  35. data/lib/hecks/behaviors/dsl.rb +7 -25
  36. data/lib/hecks/behaviors/expectations.rb +32 -142
  37. data/lib/hecks/behaviors/ir.rb +7 -20
  38. data/lib/hecks/behaviors/rspec.rb +6 -17
  39. data/lib/hecks/behaviors/runner.rb +10 -34
  40. data/lib/hecks/behaviors.rb +4 -8
  41. data/lib/hecks/bluebook/aggregate.rb +9 -75
  42. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +9 -38
  43. data/lib/hecks/bluebook/assembly/build.rb +4 -15
  44. data/lib/hecks/bluebook/assembly/contract.rb +15 -136
  45. data/lib/hecks/bluebook/assembly/contracts.rb +54 -174
  46. data/lib/hecks/bluebook/assembly/marks.rb +31 -223
  47. data/lib/hecks/bluebook/assembly/specializer.rb +6 -52
  48. data/lib/hecks/bluebook/assembly.rb +6 -45
  49. data/lib/hecks/bluebook/attribute.rb +12 -61
  50. data/lib/hecks/bluebook/behaviour/aggregate.rb +10 -39
  51. data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
  52. data/lib/hecks/bluebook/behaviour/chapter.rb +17 -42
  53. data/lib/hecks/bluebook/behaviour/command.rb +14 -66
  54. data/lib/hecks/bluebook/behaviour/domain_port.rb +4 -20
  55. data/lib/hecks/bluebook/behaviour/entity.rb +7 -27
  56. data/lib/hecks/bluebook/behaviour/hexagon.rb +23 -41
  57. data/lib/hecks/bluebook/behaviour/lifecycle.rb +15 -40
  58. data/lib/hecks/bluebook/behaviour/policy.rb +23 -52
  59. data/lib/hecks/bluebook/behaviour/process_manager.rb +30 -57
  60. data/lib/hecks/bluebook/behaviour/read_model.rb +38 -26
  61. data/lib/hecks/bluebook/behaviour/traits.rb +27 -64
  62. data/lib/hecks/bluebook/behaviour/value_object.rb +5 -24
  63. data/lib/hecks/bluebook/capabilities.rb +17 -49
  64. data/lib/hecks/bluebook/chapter.rb +10 -48
  65. data/lib/hecks/bluebook/command.rb +23 -81
  66. data/lib/hecks/bluebook/domain_port.rb +14 -63
  67. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +50 -229
  68. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +50 -377
  69. data/lib/hecks/bluebook/dsl/attribute_collector.rb +33 -204
  70. data/lib/hecks/bluebook/dsl/binding_proxy.rb +17 -65
  71. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +86 -708
  72. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +56 -170
  73. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +1 -0
  74. data/lib/hecks/bluebook/dsl/command_builder.rb +74 -546
  75. data/lib/hecks/bluebook/dsl/const_shim.rb +8 -41
  76. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +12 -87
  77. data/lib/hecks/bluebook/dsl/entity_builder.rb +44 -330
  78. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +20 -269
  79. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +26 -189
  80. data/lib/hecks/bluebook/dsl/identity_declaration.rb +13 -88
  81. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +6 -20
  82. data/lib/hecks/bluebook/dsl/policy_builder.rb +16 -88
  83. data/lib/hecks/bluebook/dsl/port_builder.rb +2 -6
  84. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +22 -96
  85. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +31 -184
  86. data/lib/hecks/bluebook/dsl/query_builder.rb +11 -42
  87. data/lib/hecks/bluebook/dsl/read_model_builder.rb +75 -201
  88. data/lib/hecks/bluebook/dsl/rule_reference.rb +22 -163
  89. data/lib/hecks/bluebook/dsl/translation_builder.rb +28 -114
  90. data/lib/hecks/bluebook/dsl/value_object_builder.rb +10 -124
  91. data/lib/hecks/bluebook/dsl/word_gate.rb +16 -162
  92. data/lib/hecks/bluebook/dsl/world_builder.rb +19 -106
  93. data/lib/hecks/bluebook/entity.rb +13 -71
  94. data/lib/hecks/bluebook/expression/ast_json.rb +24 -190
  95. data/lib/hecks/bluebook/expression/ast_reader.rb +12 -54
  96. data/lib/hecks/bluebook/expression/canonical_form.rb +11 -88
  97. data/lib/hecks/bluebook/expression/evaluator.rb +47 -295
  98. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +30 -147
  99. data/lib/hecks/bluebook/expression/resolver.rb +37 -660
  100. data/lib/hecks/bluebook/hexagon.rb +5 -28
  101. data/lib/hecks/bluebook/lifecycle.rb +5 -16
  102. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -7
  103. data/lib/hecks/bluebook/meta_validator/judge.rb +111 -361
  104. data/lib/hecks/bluebook/meta_validator/plan.rb +33 -162
  105. data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -7
  106. data/lib/hecks/bluebook/meta_validator/readings.rb +56 -342
  107. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -176
  108. data/lib/hecks/bluebook/meta_validator/shapes.rb +54 -268
  109. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -308
  110. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
  111. data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
  112. data/lib/hecks/bluebook/meta_validator.rb +79 -349
  113. data/lib/hecks/bluebook/model_check/client_profile.rb +10 -91
  114. data/lib/hecks/bluebook/model_check.rb +102 -600
  115. data/lib/hecks/bluebook/pattern_subset.rb +11 -69
  116. data/lib/hecks/bluebook/process_manager.rb +13 -83
  117. data/lib/hecks/bluebook/project_loader.rb +3 -39
  118. data/lib/hecks/bluebook/project_register.rb +12 -60
  119. data/lib/hecks/bluebook/query.rb +9 -48
  120. data/lib/hecks/bluebook/read_model.rb +57 -81
  121. data/lib/hecks/bluebook/reference.rb +7 -41
  122. data/lib/hecks/bluebook/smoke_test.rb +8 -98
  123. data/lib/hecks/bluebook/synthesizer.rb +13 -62
  124. data/lib/hecks/bluebook/translation.rb +26 -62
  125. data/lib/hecks/bluebook/value_object.rb +11 -36
  126. data/lib/hecks/bluebook.rb +5 -22
  127. data/lib/hecks/cache_dir.rb +84 -0
  128. data/lib/hecks/cli/document.rb +86 -0
  129. data/lib/hecks/cli/ir.rb +45 -0
  130. data/lib/hecks/cli/mcp.rb +31 -0
  131. data/lib/hecks/cli/mcp_door.rb +366 -0
  132. data/lib/hecks/cli/model_check.rb +203 -0
  133. data/lib/hecks/cli/project_cli.rb +90 -0
  134. data/lib/hecks/cli/project_diagrams.rb +36 -0
  135. data/lib/hecks/cli/run.rb +173 -0
  136. data/lib/hecks/cli/smoke_test.rb +45 -0
  137. data/lib/hecks/cli/stores.rb +53 -0
  138. data/lib/hecks/cli.rb +160 -0
  139. data/lib/hecks/construct.rb +8 -34
  140. data/lib/hecks/deploy/bluebook/deploy.bluebook +33 -142
  141. data/lib/hecks/deploy/bluebook/deploy.hecksagon +2 -9
  142. data/lib/hecks/embryonaut_bluebook/lock.rb +3 -35
  143. data/lib/hecks/embryonaut_bluebook/shape.rb +1 -17
  144. data/lib/hecks/embryonaut_bluebook/vendor.rb +8 -53
  145. data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +4 -19
  146. data/lib/hecks/embryonaut_bluebook.rb +4 -71
  147. data/lib/hecks/facade/cli_door.rb +24 -106
  148. data/lib/hecks/facade/cli_runner.rb +28 -172
  149. data/lib/hecks/facade/command_request.rb +13 -33
  150. data/lib/hecks/facade/handle.rb +34 -121
  151. data/lib/hecks/facade/json_door.rb +31 -146
  152. data/lib/hecks/facade/surface/aggregate_door.rb +39 -104
  153. data/lib/hecks/facade/surface/chapter.rb +10 -54
  154. data/lib/hecks/facade/surface.rb +8 -23
  155. data/lib/hecks/facade.rb +10 -25
  156. data/lib/hecks/forms/app.rb +15 -70
  157. data/lib/hecks/forms/command_form_renderer.rb +10 -29
  158. data/lib/hecks/forms/field_renderer.rb +17 -156
  159. data/lib/hecks/forms/field_shape.rb +30 -222
  160. data/lib/hecks/forms/html.rb +12 -47
  161. data/lib/hecks/forms/index_renderer.rb +1 -4
  162. data/lib/hecks/forms/page.rb +3 -9
  163. data/lib/hecks/forms/params.rb +14 -60
  164. data/lib/hecks/forms/port_argument.rb +2 -19
  165. data/lib/hecks/forms/query_form_renderer.rb +4 -79
  166. data/lib/hecks/forms/record_renderer.rb +8 -75
  167. data/lib/hecks/forms/record_table.rb +7 -50
  168. data/lib/hecks/forms/reference_options.rb +3 -30
  169. data/lib/hecks/forms/value_object_shape.rb +8 -35
  170. data/lib/hecks/forms.rb +5 -33
  171. data/lib/hecks/fqn.rb +5 -12
  172. data/lib/hecks/framework/bluebook/compliance.bluebook +16 -65
  173. data/lib/hecks/framework/bluebook/console_settings.bluebook +47 -134
  174. data/lib/hecks/framework/bluebook/framework.hecksagon +5 -14
  175. data/lib/hecks/framework/bluebook/governance.bluebook +13 -34
  176. data/lib/hecks/framework/bluebook/identity.bluebook +11 -26
  177. data/lib/hecks/framework/bluebook/privacy.bluebook +8 -33
  178. data/lib/hecks/framework.rb +11 -80
  179. data/lib/hecks/freezer.rb +5 -39
  180. data/lib/hecks/grammar/expression.bluebook +15 -40
  181. data/lib/hecks/grammar/translation.bluebook +3 -6
  182. data/lib/hecks/grammar.rb +16 -41
  183. data/lib/hecks/ir.rb +14 -83
  184. data/lib/hecks/language/adapter.bluebook +2 -7
  185. data/lib/hecks/language/bluebook/aggregate.bluebook +56 -221
  186. data/lib/hecks/language/bluebook/attaches/paging.bluebook +6 -15
  187. data/lib/hecks/language/bluebook/bluebook.bluebook +15 -83
  188. data/lib/hecks/language/bluebook/bluebook.hecksagon +3 -15
  189. data/lib/hecks/language/bluebook/command.bluebook +27 -144
  190. data/lib/hecks/language/bluebook/entity.bluebook +24 -97
  191. data/lib/hecks/language/bluebook/policy.bluebook +16 -67
  192. data/lib/hecks/language/bluebook/process_manager.bluebook +28 -151
  193. data/lib/hecks/language/bluebook/projection.bluebook +126 -60
  194. data/lib/hecks/language/bluebook/query.bluebook +12 -43
  195. data/lib/hecks/language/bluebook/shape.bluebook +23 -73
  196. data/lib/hecks/language/bluebook/syntax.bluebook +51 -262
  197. data/lib/hecks/language/bluebook/vocabulary.bluebook +98 -197
  198. data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
  199. data/lib/hecks/language/hecksagon/domain_port.bluebook +4 -19
  200. data/lib/hecks/language/hecksagon/hecksagon.bluebook +6 -25
  201. data/lib/hecks/language/hecksagon/port_operation.bluebook +3 -14
  202. data/lib/hecks/language/oidc.json +35 -0
  203. data/lib/hecks/language/port.bluebook +4 -17
  204. data/lib/hecks/language/translation/translation.bluebook +3 -9
  205. data/lib/hecks/language/translation/translation_aggregate.bluebook +5 -24
  206. data/lib/hecks/language/world/wiring.bluebook +1 -3
  207. data/lib/hecks/language/world/world.bluebook +5 -7
  208. data/lib/hecks/literal.rb +8 -50
  209. data/lib/hecks/mcp_door_scope.rb +95 -0
  210. data/lib/hecks/mcp_stdio_guard.rb +4 -41
  211. data/lib/hecks/naming.rb +29 -137
  212. data/lib/hecks/ports/access_control.rb +7 -23
  213. data/lib/hecks/ports/agent/answers.rb +5 -23
  214. data/lib/hecks/ports/agent.rb +47 -172
  215. data/lib/hecks/ports/authentication.rb +12 -39
  216. data/lib/hecks/ports/authorization.rb +16 -43
  217. data/lib/hecks/ports/clock.rb +5 -45
  218. data/lib/hecks/ports/extraction.rb +7 -12
  219. data/lib/hecks/ports/identity_assignment.rb +8 -29
  220. data/lib/hecks/ports/identity_generation.rb +6 -29
  221. data/lib/hecks/ports/identity_resolution.rb +2 -10
  222. data/lib/hecks/ports/key_vault.rb +9 -45
  223. data/lib/hecks/ports/persistence/append_only.rb +62 -65
  224. data/lib/hecks/ports/persistence/binding_policy.rb +4 -14
  225. data/lib/hecks/ports/persistence/codec_boundary.rb +20 -80
  226. data/lib/hecks/ports/persistence/null_saga_store.rb +4 -20
  227. data/lib/hecks/ports/persistence/plugin.rb +13 -42
  228. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +38 -141
  229. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
  230. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +16 -84
  231. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +7 -26
  232. data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +3 -22
  233. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +55 -181
  234. data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +77 -0
  235. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +12 -122
  236. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +28 -128
  237. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +73 -474
  238. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +31 -124
  239. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +45 -219
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +13 -108
  241. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +18 -75
  242. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +9 -49
  243. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +6 -123
  244. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +23 -55
  245. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +14 -72
  246. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +7 -16
  247. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +14 -115
  248. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
  249. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +127 -554
  250. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
  251. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +2 -17
  252. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +3 -14
  253. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +13 -58
  254. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -17
  255. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -26
  256. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +9 -25
  257. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +22 -122
  258. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +34 -123
  259. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +5 -12
  260. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +3 -9
  261. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -14
  262. data/lib/hecks/ports/persistence/plugins/era.rb +4 -23
  263. data/lib/hecks/ports/persistence/postgres_dump/connection.rb +7 -23
  264. data/lib/hecks/ports/persistence/postgres_dump.rb +4 -35
  265. data/lib/hecks/ports/persistence/remote_runtime.rb +2 -24
  266. data/lib/hecks/ports/persistence/repository_factory.rb +8 -27
  267. data/lib/hecks/ports/persistence/state_codec.rb +23 -103
  268. data/lib/hecks/ports/persistence.rb +7 -14
  269. data/lib/hecks/ports/projection.rb +42 -84
  270. data/lib/hecks/ports/query/in_memory.rb +10 -58
  271. data/lib/hecks/ports/query/ordering.rb +11 -32
  272. data/lib/hecks/ports/query.rb +14 -37
  273. data/lib/hecks/ports.rb +2 -8
  274. data/lib/hecks/projections/bootstrap_table.rb +11 -43
  275. data/lib/hecks/projections/deploy/fargate/assembly.rb +2 -15
  276. data/lib/hecks/projections/deploy/fargate/cdn.rb +1 -48
  277. data/lib/hecks/projections/deploy/fargate/cdn_default.rb +2 -11
  278. data/lib/hecks/projections/deploy/fargate/check.rb +5 -14
  279. data/lib/hecks/projections/deploy/fargate/containers.rb +3 -78
  280. data/lib/hecks/projections/deploy/fargate/extras.rb +3 -44
  281. data/lib/hecks/projections/deploy/fargate/monitoring.rb +0 -44
  282. data/lib/hecks/projections/deploy/fargate/settings.rb +2 -54
  283. data/lib/hecks/projections/deploy/fargate/yaml.rb +13 -58
  284. data/lib/hecks/projections/deploy/fargate.rb +35 -202
  285. data/lib/hecks/projections/deploy/lambda.rb +151 -679
  286. data/lib/hecks/projections/deploy/preview/access_yaml.rb +1 -33
  287. data/lib/hecks/projections/deploy/preview/containers.rb +6 -111
  288. data/lib/hecks/projections/deploy/preview/routing_yaml.rb +4 -56
  289. data/lib/hecks/projections/deploy/preview/script.rb +2 -14
  290. data/lib/hecks/projections/deploy/preview/settings.rb +3 -33
  291. data/lib/hecks/projections/deploy/preview/task_yaml.rb +2 -38
  292. data/lib/hecks/projections/deploy/preview/template.rb +2 -58
  293. data/lib/hecks/projections/deploy/preview.rb +2 -67
  294. data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
  295. data/lib/hecks/projections/deploy/scripts.rb +4 -57
  296. data/lib/hecks/projections/deploy/shared.rb +47 -146
  297. data/lib/hecks/projections/deploy/smoke.rb +4 -61
  298. data/lib/hecks/projections/deploy/template_diff/comparer.rb +3 -25
  299. data/lib/hecks/projections/deploy/template_diff/loader.rb +2 -11
  300. data/lib/hecks/projections/deploy/template_diff.rb +5 -57
  301. data/lib/hecks/projections/diagrams.rb +20 -582
  302. data/lib/hecks/projections/glossary/html.rb +14 -130
  303. data/lib/hecks/projections/glossary/markdown.rb +8 -69
  304. data/lib/hecks/projections/glossary/mermaid.rb +9 -66
  305. data/lib/hecks/projections/glossary/sensitivity.rb +5 -34
  306. data/lib/hecks/projections/glossary/sentences.rb +14 -159
  307. data/lib/hecks/projections/glossary.rb +30 -222
  308. data/lib/hecks/projections/ir.rb +3 -10
  309. data/lib/hecks/projections/model/deviations.rb +15 -42
  310. data/lib/hecks/projections/model.rb +12 -51
  311. data/lib/hecks/projections/oidc.rb +12 -56
  312. data/lib/hecks/projections/parser_table.rb +9 -28
  313. data/lib/hecks/projections/reference.rb +9 -16
  314. data/lib/hecks/projections/rust_vocabulary.rb +12 -250
  315. data/lib/hecks/projections/shape.rb +3 -29
  316. data/lib/hecks/projections/statements.rb +7 -86
  317. data/lib/hecks/projections/vocabulary.rb +6 -22
  318. data/lib/hecks/projections.rb +2 -16
  319. data/lib/hecks/projector/cli_projector.rb +60 -375
  320. data/lib/hecks/projector/docs_projector.rb +10 -201
  321. data/lib/hecks/projector/exporter.rb +46 -138
  322. data/lib/hecks/projector/ir_projector.rb +2 -9
  323. data/lib/hecks/projector/narrate_projector.rb +15 -189
  324. data/lib/hecks/projector/target.rb +13 -76
  325. data/lib/hecks/projector.rb +30 -121
  326. data/lib/hecks/query_specification/common/comparators.rb +5 -26
  327. data/lib/hecks/query_specification/common/comparison.rb +35 -143
  328. data/lib/hecks/query_specification/common/dsl.rb +8 -24
  329. data/lib/hecks/query_specification/common/null_policy.rb +20 -70
  330. data/lib/hecks/query_specification/common/options.rb +2 -6
  331. data/lib/hecks/query_specification/common/where_clause.rb +2 -10
  332. data/lib/hecks/query_specification/field_path.rb +37 -77
  333. data/lib/hecks/query_specification/hop_path.rb +12 -76
  334. data/lib/hecks/release/gem_pin.rb +2 -26
  335. data/lib/hecks/release/runner/ci_publisher.rb +93 -0
  336. data/lib/hecks/release/runner/commands.rb +66 -0
  337. data/lib/hecks/release/runner/console.rb +52 -0
  338. data/lib/hecks/release/runner/gem_publisher.rb +43 -0
  339. data/lib/hecks/release/runner/git.rb +48 -0
  340. data/lib/hecks/release/runner/npm_publisher.rb +75 -0
  341. data/lib/hecks/release/runner/preflight.rb +104 -0
  342. data/lib/hecks/release/runner/published.rb +55 -0
  343. data/lib/hecks/release/runner/tagger.rb +80 -0
  344. data/lib/hecks/release/runner.rb +215 -0
  345. data/lib/hecks/rendering.rb +4 -26
  346. data/lib/hecks/router/namespace_installer.rb +12 -36
  347. data/lib/hecks/router.rb +4 -8
  348. data/lib/hecks/runtime/aggregate_lock.rb +8 -33
  349. data/lib/hecks/runtime/boot_gates.rb +3 -18
  350. data/lib/hecks/runtime/caller.rb +19 -59
  351. data/lib/hecks/runtime/capability_graph.rb +5 -21
  352. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
  353. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +23 -160
  354. data/lib/hecks/runtime/command_interpreter.rb +125 -307
  355. data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
  356. data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
  357. data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
  358. data/lib/hecks/runtime/command_rules/emission.rb +8 -21
  359. data/lib/hecks/runtime/command_rules/references.rb +23 -202
  360. data/lib/hecks/runtime/dependency_planning.rb +30 -102
  361. data/lib/hecks/runtime/dispatcher.rb +82 -241
  362. data/lib/hecks/runtime/entity_element.rb +40 -462
  363. data/lib/hecks/runtime/entity_interpreter.rb +65 -220
  364. data/lib/hecks/runtime/errors.rb +32 -115
  365. data/lib/hecks/runtime/event.rb +3 -25
  366. data/lib/hecks/runtime/identity.rb +18 -154
  367. data/lib/hecks/runtime/instance.rb +18 -150
  368. data/lib/hecks/runtime/interpreting.rb +11 -70
  369. data/lib/hecks/runtime/invocation.rb +28 -164
  370. data/lib/hecks/runtime/loader.rb +48 -186
  371. data/lib/hecks/runtime/outbox.rb +38 -261
  372. data/lib/hecks/runtime/policy_interpreter.rb +38 -200
  373. data/lib/hecks/runtime/port_operation_interpreter.rb +26 -87
  374. data/lib/hecks/runtime/query_interpreter.rb +45 -187
  375. data/lib/hecks/runtime/reaction_invocation.rb +36 -138
  376. data/lib/hecks/runtime/read_model_interpreter.rb +139 -208
  377. data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
  378. data/lib/hecks/runtime/reference_hop.rb +8 -81
  379. data/lib/hecks/runtime/refusal_wording.rb +8 -89
  380. data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
  381. data/lib/hecks/runtime/registry/verification.rb +52 -225
  382. data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
  383. data/lib/hecks/runtime/registry.rb +103 -244
  384. data/lib/hecks/runtime/remote_dispatcher.rb +37 -152
  385. data/lib/hecks/runtime/routing.rb +4 -15
  386. data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
  387. data/lib/hecks/runtime/saga_interpreter.rb +79 -263
  388. data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
  389. data/lib/hecks/runtime/tenant_check.rb +6 -68
  390. data/lib/hecks/runtime/tenant_scope.rb +3 -28
  391. data/lib/hecks/runtime/value/admission.rb +16 -67
  392. data/lib/hecks/runtime/value/coercion.rb +155 -641
  393. data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
  394. data/lib/hecks/runtime/value.rb +12 -84
  395. data/lib/hecks/runtime.rb +17 -65
  396. data/lib/hecks/storehouse.rb +146 -567
  397. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +8 -32
  398. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
  399. data/lib/hecks/vendoring/git_environment.rb +2 -8
  400. data/lib/hecks/vendoring/git_source.rb +4 -10
  401. data/lib/hecks/vendoring.rb +7 -46
  402. data/lib/hecks/version.rb +3 -14
  403. data/lib/hecks/vocabulary.rb +5 -0
  404. data/lib/hecks.rb +32 -82
  405. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
  406. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
  407. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
  408. metadata +30 -59
  409. data/lib/hecks/bench/cli.rb +0 -74
  410. data/lib/hecks/bench/environment.rb +0 -100
  411. data/lib/hecks/bench/postgres_probe.rb +0 -41
  412. data/lib/hecks/bench/report.rb +0 -116
  413. data/lib/hecks/bench/ruby_runner.rb +0 -151
  414. data/lib/hecks/bench/run.rb +0 -55
  415. data/lib/hecks/bench/rust_runner.rb +0 -165
  416. data/lib/hecks/bench/stats.rb +0 -75
  417. data/lib/hecks/bench/suite.rb +0 -172
  418. data/lib/hecks/bench/workload.rb +0 -137
  419. data/lib/hecks/bench.rb +0 -28
  420. data/lib/hecks/codemod.rb +0 -443
  421. data/lib/hecks/corpus.rb +0 -479
  422. data/lib/hecks/doc/reference.rb +0 -585
  423. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -684
  424. data/lib/hecks/fuzzing/combination_miner.rb +0 -178
  425. data/lib/hecks/fuzzing/concurrent_dispatch.rb +0 -469
  426. data/lib/hecks/fuzzing/coverage_campaign.rb +0 -161
  427. data/lib/hecks/fuzzing/differential.rb +0 -192
  428. data/lib/hecks/fuzzing/domain_generator.rb +0 -871
  429. data/lib/hecks/fuzzing/era_boundary.rb +0 -154
  430. data/lib/hecks/fuzzing/form_census.rb +0 -285
  431. data/lib/hecks/fuzzing/generated_domain_check.rb +0 -171
  432. data/lib/hecks/fuzzing/invalid_value_generator.rb +0 -125
  433. data/lib/hecks/fuzzing/isolated_boot.rb +0 -658
  434. data/lib/hecks/fuzzing/nondeterministic.rb +0 -79
  435. data/lib/hecks/fuzzing/persistence_parity.rb +0 -253
  436. data/lib/hecks/fuzzing/properties/corrections.rb +0 -125
  437. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +0 -688
  438. data/lib/hecks/fuzzing/properties/guards.rb +0 -398
  439. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +0 -399
  440. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +0 -137
  441. data/lib/hecks/fuzzing/properties/outbox.rb +0 -180
  442. data/lib/hecks/fuzzing/properties/querying.rb +0 -260
  443. data/lib/hecks/fuzzing/properties.rb +0 -267
  444. data/lib/hecks/fuzzing/qa_settings.rb +0 -164
  445. data/lib/hecks/fuzzing/replay.rb +0 -930
  446. data/lib/hecks/fuzzing/rotation_priority.rb +0 -114
  447. data/lib/hecks/fuzzing/rust_gap_manifest.rb +0 -139
  448. data/lib/hecks/fuzzing/self_consistency.rb +0 -825
  449. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +0 -532
  450. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +0 -171
  451. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +0 -127
  452. data/lib/hecks/fuzzing/sequence_generator/picker.rb +0 -116
  453. data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +0 -138
  454. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +0 -344
  455. data/lib/hecks/fuzzing/sequence_generator.rb +0 -311
  456. data/lib/hecks/fuzzing/shrinker.rb +0 -309
  457. data/lib/hecks/fuzzing/structural_skips.rb +0 -53
  458. data/lib/hecks/fuzzing/sweep_depth.rb +0 -61
  459. data/lib/hecks/fuzzing/target_capabilities.rb +0 -255
  460. data/lib/hecks/fuzzing/value_generator.rb +0 -340
  461. data/lib/hecks/fuzzing.rb +0 -23
  462. data/lib/hecks/grammar/evolve.rb +0 -524
  463. data/lib/hecks/query_ir.rb +0 -482
@@ -14,36 +14,20 @@ require_relative "../naming"
14
14
 
15
15
  module Hecks
16
16
  module Runtime
17
- # The runtime's single dispatch entry point for one booted registry:
18
- # routes a "Domain::Aggregate.Command"-shaped verb to the right
19
- # sub-interpreter (command, entity, port operation, query, read
20
- # model), then runs every policy and saga reaction the resulting
21
- # events trigger. Also the door reactions dispatch back through
22
- # (#reenter), and the one place reaction-cascade depth is tracked, to
23
- # keep a policy/saga loop from re-triggering itself without bound.
17
+ # Routes a "Domain::Aggregate.Command" verb to its interpreter, then runs the policy
18
+ # and saga reactions its events trigger; tracks reaction depth to bound cascades.
24
19
  class Dispatcher
25
20
  MAX_REACTION_DEPTH = 5
26
21
 
27
22
  Result = Struct.new(:verb, :instance, :events, :execution_plan, :persistence_outcome, keyword_init: true) do
28
23
  # Reads the identity of the record the dispatch settled on.
29
24
  #
30
- # `instance` is nil for a port operation dispatched by verb (below)
31
- # — nothing was hydrated or saved, the same reason
32
- # `PortOperationInterpreter#emit`'s own comment gives for sourcing
33
- # `id:` off the operation's reference attribute instead. `&.`, not
34
- # a raised error: a caller that dispatches a port verb and then
35
- # asks this Result for `.id`/`.state` made a category error the
36
- # domain itself already told it about (there is no record here),
37
- # not a crash-worthy one.
38
- #
39
- # @return [String, nil] the record's identity; nil for a port operation, which
40
- # hydrates no record
25
+ # @return [String, nil] the record's identity; nil for a port operation (no record)
41
26
  def id = instance&.id
42
27
 
43
- # Reads the settled record's attributes as one Hash, for the same nil-safe reason as `id`.
28
+ # Reads the settled record's attributes as one Hash.
44
29
  #
45
- # @return [Hash{Symbol => Object}, nil] the record's state with `:id` merged in
46
- # last; nil for a port operation, which hydrates no record
30
+ # @return [Hash{Symbol => Object}, nil] the record's state; nil for a port operation
47
31
  def state = instance&.to_h
48
32
 
49
33
  def to_s
@@ -56,8 +40,7 @@ module Hecks
56
40
 
57
41
  attr_reader :registry
58
42
 
59
- # @param registry [Runtime::Registry] the booted registry every interpreter reads; its
60
- # outbox is attached to this dispatcher's policy and saga interpreters
43
+ # @param registry [Runtime::Registry] the booted registry every interpreter reads
61
44
  def initialize(registry)
62
45
  @registry = registry
63
46
  rules = CommandRules.new(registry)
@@ -68,108 +51,72 @@ module Hecks
68
51
  @read_models = ReadModelInterpreter.new(registry)
69
52
  @policies = PolicyInterpreter.new(registry, door: self)
70
53
  @sagas = SagaInterpreter.new(registry, door: self)
71
- # The relay is the registry's, not this dispatcher's — the
72
- # interpreters enqueue through `@registry.outbox` from inside
73
- # the save transaction, and this dispatcher drains through the
74
- # same object, so there is exactly one relay per registry no
75
- # matter how many dispatchers front it. What it borrows from
76
- # here is the pair of interpreters a consumer runs through.
54
+ # One relay per registry: interpreters enqueue through it inside the save transaction.
77
55
  @registry.outbox.attach(policies: @policies, sagas: @sagas)
78
56
  end
79
57
 
80
- # Exposes the registry's outbox relay, the object this dispatcher drains reactions through.
81
- #
82
- # `runtime.outbox.rows`, `.rows(status: "claimed")`, `.redrive!`,
83
- # `.log` — see `Runtime::Outbox`.
58
+ # Exposes the registry's outbox relay, through which this dispatcher drains reactions.
84
59
  #
85
- # @return [Runtime::Outbox::Relay] the registry's one relay, shared by every dispatcher
86
- # fronting that registry
60
+ # @return [Runtime::Outbox::Relay] the registry's one relay
87
61
  def outbox = @registry.outbox
88
62
 
89
- # Exposes every event emitted through this registry since boot or the last reset.
63
+ # Exposes every event emitted through this registry, oldest first.
90
64
  #
91
- # @return [Array<Runtime::Event>] the registry's live event log, oldest first
65
+ # @return [Array<Runtime::Event>]
92
66
  def events = @registry.event_log
93
67
 
94
- # Exposes one record per policy reaction that was delivered, refused or left undelivered.
68
+ # Exposes one record per policy reaction delivered, refused or left undelivered.
95
69
  #
96
- # @return [Array<Hash{Symbol => Object}>] the registry's live reaction log, oldest first
70
+ # @return [Array<Hash{Symbol => Object}>]
97
71
  def reactions = @registry.reaction_log
98
72
 
99
73
  # Exposes one record per process-manager step: a start, an advance, a delivery, a refusal.
100
74
  #
101
- # @return [Array<Hash{Symbol => Object}>] the registry's live saga log, oldest first
75
+ # @return [Array<Hash{Symbol => Object}>]
102
76
  def sagas = @registry.saga_log
103
77
 
104
- # Exposes the raw inputs each saga dispatch bound its arguments from, a Ruby-only log.
78
+ # Exposes the raw inputs each saga dispatch bound its arguments from (Ruby-only).
105
79
  #
106
- # @return [Array<Hash{Symbol => Object}>] the registry's live saga dispatch log, oldest first
80
+ # @return [Array<Hash{Symbol => Object}>]
107
81
  def saga_dispatches = @registry.saga_dispatch_log
108
82
 
109
- # Exposes the raw inputs each policy trigger bound its arguments from, a Ruby-only log.
83
+ # Exposes the raw inputs each policy trigger bound its arguments from (Ruby-only).
110
84
  #
111
- # @return [Array<Hash{Symbol => Object}>] the registry's live policy dispatch log, with
112
- # keys `:policy`, `:on`, `:payload`, `:with_spec` and `:args`
85
+ # @return [Array<Hash{Symbol => Object}>] entries keyed `:policy`, `:on`, `:payload`,
86
+ # `:with_spec` and `:args`
113
87
  def policy_dispatches = @registry.policy_dispatch_log
114
88
 
115
89
  # Lists every verb the loaded bluebooks declare.
116
90
  #
117
- # @return [Array<String>] the verbs of every loaded bluebook, sorted
91
+ # @return [Array<String>] sorted
118
92
  def verbs = @registry.verbs
119
93
 
120
- # Runs one command, entity command or port operation, then every policy and saga reaction
121
- # its events are owed.
122
- #
123
- # The receiver in `to:`, the facts in `with:`, and nothing else.
124
- # Loose keyword facts — `dispatch(verb, amount: 5)`, one bag holding
125
- # both the route and the payload — were deprecated in 1.3.x and are
126
- # gone: Ruby now refuses them itself, by name ("unknown keyword:
127
- # :amount"). Code holding a bag of data rather than written keywords
128
- # calls `dispatch_flat` below; that door is not going anywhere.
94
+ # Runs one command, entity command or port operation, then the reactions its events owe.
129
95
  #
130
- # @param verb [String] the fully qualified verb: `"Domain::Aggregate.Command"`,
131
- # `"Domain::Aggregate.Entity.Command"` or `"Domain::Aggregate.Port.Operation"`
132
- # @param to [String, Hash, nil] the receiver: an aggregate identity, or an entity route
133
- # Hash with `:aggregate` and one of `:entity`/`:entities`; nil when the facts carry
134
- # the identity themselves
135
- # @param with [Hash, nil] the command's facts, keyed by argument name (String or Symbol)
136
- # @param saga_correlation [Hash, nil] correlation head => value, stamped on every
137
- # emitted event when a saga leg causes this dispatch; nil otherwise
138
- # @return [Runtime::Dispatcher::Result] the verb, settled instance (nil for a port
139
- # operation), emitted events, execution plan and persistence outcome
140
- # @raise [Runtime::UnknownVerb] if the verb is not fully qualified, or names a domain,
141
- # aggregate, command, entity or port operation that is not declared
142
- # @raise [StandardError] any class in `Runtime::DOMAIN_REFUSALS` when the domain refuses
143
- # the call (`GivenNotMet`, `TypeMismatch`, `Unauthorized`, `NotFound`, …)
144
- # @raise [Runtime::StaleWrite] if concurrent writers beat this one through every retry
145
- # @raise [Runtime::WiringError] if the aggregate's repository cannot be resolved
96
+ # @param verb [String] `"Domain::Aggregate.Command"`, `"Domain::Aggregate.Entity.Command"`
97
+ # or `"Domain::Aggregate.Port.Operation"`
98
+ # @param to [String, Hash, nil] the receiver: an aggregate identity, or an entity route Hash
99
+ # with `:aggregate` and `:entity`/`:entities`
100
+ # @param with [Hash, nil] the command's facts, keyed by argument name
101
+ # @param saga_correlation [Hash, nil] stamped on emitted events when a saga leg dispatches
102
+ # @return [Runtime::Dispatcher::Result] the instance is nil for a port operation
103
+ # @raise [Runtime::UnknownVerb] if the verb is malformed or names something undeclared
104
+ # @raise [StandardError] a `Runtime::DOMAIN_REFUSALS` class when the domain refuses
105
+ # @raise [Runtime::StaleWrite] if concurrent writers win every retry
146
106
  def dispatch(verb, to: nil, with: nil, saga_correlation: nil)
147
107
  dispatch_invocation(verb, to: to, with: with, saga_correlation: saga_correlation, flat: {})
148
108
  end
149
109
 
150
110
  # Dispatches a verb whose receiver and facts arrive together in one flat Hash.
151
111
  #
152
- # **The flat-facts wire form** — one Hash, not keywords: the shape
153
- # `spec/corpus/*.json` steps, the Rust kernel's `cli.rs` contract, a
154
- # reaction without a `with:` projection, and the self-hosted
155
- # meta-domain all carry, where the receiver's identity is one of the
156
- # keys because that is how the wire spells it. Routes exactly as the
157
- # removed keyword door did: a Symbol `:to`, `:with` or
158
- # `:saga_correlation` key is lifted out as that keyword, everything
159
- # else is a fact (so a String "to" key stays a fact, as it did).
160
- # Framework code that replays data calls this; application code
161
- # calls `dispatch(verb, to:, with:)`.
112
+ # The wire form: Symbol keys `:to`, `:with` and `:saga_correlation` are lifted out as
113
+ # the keywords of `dispatch`; every other key, including a String "to", is a fact.
162
114
  #
163
115
  # @param verb [String] the fully qualified verb, in any shape `dispatch` accepts
164
- # @param args [Hash] the facts, plus optional Symbol keys `:to`, `:with` and
165
- # `:saga_correlation`, read as `dispatch`'s keywords of the same names; not mutated
116
+ # @param args [Hash] the facts plus the optional Symbol keys above; not mutated
166
117
  # @return [Runtime::Dispatcher::Result] the same result `dispatch` returns
167
- # @raise [Runtime::UnknownVerb] if the verb is not fully qualified, or names a domain,
168
- # aggregate, command, entity or port operation that is not declared
169
- # @raise [StandardError] any class in `Runtime::DOMAIN_REFUSALS` when the domain refuses
170
- # the call
171
- # @raise [Runtime::StaleWrite] if concurrent writers beat this one through every retry
172
- # @raise [Runtime::WiringError] if the aggregate's repository cannot be resolved
118
+ # @raise [Runtime::UnknownVerb] if the verb is malformed or names something undeclared
119
+ # @raise [StandardError] a `Runtime::DOMAIN_REFUSALS` class when the domain refuses
173
120
  def dispatch_flat(verb, args = {})
174
121
  facts = args.dup
175
122
  to = facts.delete(:to)
@@ -186,17 +133,8 @@ module Hecks
186
133
  if command_name.include?(".")
187
134
  head, sub = command_name.split(".", 2)
188
135
  port = aggregate.port(head)
189
- # A port operation, reached by the same verb shape an entity
190
- # command already uses ("Domain::Aggregate.Head.Rest") — ports
191
- # are checked first, so an aggregate that ever declared both a
192
- # port and an entity of the same name would resolve to the
193
- # port; no domain in this corpus does, and `dispatch_port`'s
194
- # own header already named this as an open wire-spelling
195
- # question this resolves, not silently avoids. No `instance`
196
- # comes back — nothing is hydrated or saved by a port
197
- # operation (`PortOperationInterpreter`'s own header) — so
198
- # `Result#id`/`#state` are nil-safe (above) for exactly this
199
- # path.
136
+ # Ports are checked before entities, so a port wins a name an entity also declares.
137
+ # No instance comes back: a port operation hydrates and saves nothing.
200
138
  if port
201
139
  operation = port.operation(sub) ||
202
140
  raise(UnknownVerb, RefusalWording.render_site("UnknownVerb", "port_no_operation",
@@ -218,37 +156,19 @@ module Hecks
218
156
  @commands.call(domain, aggregate, command, invocation, saga_correlation)
219
157
  end
220
158
 
221
- # Correlation is set when each event is constructed, not merged on
222
- # here — it is part of the transaction, known from this method's
223
- # own argument before a single event exists. Stamping it onto
224
- # already-emitted events would keep an event mutable after it had
225
- # happened.
226
- #
227
- # The ordering that depends on it holds for the same reason:
228
- # `SagaInterpreter#advance` runs on this domain's `announced`
229
- # events within this very call, and finds the correlation already
230
- # there because it was never absent.
159
+ # Correlation is stamped as each event is built, so emitted events are never mutated.
231
160
 
232
161
  react(announced, domain, aggregate, outbox_rows)
233
162
 
234
163
  Result.new(verb: verb, instance: instance, events: announced,
235
164
  execution_plan: execution_plan, persistence_outcome: persistence_outcome)
236
165
  end
237
-
238
- # **The one body both doors run** — `dispatch` (keywords) and
239
- # `dispatch_flat` (one Hash) differ only in how the call's parts are
240
- # spelled, never in what happens next.
241
166
  private :dispatch_invocation
242
167
 
243
- # **Everything owed because `announced` committed** — policies first,
244
- # then sagas, the order this method always ran them in. The
245
- # command/entity interpreters hand back the outbox rows they
246
- # enqueued inside the save transaction (`Interpreting#
247
- # run_dispatch_order`); a port operation saves nothing, so its
248
- # rows are enqueued here, after the fact (`:enqueue`) — durable
249
- # still, just not transactional with anything, because there is
250
- # nothing for them to be transactional with. `nil` rows mean the
251
- # repository has no outbox: react directly, exactly as before.
168
+ # Runs the policy and saga reactions owed because `announced` committed.
169
+ #
170
+ # A port operation saves nothing, so its outbox rows are enqueued here (`:enqueue`);
171
+ # nil rows mean the repository has no outbox and reactions run directly.
252
172
  def react(announced, domain, aggregate, outbox_rows)
253
173
  return if announced.empty?
254
174
 
@@ -260,38 +180,16 @@ module Hecks
260
180
 
261
181
  # Answers whether a command would succeed right now, without saving, emitting or reacting.
262
182
  #
263
- # "If this were dispatched right now, would it succeed" — the same
264
- # pipeline #dispatch itself runs (arguments coerced, givens checked,
265
- # mutations applied in memory, ensures checked against the settled
266
- # result), except `step_save`/`step_emit` never run, and neither do
267
- # policies or sagas afterward: nothing here is committed, so nothing
268
- # should react to it. Built for exactly the shape a whole-board
269
- # postcondition needs to be tested against (a downstream project's
270
- # own chess domain, checking "does this move leave my own king in
271
- # check" — the alternative was dispatching a real, unrelated piece's
272
- # own move purely to trigger the check, which then had to avoid
273
- # interfering with the very position being tested).
274
- #
275
- # Raises the same refusals #dispatch does — a DomainRefusal
276
- # subclass propagates normally, so a caller checking "would this be
277
- # legal" writes the identical rescue clause a real dispatch already
278
- # needs; this returns `true` only when nothing was refused.
279
- #
280
- # **Never a port verb** — `PortOperationInterpreter`'s own side effects
281
- # (an external gateway call, say) have no meaningful in-memory-only
282
- # form, so this refuses one outright rather than silently running
283
- # it for real, which "dry" would otherwise quietly lie about.
183
+ # Runs the dispatch pipeline in memory only; refusals raise exactly as `dispatch` raises
184
+ # them. A port verb is refused: its side effects have no in-memory form.
284
185
  #
285
186
  # @param verb [String] the fully qualified aggregate or entity command verb
286
- # @param args [Hash{Symbol => Object}] the command's facts as flat keywords; a key named
287
- # `to` or `with` is an ordinary fact here, never routing (BUG#131)
288
- # @return [true] whenever nothing refused; a refusal is raised, never returned as false
289
- # @raise [Runtime::WiringError] if the verb names a port operation, or the aggregate's
290
- # repository cannot be resolved
291
- # @raise [Runtime::UnknownVerb] if the verb is not fully qualified, or names a domain,
292
- # aggregate, command or entity that is not declared
293
- # @raise [StandardError] any class in `Runtime::DOMAIN_REFUSALS` that the real dispatch
294
- # would raise
187
+ # @param args [Hash{Symbol => Object}] the facts as flat keywords; `to` and `with` are
188
+ # ordinary facts here, never routing
189
+ # @return [true] whenever nothing refused
190
+ # @raise [Runtime::WiringError] if the verb names a port operation
191
+ # @raise [Runtime::UnknownVerb] if the verb is malformed or names something undeclared
192
+ # @raise [StandardError] any `Runtime::DOMAIN_REFUSALS` class the real dispatch would raise
295
193
  def dry_run?(verb, **args)
296
194
  domain, aggregate_name, command_name = parse(verb)
297
195
  aggregate = resolve_aggregate(domain, aggregate_name, verb)
@@ -304,8 +202,7 @@ module Hecks
304
202
  "only for aggregate and entity commands"
305
203
  end
306
204
 
307
- # `to:`/`with:` are not keywords of this method — a key named
308
- # either is an ordinary fact here (BUG#131), so both go in as nil.
205
+ # `to:`/`with:` are not keywords here; a key named either is an ordinary fact.
309
206
  resolution = nil
310
207
  invocation = Invocation.from_call(verb, to: nil, with: nil, flat: args, receiver: :entity) do
311
208
  (resolution = EntityInterpreter::Resolution.of(aggregate, command_name)).command
@@ -320,36 +217,18 @@ module Hecks
320
217
  true
321
218
  end
322
219
 
323
- # Runs one port operation named by its parts, then the reactions its events are owed.
324
- #
325
- # The door an adapter outside the bluebook calls through — never the
326
- # domain itself. `port_name`/`operation_name` are separate arguments
327
- # rather than one packed verb string on purpose: there is no established
328
- # wire spelling for "domain, aggregate, port, operation" yet, and
329
- # inventing one is a bigger decision than this call needs to make.
220
+ # Runs one port operation named by its parts, then the reactions its events owe.
330
221
  #
331
- # No adapter-to-port binding lookup happens here — that is
332
- # `Hecks.adapter`'s existing job (unchanged by this), and wiring "which
333
- # adapter may call this port" through is the next piece, not this one.
222
+ # The door for an adapter outside the bluebook; the parts are separate arguments because
223
+ # no wire spelling for a packed port verb exists.
334
224
  #
335
- # @param domain [String, Symbol] name of the domain the aggregate belongs to
336
- # @param aggregate_name [String, Symbol] name of the aggregate that declares the port
337
- # @param port_name [String] name of the port, as the aggregate declares it
338
- # @param operation_name [String] name of the operation on that port
339
- # @param to [String, Hash, nil] the receiving aggregate's identity; when nil it is read
340
- # from the facts, by the operation's reference or identity attribute
225
+ # @param to [String, Hash, nil] the receiver; when nil it is read from the facts
341
226
  # @param with [Hash, nil] the operation's facts, keyed by argument name
342
- # @param flat [Hash] the wire form `dispatch_flat` takes, for the driving adapter
343
- # holding a decoded webhook rather than written keywords; it is where the operation's
344
- # own reference attribute is read from and lifted into `to:` when `to:` is nil
345
- # @return [Array<Runtime::Event>] the events the operation announced: one per declared
346
- # `emits` for an inbound operation, the one answering or refusing event for an
347
- # outbound one
348
- # @raise [Runtime::UnknownVerb] if the domain, aggregate, port or operation is not declared
349
- # @raise [Runtime::TypeMismatch] if no receiving identity can be found, or `to:`/`with:`
350
- # is malformed
351
- # @raise [Runtime::NotFound] if the receiving aggregate record does not exist
352
- # @raise [Runtime::WiringError] if the aggregate's repository cannot be resolved
227
+ # @param flat [Hash] the `dispatch_flat` wire form, for an adapter holding a decoded webhook
228
+ # @return [Array<Runtime::Event>] the events the operation announced
229
+ # @raise [Runtime::UnknownVerb] if the domain, aggregate, port or operation is undeclared
230
+ # @raise [Runtime::TypeMismatch] if no receiving identity is found, or `to:`/`with:` is bad
231
+ # @raise [Runtime::NotFound] if the receiving record does not exist
353
232
  def dispatch_port(domain, aggregate_name, port_name, operation_name, to: nil, with: nil, flat: {})
354
233
  aggregate = resolve_aggregate(domain, aggregate_name, "#{domain}::#{aggregate_name}.#{port_name}.#{operation_name}")
355
234
  port = aggregate.port(port_name) ||
@@ -369,22 +248,16 @@ module Hecks
369
248
 
370
249
  # Answers a declared query: an aggregate query, an entity query, or a read model.
371
250
  #
372
- # The verb's shape picks the interpreter. `"Domain.ReadModel"` — no `::` before the
373
- # dot — is a read model; `"Domain::Aggregate.Query"` is an aggregate query, and
374
- # `"Domain::Aggregate.Entity.Query"` an entity query.
251
+ # `"Domain.ReadModel"` (no `::`) is a read model; `"Domain::Aggregate.Query"` and
252
+ # `"Domain::Aggregate.Entity.Query"` are aggregate and entity queries.
375
253
  #
376
254
  # @param verb [String, Symbol] the query's verb, in one of the three shapes above
377
255
  # @param args [Hash{Symbol => Object}] the query's declared arguments
378
- # @return [Array<Hash>] for an aggregate query, one deep-frozen Hash per matching record,
379
- # its state with `:id` merged in last; for an entity query, one Hash per matching
380
- # element with the parent's reference key merged in first; for a read model, a
256
+ # @return [Array<Hash>] one Hash per matching record or element; a read model returns a
381
257
  # one-element Array holding a Hash of head name to projected rows
382
- # @raise [Runtime::UnknownVerb] if the verb is not fully qualified, or names a domain,
383
- # aggregate, entity, query or read model that is not declared
258
+ # @raise [Runtime::UnknownVerb] if the verb is malformed or names something undeclared
384
259
  # @raise [Runtime::NotFound] if a read model's root reference names no record
385
260
  # @raise [Runtime::TypeMismatch] if an argument cannot be coerced to its declared type
386
- # @raise [KeyError] if a rooted read model is asked without its reference argument
387
- # @raise [Runtime::WiringError] if the aggregate's repository cannot be resolved
388
261
  def query(verb, **args)
389
262
  domain, query_name = verb.to_s.split(".", 2)
390
263
  if query_name && !domain.include?("::")
@@ -404,20 +277,14 @@ module Hecks
404
277
 
405
278
  # Answers an aggregate or entity query through the reference interpreter alone.
406
279
  #
407
- # The same ask as `#query`, never answered by the bound adapter's
408
- # native hook. Read models have no reference twin, so only the
409
- # `"Domain::Aggregate.Query"` forms answer here; the fuzzer's
410
- # query oracle diffs this against `#query`'s answer.
280
+ # Never answered by the bound adapter's native hook; the fuzzer's query oracle diffs it
281
+ # against `#query`. Read models have no reference twin.
411
282
  #
412
- # @param verb [String] the fully qualified query verb, `"Domain::Aggregate.Query"` or
413
- # `"Domain::Aggregate.Entity.Query"`
283
+ # @param verb [String] `"Domain::Aggregate.Query"` or `"Domain::Aggregate.Entity.Query"`
414
284
  # @param args [Hash{Symbol => Object}] the query's declared arguments
415
- # @return [Array<Hash>] one Hash per matching record, its state with `:id` merged in
416
- # last; for an entity query, one Hash per matching element
417
- # @raise [Runtime::UnknownVerb] if the verb is not fully qualified, or names a domain,
418
- # aggregate, entity or query that is not declared
285
+ # @return [Array<Hash>] one Hash per matching record or element
286
+ # @raise [Runtime::UnknownVerb] if the verb is malformed or names something undeclared
419
287
  # @raise [Runtime::TypeMismatch] if an argument cannot be coerced to its declared type
420
- # @raise [Runtime::WiringError] if the aggregate's repository cannot be resolved
421
288
  def reference_query(verb, **args)
422
289
  domain, aggregate_name, query_name = parse(verb)
423
290
  aggregate = resolve_aggregate(domain, aggregate_name, verb)
@@ -425,43 +292,18 @@ module Hecks
425
292
  @queries.reference_call(domain, aggregate, query_name, args)
426
293
  end
427
294
 
428
- # Dispatches a reaction's command one level deeper in the cascade, as the system rather
429
- # than as the triggering caller.
430
- #
431
- # A reaction is the system acting, not the caller who happened to be
432
- # on the stack when the triggering command ran — the ambient caller
433
- # is cleared for the reaction's own dispatch, so a triggering
434
- # caller's role can neither satisfy nor block a reaction command it
435
- # has nothing to do with (Runtime::Caller.without).
436
- #
437
- # `Thread.current[:hecks_reaction_depth]`, not a plain ivar — this
438
- # `Dispatcher` instance is a single object shared by every thread
439
- # dispatching through it (a Puma worker pool, say), so a plain ivar
440
- # here is exactly the known Puma-concurrency bug class: two
441
- # concurrent top-level dispatches on different threads would
442
- # increment/decrement the same counter, letting one thread's nested
443
- # reaction depth leak into another thread's unrelated dispatch. A
444
- # `Mutex` is not the answer either — a reaction cascade re-enters
445
- # `reenter` on the same thread (see `SagaInterpreter#advance_saga`'s
446
- # own comment on why a non-reentrant `Mutex` can't guard this).
447
- # `Thread.current`-backed, saved/restored around the call with a
448
- # plain local + `ensure`, is the same idiom `Runtime::Caller`
449
- # (`caller.rb`) already established for exactly this shape of
450
- # per-thread ambient state.
295
+ # Dispatches a reaction's command one level deeper in the cascade, as the system.
451
296
  #
452
- # The depth is not checked here: a reacting interpreter asks
453
- # `reaction_depth_reached?` first and records an undelivered reaction
454
- # instead of calling this.
297
+ # The ambient caller is cleared (Runtime::Caller.without) so the triggering caller's role
298
+ # neither satisfies nor blocks the reaction. Depth lives in `Thread.current`, not an ivar
299
+ # or Mutex: the dispatcher is shared across threads and cascades re-enter on one thread.
300
+ # The depth limit is checked by the reacting interpreter, not here.
455
301
  #
456
302
  # @param verb [String] the fully qualified verb of the reaction's target command
457
- # @param saga_correlation [Hash{String => Object}, nil] correlation head => value when a
458
- # saga leg dispatches; nil for a policy
459
- # @param args [Hash{Symbol => Object}] the flat facts, read exactly as `dispatch_flat`
460
- # reads them, so `:to` and `:with` keys route rather than count as facts
461
- # @return [Runtime::Dispatcher::Result] the result of the nested dispatch
462
- # @raise [Runtime::UnknownVerb] if the verb names nothing declared
463
- # @raise [StandardError] any class in `Runtime::DOMAIN_REFUSALS` when the target
464
- # refuses; the policy and saga interpreters rescue these as recorded outcomes
303
+ # @param saga_correlation [Hash{String => Object}, nil] set when a saga leg dispatches
304
+ # @param args [Hash{Symbol => Object}] flat facts, read as `dispatch_flat` reads them
305
+ # @return [Runtime::Dispatcher::Result] the nested dispatch's result
306
+ # @raise [StandardError] any `Runtime::DOMAIN_REFUSALS` class; interpreters rescue these
465
307
  def reenter(verb, saga_correlation: nil, **args)
466
308
  depth = Thread.current[:hecks_reaction_depth].to_i
467
309
  Thread.current[:hecks_reaction_depth] = depth + 1
@@ -472,13 +314,12 @@ module Hecks
472
314
 
473
315
  # Answers whether the calling thread's reaction cascade is as deep as it may go.
474
316
  #
475
- # @return [Boolean] true once this thread's nested `reenter` calls number
476
- # `MAX_REACTION_DEPTH` or more; other threads' cascades are not counted
317
+ # @return [Boolean] true once nested `reenter` calls on this thread reach the limit
477
318
  def reaction_depth_reached? = Thread.current[:hecks_reaction_depth].to_i >= MAX_REACTION_DEPTH
478
319
 
479
- # Reads the cascade limit, for the reason an interpreter records when it declines to react.
320
+ # Reads the cascade limit an interpreter records when it declines to react.
480
321
  #
481
- # @return [Integer] `MAX_REACTION_DEPTH`, the number of nested reactions allowed
322
+ # @return [Integer] `MAX_REACTION_DEPTH`
482
323
  def max_reaction_depth = MAX_REACTION_DEPTH
483
324
 
484
325
  private