hecks 1.3.0 → 1.4.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 (355) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/claude_code.rb +7 -7
  3. data/lib/hecks/adapters/driven/d1.rb +187 -23
  4. data/lib/hecks/adapters/driven/folder.rb +10 -10
  5. data/lib/hecks/adapters/driven/google_authentication.rb +8 -8
  6. data/lib/hecks/adapters/driven/governance_authorization.rb +31 -13
  7. data/lib/hecks/adapters/driven/heki/journal.rb +5 -4
  8. data/lib/hecks/adapters/driven/heki/saga_store.rb +5 -5
  9. data/lib/hecks/adapters/driven/heki.rb +13 -7
  10. data/lib/hecks/adapters/driven/identity_registry.rb +2 -2
  11. data/lib/hecks/adapters/driven/in_memory_ordering.rb +3 -3
  12. data/lib/hecks/adapters/driven/lambda/client.rb +34 -9
  13. data/lib/hecks/adapters/driven/lambda.rb +39 -33
  14. data/lib/hecks/adapters/driven/local_storage.rb +17 -10
  15. data/lib/hecks/adapters/driven/memory.rb +205 -9
  16. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +1 -1
  17. data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
  18. data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
  19. data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
  20. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
  21. data/lib/hecks/adapters/driven/postgres.rb +175 -28
  22. data/lib/hecks/adapters/driven/prism.rb +4 -4
  23. data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
  24. data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
  25. data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
  26. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
  27. data/lib/hecks/adapters/driven/sqlite.rb +181 -21
  28. data/lib/hecks/adapters/driven.rb +4 -4
  29. data/lib/hecks/adapters/driving/github_webhook.rb +18 -18
  30. data/lib/hecks/behaviors/dsl.rb +2 -2
  31. data/lib/hecks/behaviors/expectations.rb +23 -23
  32. data/lib/hecks/behaviors/ir.rb +1 -1
  33. data/lib/hecks/behaviors/rspec.rb +1 -1
  34. data/lib/hecks/behaviors/runner.rb +2 -2
  35. data/lib/hecks/behaviors.rb +1 -1
  36. data/lib/hecks/bluebook/aggregate.rb +13 -13
  37. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +10 -10
  38. data/lib/hecks/bluebook/assembly/build.rb +1 -1
  39. data/lib/hecks/bluebook/assembly/contract.rb +39 -16
  40. data/lib/hecks/bluebook/assembly/contracts.rb +40 -38
  41. data/lib/hecks/bluebook/assembly/marks.rb +18 -18
  42. data/lib/hecks/bluebook/assembly/specializer.rb +19 -21
  43. data/lib/hecks/bluebook/assembly.rb +9 -9
  44. data/lib/hecks/bluebook/attribute.rb +9 -9
  45. data/lib/hecks/bluebook/behaviour/aggregate.rb +11 -11
  46. data/lib/hecks/bluebook/behaviour/attribute.rb +5 -5
  47. data/lib/hecks/bluebook/behaviour/chapter.rb +23 -5
  48. data/lib/hecks/bluebook/behaviour/command.rb +23 -23
  49. data/lib/hecks/bluebook/behaviour/domain_port.rb +7 -7
  50. data/lib/hecks/bluebook/behaviour/entity.rb +8 -8
  51. data/lib/hecks/bluebook/behaviour/hexagon.rb +4 -4
  52. data/lib/hecks/bluebook/behaviour/lifecycle.rb +5 -5
  53. data/lib/hecks/bluebook/behaviour/policy.rb +12 -12
  54. data/lib/hecks/bluebook/behaviour/process_manager.rb +7 -7
  55. data/lib/hecks/bluebook/behaviour/query.rb +1 -1
  56. data/lib/hecks/bluebook/behaviour/read_model.rb +8 -8
  57. data/lib/hecks/bluebook/behaviour/traits.rb +12 -12
  58. data/lib/hecks/bluebook/behaviour/value_object.rb +6 -6
  59. data/lib/hecks/bluebook/capabilities.rb +27 -0
  60. data/lib/hecks/bluebook/chapter.rb +28 -9
  61. data/lib/hecks/bluebook/command.rb +12 -12
  62. data/lib/hecks/bluebook/domain_port.rb +9 -9
  63. data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
  64. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
  65. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +96 -96
  66. data/lib/hecks/bluebook/dsl/attribute_collector.rb +41 -41
  67. data/lib/hecks/bluebook/dsl/binding_proxy.rb +22 -2
  68. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +111 -74
  69. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +48 -30
  70. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
  71. data/lib/hecks/bluebook/dsl/command_builder.rb +103 -103
  72. data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
  73. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
  74. data/lib/hecks/bluebook/dsl/entity_builder.rb +56 -56
  75. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
  76. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +89 -30
  77. data/lib/hecks/bluebook/dsl/identity_declaration.rb +17 -17
  78. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
  79. data/lib/hecks/bluebook/dsl/policy_builder.rb +30 -21
  80. data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
  81. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
  82. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +35 -35
  83. data/lib/hecks/bluebook/dsl/query_builder.rb +5 -5
  84. data/lib/hecks/bluebook/dsl/read_model_builder.rb +34 -34
  85. data/lib/hecks/bluebook/dsl/rule_reference.rb +41 -39
  86. data/lib/hecks/bluebook/dsl/translation_builder.rb +9 -9
  87. data/lib/hecks/bluebook/dsl/value_object_builder.rb +16 -16
  88. data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
  89. data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
  90. data/lib/hecks/bluebook/entity.rb +11 -11
  91. data/lib/hecks/bluebook/expression/ast_json.rb +20 -20
  92. data/lib/hecks/bluebook/expression/ast_reader.rb +3 -3
  93. data/lib/hecks/bluebook/expression/canonical_form.rb +9 -9
  94. data/lib/hecks/bluebook/expression/evaluator.rb +18 -18
  95. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +18 -18
  96. data/lib/hecks/bluebook/expression/resolver.rb +60 -62
  97. data/lib/hecks/bluebook/hexagon.rb +1 -1
  98. data/lib/hecks/bluebook/lifecycle.rb +1 -1
  99. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -1
  100. data/lib/hecks/bluebook/meta_validator/judge.rb +101 -87
  101. data/lib/hecks/bluebook/meta_validator/plan.rb +39 -39
  102. data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -2
  103. data/lib/hecks/bluebook/meta_validator/readings.rb +43 -43
  104. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +44 -37
  105. data/lib/hecks/bluebook/meta_validator/shapes.rb +25 -21
  106. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +145 -31
  107. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +6 -6
  108. data/lib/hecks/bluebook/meta_validator/world_judge.rb +5 -5
  109. data/lib/hecks/bluebook/meta_validator.rb +70 -70
  110. data/lib/hecks/bluebook/model_check.rb +170 -84
  111. data/lib/hecks/bluebook/pattern_subset.rb +9 -9
  112. data/lib/hecks/bluebook/policy.rb +15 -13
  113. data/lib/hecks/bluebook/process_manager.rb +14 -14
  114. data/lib/hecks/bluebook/project_register.rb +6 -6
  115. data/lib/hecks/bluebook/query.rb +4 -4
  116. data/lib/hecks/bluebook/read_model.rb +14 -14
  117. data/lib/hecks/bluebook/reference.rb +8 -8
  118. data/lib/hecks/bluebook/smoke_test.rb +19 -19
  119. data/lib/hecks/bluebook/synthesizer.rb +12 -12
  120. data/lib/hecks/bluebook/translation.rb +4 -4
  121. data/lib/hecks/bluebook/value_object.rb +6 -6
  122. data/lib/hecks/bluebook.rb +2 -2
  123. data/lib/hecks/codemod/legacy_dispatch_args.rb +299 -0
  124. data/lib/hecks/codemod/legacy_dispatch_recorder.rb +186 -0
  125. data/lib/hecks/codemod.rb +36 -35
  126. data/lib/hecks/construct.rb +6 -6
  127. data/lib/hecks/corpus.rb +317 -0
  128. data/lib/hecks/deprecation.rb +95 -0
  129. data/lib/hecks/doc/reference.rb +19 -19
  130. data/lib/hecks/embryonaut_bluebook.rb +11 -11
  131. data/lib/hecks/facade/cli_door.rb +69 -10
  132. data/lib/hecks/facade/cli_runner.rb +105 -24
  133. data/lib/hecks/facade/command_request.rb +23 -0
  134. data/lib/hecks/facade/handle.rb +79 -32
  135. data/lib/hecks/facade/json_door.rb +106 -25
  136. data/lib/hecks/facade/surface/aggregate_door.rb +42 -27
  137. data/lib/hecks/facade/surface/chapter.rb +26 -17
  138. data/lib/hecks/facade/surface.rb +16 -3
  139. data/lib/hecks/facade.rb +15 -4
  140. data/lib/hecks/forms/app.rb +46 -30
  141. data/lib/hecks/forms/command_form_renderer.rb +70 -9
  142. data/lib/hecks/forms/field_renderer.rb +142 -6
  143. data/lib/hecks/forms/field_shape.rb +180 -19
  144. data/lib/hecks/forms/html.rb +51 -7
  145. data/lib/hecks/forms/index_renderer.rb +14 -2
  146. data/lib/hecks/forms/params.rb +120 -23
  147. data/lib/hecks/forms/port_argument.rb +2 -2
  148. data/lib/hecks/forms/query_form_renderer.rb +2 -2
  149. data/lib/hecks/forms/record_renderer.rb +2 -2
  150. data/lib/hecks/forms/record_table.rb +1 -1
  151. data/lib/hecks/forms/value_object_shape.rb +3 -3
  152. data/lib/hecks/forms.rb +24 -4
  153. data/lib/hecks/fqn.rb +1 -1
  154. data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
  155. data/lib/hecks/framework.rb +48 -17
  156. data/lib/hecks/freezer.rb +11 -11
  157. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +90 -90
  158. data/lib/hecks/fuzzing/combination_miner.rb +119 -0
  159. data/lib/hecks/fuzzing/concurrent_dispatch.rb +140 -45
  160. data/lib/hecks/fuzzing/coverage_campaign.rb +118 -0
  161. data/lib/hecks/fuzzing/differential.rb +158 -0
  162. data/lib/hecks/fuzzing/domain_generator.rb +694 -0
  163. data/lib/hecks/fuzzing/era_boundary.rb +37 -18
  164. data/lib/hecks/fuzzing/form_census.rb +35 -20
  165. data/lib/hecks/fuzzing/generated_domain_check.rb +95 -0
  166. data/lib/hecks/fuzzing/invalid_value_generator.rb +6 -6
  167. data/lib/hecks/fuzzing/isolated_boot.rb +62 -62
  168. data/lib/hecks/fuzzing/nondeterministic.rb +67 -0
  169. data/lib/hecks/fuzzing/persistence_parity.rb +19 -21
  170. data/lib/hecks/fuzzing/properties/corrections.rb +9 -9
  171. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +154 -49
  172. data/lib/hecks/fuzzing/properties/guards.rb +42 -42
  173. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +32 -32
  174. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +22 -35
  175. data/lib/hecks/fuzzing/properties/outbox.rb +24 -24
  176. data/lib/hecks/fuzzing/properties/querying.rb +17 -17
  177. data/lib/hecks/fuzzing/properties.rb +64 -40
  178. data/lib/hecks/fuzzing/qa_settings.rb +152 -0
  179. data/lib/hecks/fuzzing/replay.rb +114 -114
  180. data/lib/hecks/fuzzing/rotation_priority.rb +10 -10
  181. data/lib/hecks/fuzzing/rust_gap_manifest.rb +113 -0
  182. data/lib/hecks/fuzzing/self_consistency.rb +141 -112
  183. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +35 -35
  184. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +18 -11
  185. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +13 -12
  186. data/lib/hecks/fuzzing/sequence_generator/picker.rb +21 -12
  187. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +58 -31
  188. data/lib/hecks/fuzzing/sequence_generator.rb +111 -23
  189. data/lib/hecks/fuzzing/shrinker.rb +197 -0
  190. data/lib/hecks/fuzzing/structural_skips.rb +23 -130
  191. data/lib/hecks/fuzzing/sweep_depth.rb +4 -4
  192. data/lib/hecks/fuzzing/target_capabilities.rb +49 -18
  193. data/lib/hecks/fuzzing/value_generator.rb +14 -14
  194. data/lib/hecks/fuzzing.rb +5 -0
  195. data/lib/hecks/grammar/evolve.rb +10 -10
  196. data/lib/hecks/grammar.rb +7 -7
  197. data/lib/hecks/ir.rb +13 -13
  198. data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
  199. data/lib/hecks/language/bluebook/policy.bluebook +11 -1
  200. data/lib/hecks/language/bluebook/vocabulary.bluebook +348 -13
  201. data/lib/hecks/language/oidc.json +5 -0
  202. data/lib/hecks/literal.rb +9 -9
  203. data/lib/hecks/naming.rb +25 -25
  204. data/lib/hecks/ports/access_control.rb +58 -2
  205. data/lib/hecks/ports/agent/answers.rb +83 -6
  206. data/lib/hecks/ports/agent.rb +119 -35
  207. data/lib/hecks/ports/authentication.rb +44 -4
  208. data/lib/hecks/ports/authorization.rb +53 -11
  209. data/lib/hecks/ports/clock.rb +42 -23
  210. data/lib/hecks/ports/extraction.rb +16 -0
  211. data/lib/hecks/ports/identity_assignment.rb +24 -2
  212. data/lib/hecks/ports/identity_generation.rb +17 -3
  213. data/lib/hecks/ports/identity_resolution.rb +18 -1
  214. data/lib/hecks/ports/loading.rb +4 -0
  215. data/lib/hecks/ports/persistence/append_only.rb +172 -8
  216. data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
  217. data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
  218. data/lib/hecks/ports/persistence/execution.rb +4 -0
  219. data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
  220. data/lib/hecks/ports/persistence/plugin.rb +42 -4
  221. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
  222. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
  223. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
  224. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
  225. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
  226. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
  227. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
  228. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
  229. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
  230. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +118 -66
  231. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
  232. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +28 -5
  233. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
  234. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
  235. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
  236. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +33 -9
  237. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
  238. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +57 -4
  239. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +314 -90
  241. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
  242. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
  243. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
  244. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +42 -8
  245. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
  246. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
  247. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
  248. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -19
  249. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +5 -5
  250. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +1 -1
  251. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
  252. data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
  253. data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
  254. data/lib/hecks/ports/persistence/repository_factory.rb +23 -3
  255. data/lib/hecks/ports/persistence/state_codec.rb +319 -0
  256. data/lib/hecks/ports/persistence.rb +36 -1
  257. data/lib/hecks/ports/projection.rb +61 -7
  258. data/lib/hecks/ports/query/in_memory.rb +3 -3
  259. data/lib/hecks/ports/query/ordering.rb +6 -6
  260. data/lib/hecks/ports/query.rb +35 -0
  261. data/lib/hecks/projections/bootstrap_table.rb +112 -0
  262. data/lib/hecks/projections/diagrams.rb +75 -75
  263. data/lib/hecks/projections/glossary/html.rb +3 -3
  264. data/lib/hecks/projections/glossary/markdown.rb +3 -3
  265. data/lib/hecks/projections/glossary/mermaid.rb +1 -1
  266. data/lib/hecks/projections/glossary/sections.rb +1 -1
  267. data/lib/hecks/projections/glossary/sentences.rb +5 -5
  268. data/lib/hecks/projections/glossary.rb +15 -15
  269. data/lib/hecks/projections/ir.rb +1 -1
  270. data/lib/hecks/projections/model/deviations.rb +18 -17
  271. data/lib/hecks/projections/model.rb +25 -21
  272. data/lib/hecks/projections/oidc.rb +7 -7
  273. data/lib/hecks/projections/parser_table.rb +5 -5
  274. data/lib/hecks/projections/reference.rb +3 -3
  275. data/lib/hecks/projections/rust_vocabulary.rb +443 -0
  276. data/lib/hecks/projections/shape.rb +2 -2
  277. data/lib/hecks/projections/statements.rb +11 -11
  278. data/lib/hecks/projections/vocabulary.rb +9 -9
  279. data/lib/hecks/projections.rb +5 -3
  280. data/lib/hecks/projector/cli_projector.rb +29 -29
  281. data/lib/hecks/projector/docs_projector.rb +13 -13
  282. data/lib/hecks/projector/exporter.rb +42 -21
  283. data/lib/hecks/projector/ir_projector.rb +1 -1
  284. data/lib/hecks/projector/narrate_projector.rb +11 -11
  285. data/lib/hecks/projector/target.rb +13 -13
  286. data/lib/hecks/projector.rb +15 -15
  287. data/lib/hecks/query_ir.rb +47 -47
  288. data/lib/hecks/query_specification/common/comparators.rb +19 -3
  289. data/lib/hecks/query_specification/common/comparison.rb +109 -27
  290. data/lib/hecks/query_specification/common/dsl.rb +65 -9
  291. data/lib/hecks/query_specification/common/null_policy.rb +57 -13
  292. data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
  293. data/lib/hecks/query_specification/common/options.rb +25 -0
  294. data/lib/hecks/query_specification/field_path.rb +69 -15
  295. data/lib/hecks/query_specification/hop_path.rb +57 -20
  296. data/lib/hecks/query_specification/read_model/specification.rb +4 -0
  297. data/lib/hecks/rendering.rb +3 -3
  298. data/lib/hecks/router/namespace_installer.rb +3 -3
  299. data/lib/hecks/router.rb +1 -1
  300. data/lib/hecks/runtime/aggregate_lock.rb +11 -11
  301. data/lib/hecks/runtime/boot_gates.rb +3 -3
  302. data/lib/hecks/runtime/caller.rb +8 -8
  303. data/lib/hecks/runtime/capability_graph.rb +2 -2
  304. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +22 -25
  305. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +42 -63
  306. data/lib/hecks/runtime/command_interpreter.rb +159 -102
  307. data/lib/hecks/runtime/command_rules/admissibility.rb +69 -69
  308. data/lib/hecks/runtime/command_rules/arithmetic.rb +137 -91
  309. data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
  310. data/lib/hecks/runtime/command_rules/emission.rb +18 -1
  311. data/lib/hecks/runtime/command_rules/references.rb +35 -35
  312. data/lib/hecks/runtime/command_rules.rb +3 -0
  313. data/lib/hecks/runtime/dependency_planning.rb +11 -11
  314. data/lib/hecks/runtime/dispatcher.rb +283 -90
  315. data/lib/hecks/runtime/entity_element.rb +151 -67
  316. data/lib/hecks/runtime/entity_interpreter.rb +108 -82
  317. data/lib/hecks/runtime/errors.rb +19 -19
  318. data/lib/hecks/runtime/event.rb +6 -6
  319. data/lib/hecks/runtime/identity.rb +22 -22
  320. data/lib/hecks/runtime/instance.rb +39 -14
  321. data/lib/hecks/runtime/interpreting.rb +12 -12
  322. data/lib/hecks/runtime/invocation.rb +276 -0
  323. data/lib/hecks/runtime/loader.rb +14 -14
  324. data/lib/hecks/runtime/outbox.rb +23 -23
  325. data/lib/hecks/runtime/policy_interpreter.rb +54 -54
  326. data/lib/hecks/runtime/port_operation_interpreter.rb +22 -19
  327. data/lib/hecks/runtime/query_interpreter.rb +55 -64
  328. data/lib/hecks/runtime/reaction_invocation.rb +20 -20
  329. data/lib/hecks/runtime/read_model_interpreter.rb +40 -40
  330. data/lib/hecks/runtime/rebuild_sweep.rb +4 -4
  331. data/lib/hecks/runtime/reference_hop.rb +6 -6
  332. data/lib/hecks/runtime/refusal_wording.rb +92 -115
  333. data/lib/hecks/runtime/registry/saga_persistence.rb +21 -21
  334. data/lib/hecks/runtime/registry/verification.rb +36 -26
  335. data/lib/hecks/runtime/registry.rb +45 -28
  336. data/lib/hecks/runtime/remote_dispatcher.rb +38 -23
  337. data/lib/hecks/runtime/routing.rb +10 -153
  338. data/lib/hecks/runtime/saga_interpreter/correlation.rb +17 -17
  339. data/lib/hecks/runtime/saga_interpreter.rb +64 -64
  340. data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
  341. data/lib/hecks/runtime/tenant_check.rb +9 -9
  342. data/lib/hecks/runtime/tenant_scope.rb +5 -5
  343. data/lib/hecks/runtime/value/admission.rb +75 -30
  344. data/lib/hecks/runtime/value/coercion.rb +318 -127
  345. data/lib/hecks/runtime/value/entity_list_coercion.rb +34 -34
  346. data/lib/hecks/runtime/value.rb +21 -21
  347. data/lib/hecks/runtime.rb +7 -7
  348. data/lib/hecks/storehouse.rb +64 -64
  349. data/lib/hecks/version.rb +3 -3
  350. data/lib/hecks/vocabulary.rb +205 -4
  351. data/lib/hecks.rb +13 -11
  352. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +11 -11
  353. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +14 -12
  354. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +11 -11
  355. metadata +22 -2
@@ -6,7 +6,7 @@ require_relative "identity"
6
6
  require_relative "instance"
7
7
  require_relative "value"
8
8
  require_relative "refusal_wording"
9
- require_relative "routing"
9
+ require_relative "invocation"
10
10
  require_relative "dependency_planning"
11
11
  require_relative "../ports/persistence/execution"
12
12
  require_relative "entity_element"
@@ -23,9 +23,9 @@ module Hecks
23
23
  # entity-owned commands.
24
24
  class EntityInterpreter
25
25
  include Interpreting
26
- # THE SAME PAYLOAD GATE aggregate commands and port operations already
26
+ # The same payload gate aggregate commands and port operations already
27
27
  # run — bug audit H1 (docs/audits/2026-08-10-main-bug-audit.md): this
28
- # class used to run NEITHER refuse_unknown_arguments NOR
28
+ # class used to run neither refuse_unknown_arguments nor
29
29
  # refuse_absent_arguments, on a comment claiming "an entity inherits
30
30
  # its aggregate's own gate." Nothing on the entity dispatch path ever
31
31
  # ran one — confirmed live, `LedgerEntry.Reverse` accepted an
@@ -39,11 +39,9 @@ module Hecks
39
39
 
40
40
  attr_reader :registry
41
41
 
42
- # THE DECLARED ORDER, HAND-TYPED mirrors Vocabulary::EntityDispatchOrder
43
- # (language/bluebook/vocabulary.bluebook:217-232), held equal to it by
44
- # spec/vocabulary_conformance_spec.rb the same way CommandInterpreter's
45
- # own DISPATCH_ORDER is; see that constant's doc comment for why this is
46
- # hand-typed rather than read live off the meta-domain at every dispatch.
42
+ # **The declared order** — Vocabulary::EntityDispatchOrder
43
+ # (language/bluebook/vocabulary.bluebook), read off the generated table
44
+ # the same way CommandInterpreter's own DISPATCH_ORDER is.
47
45
  # `refuse_unknown_arguments`/`refuse_absent_arguments` now lead it, same
48
46
  # position `AggregateDispatchOrder` holds them at (H1, above) — the only
49
47
  # remaining difference from the aggregate order is no
@@ -55,7 +53,7 @@ module Hecks
55
53
  # see that constant's own comment.
56
54
  MAX_STALE_WRITE_RETRIES = 5
57
55
 
58
- # `instance` is the PARENT aggregate record (what gets saved and
56
+ # `instance` is the parent aggregate record (what gets saved and
59
57
  # returned) ; `element`/`view` are the entity piece itself — `view`
60
58
  # wraps `element` as it stood at `locate_element`, pre-mutation, and
61
59
  # `enforce_ensures` builds its own settled wrapper off `element` as it
@@ -63,7 +61,7 @@ module Hecks
63
61
  #
64
62
  # `chain` — S17, ADR 0026 — every entity the dotted verb passes
65
63
  # through, root-first (`[Handler, Dispatch]` for `Handler.Dispatch.
66
- # Bind`) ; `entity`/`entity_name` stay the CHAIN'S OWN LAST entry,
64
+ # Bind`) ; `entity`/`entity_name` stay the chain's own last entry,
67
65
  # the one a command actually belongs to and a mutation actually
68
66
  # targets, so every step written before this ADR (enforce_givens,
69
67
  # apply_mutations, advance_lifecycle, element_identity, ...) reads
@@ -76,7 +74,50 @@ module Hecks
76
74
  Context = Struct.new(:domain, :aggregate, :entity, :entity_name, :command, :command_name,
77
75
  :args, :repository, :instance, :chain, :element, :view, :transition,
78
76
  :old_element, :result, :route, :plan, :persistence_outcome, :dry_run, :outbox_rows,
79
- :correction_bindings)
77
+ :correction_bindings, :invocation)
78
+
79
+ # A dotted entity verb resolved against its aggregate. `Resolution.of`
80
+ # lives here rather than as a second public verb on the interpreter
81
+ # (spec/runtime/command_rules_spec.rb holds each interpreter to one),
82
+ # so `Dispatcher` can resolve it at the point `Invocation.from_call`
83
+ # reads the declaring command — after `to:` is parsed, before the
84
+ # facts are.
85
+ Resolution = Data.define(:entity_names, :chain, :command_name, :command) do
86
+ # Refuses UnknownVerb for an unknown entity or command.
87
+ def self.of(aggregate, dotted)
88
+ *entity_names, command_name = dotted.to_s.split(".")
89
+ if entity_names.empty?
90
+ raise UnknownVerb, RefusalWording.render_site("UnknownVerb", "entity_unknown",
91
+ aggregate: aggregate.hecks_name, entity: dotted.to_s)
92
+ end
93
+
94
+ chain = walk(aggregate, entity_names)
95
+ command = chain.last.command(command_name) ||
96
+ raise(UnknownVerb, RefusalWording.render_site("UnknownVerb", "entity_no_command",
97
+ entity: chain.last.hecks_name, command: command_name))
98
+ new(entity_names: entity_names, chain: chain, command_name: command_name, command: command)
99
+ end
100
+
101
+ # **One hop per dotted segment** — `ProcessManager.Handler.Dispatch.Bind`
102
+ # (once the dispatcher has already stripped "Domain::Aggregate.")
103
+ # walks Handler off the aggregate, then Dispatch off Handler, each
104
+ # step reading `.entities` exactly the way the single-level case
105
+ # always did — a nested entity is "structurally interchangeable
106
+ # with an aggregate" (Entity's own header) for precisely this
107
+ # reason. Two levels is what Handler/Dispatch need today ; nothing
108
+ # here assumes it stops at two.
109
+ def self.walk(aggregate, entity_names)
110
+ owner = aggregate
111
+ entity_names.map do |name|
112
+ found = owner.entities.find { |piece| piece.hecks_name == name } ||
113
+ raise(UnknownVerb, RefusalWording.render_site("UnknownVerb", "entity_unknown",
114
+ aggregate: owner.hecks_name, entity: name))
115
+ owner = found
116
+ found
117
+ end
118
+ end
119
+ private_class_method :walk
120
+ end
80
121
 
81
122
  def initialize(registry, rules:)
82
123
  @registry = registry
@@ -87,39 +128,37 @@ module Hecks
87
128
  # comment for the shared reasoning (Dispatcher#dry_run?'s own entry
88
129
  # point). `step_save`/`step_emit` are the only two steps here that
89
130
  # read it either.
90
- # RETRIES THE WHOLE METHOD BODY on `StaleWrite` — same reasoning as
131
+ # Retries the whole method body on `StaleWrite` — same reasoning as
91
132
  # `CommandInterpreter#call`'s own retry: a fresh `ctx`, a fresh
92
133
  # `step_hydrate_parent`/`step_locate_element` re-reading current
93
134
  # state.
94
- def call(domain, aggregate, dotted, legacy_args, route: nil, with: nil, dry_run: false)
95
- *entity_names, command_name = dotted.to_s.split(".")
96
- if entity_names.empty?
97
- raise UnknownVerb, RefusalWording.render("UnknownVerb", "entity_unknown",
98
- aggregate: aggregate.hecks_name, entity: dotted.to_s.inspect)
99
- end
100
-
101
- chain = walk_entity_chain(aggregate, entity_names)
102
- entity = chain.last
103
- command = entity.command(command_name) ||
104
- raise(UnknownVerb, RefusalWording.render("UnknownVerb", "entity_no_command",
105
- entity: entity.hecks_name, command: command_name.inspect))
106
-
107
- args = Routing.payload(command, with: with, legacy: legacy_args)
135
+ #
136
+ # `resolution` is #resolve's answer; `invocation` the
137
+ # `Runtime::Invocation` `Dispatcher` built — `ctx.args` is its
138
+ # `to_args`, `ctx.route` its `target`.
139
+ def call(domain, aggregate, resolution, invocation, dry_run: false)
140
+ chain = resolution.chain
141
+ entity = chain.last
142
+ command = resolution.command
143
+ command_name = resolution.command_name
144
+ route = invocation.target
145
+ args = invocation.to_args
108
146
  attempt = 0
109
147
  begin
110
- ctx = Context.new(domain, aggregate, entity, entity_names.join("."), command, command_name, args)
148
+ ctx = Context.new(domain, aggregate, entity, resolution.entity_names.join("."), command, command_name, args)
149
+ ctx.invocation = invocation
111
150
  ctx.chain = chain
112
151
  ctx.route = route
113
152
  ctx.dry_run = dry_run
114
153
  # `root_aggregate:` — `entity` is the immediate owner (what
115
154
  # `owner_fields` inside the Analyzer means), but a `parent.X`
116
- # read inside this command's own given/ensures means the ROOT
117
- # aggregate's own field, not the entity's — `aggregate` here IS
155
+ # read inside this command's own given/ensures means the root
156
+ # aggregate's own field, not the entity's — `aggregate` here is
118
157
  # that root (this method's own first parameter, never the
119
158
  # entity). See DependencyPlanning::Analyzer.call's own header
120
159
  # for the bug this closes.
121
160
  ctx.plan = DependencyPlanning::Analyzer.call(aggregate: entity, command: command, root_aggregate: aggregate)
122
- # RESOLVED HERE, ONCE — see CommandInterpreter#call's own comment;
161
+ # **Resolved here, once** — see CommandInterpreter#call's own comment;
123
162
  # `step_hydrate_parent` reads `ctx.repository` without re-fetching.
124
163
  ctx.repository = @registry.repository(domain, aggregate)
125
164
  lock_id = Identity.best_effort(aggregate, args, route)
@@ -134,28 +173,13 @@ module Hecks
134
173
 
135
174
  private
136
175
 
137
- # ONE HOP PER DOTTED SEGMENT `ProcessManager.Handler.Dispatch.Bind`
138
- # (once the dispatcher has already stripped "Domain::Aggregate.")
139
- # walks Handler off the aggregate, then Dispatch off Handler, each
140
- # step reading `.entities` exactly the way the single-level case
141
- # always did — a nested entity is "structurally interchangeable
142
- # with an aggregate" (Entity's own header) for precisely this
143
- # reason. Two levels is what Handler/Dispatch need today ; nothing
144
- # here assumes it stops at two.
145
- def walk_entity_chain(aggregate, entity_names)
146
- owner = aggregate
147
- entity_names.map do |name|
148
- found = owner.entities.find { |piece| piece.hecks_name == name } ||
149
- raise(UnknownVerb, RefusalWording.render("UnknownVerb", "entity_unknown",
150
- aggregate: owner.hecks_name, entity: name.inspect))
151
- owner = found
152
- found
153
- end
154
- end
176
+ # **A no-op, and untraced**Vocabulary::EntityDispatchOrder's
177
+ # decode_arguments. See CommandInterpreter#step_decode_arguments.
178
+ def step_decode_arguments(_ctx); end
155
179
 
156
180
  # `extra_identity_heads:` — every entity `ctx.chain` walks through, not
157
181
  # just the root aggregate `ArgumentGate` already knows about. A
158
- # two-hop dispatch (`Handler.Dispatch.Bind`) is addressed by BOTH
182
+ # two-hop dispatch (`Handler.Dispatch.Bind`) is addressed by both
159
183
  # hops' own identity, each read straight out of `args` by
160
184
  # `EntityElement#element_of` — refusing those as unknown would refuse
161
185
  # every legitimate nested-entity dispatch there is, the same reasoning
@@ -202,30 +226,30 @@ module Hecks
202
226
  ctx.element = step(:locate_element) do
203
227
  EntityElement.locate_chain(ctx.aggregate, ctx.chain, ctx.instance, ctx.args, ctx.command_name, ctx.route)
204
228
  end
205
- # `view` was hydrated ONCE, here, into its OWN state hash
229
+ # `view` was hydrated once, here, into its own state hash
206
230
  # (Value.hydrate builds a fresh Hash — never aliased with `element`)
207
231
  # — exactly right for enforce_givens, which must read pre-mutation.
208
232
  ctx.view = Instance.new(aggregate: ctx.entity, id: EntityElement.element_identity(ctx.entity, ctx.element).to_s,
209
233
  state: ctx.element)
210
234
  end
211
235
 
212
- # BUG#30 — THE ENTITY-LEVEL HALF OF `CommandInterpreter#step_enforce_
236
+ # BUG#30 — the entity-level half of `CommandInterpreter#step_enforce_
213
237
  # givens`'s own structural-before-declared ordering (see that
214
238
  # method's comment for the shared reasoning): "does the fact this
215
239
  # command's `corrects` names even exist" is checked here too, once,
216
240
  # before the entity's own `given`s.
217
241
  #
218
- # ADMISSIBILITY IS CHECKED AGAINST THE PARENT/ROOT, NOT THE ENTITY
219
- # deliberately `ctx.instance`/`ctx.aggregate` (the PARENT aggregate
220
- # record and the ROOT aggregate construct), never `ctx.view`/
242
+ # **Admissibility is checked against the parent/root, not the entity**
243
+ # deliberately `ctx.instance`/`ctx.aggregate` (the parent aggregate
244
+ # record and the root aggregate construct), never `ctx.view`/
221
245
  # `ctx.entity` (the entity's own pre-mutation view/construct). This
222
- # is not a simplification; it is the ONLY choice that lines up with
246
+ # is not a simplification; it is the only choice that lines up with
223
247
  # how the event being corrected was actually recorded: an entity has
224
248
  # no event stream of its own — `CommandRules::Emission#emit` (called
225
- # from THIS class's own `step_emit`, and from `CommandInterpreter`'s
249
+ # from this class's own `step_emit`, and from `CommandInterpreter`'s
226
250
  # `step_emit` for an aggregate-level command alike) always stamps an
227
- # emitted event with the ROOT aggregate's own qualified name
228
- # (`"#{domain}::#{aggregate.hecks_name}"`) and the PARENT record's
251
+ # emitted event with the root aggregate's own qualified name
252
+ # (`"#{domain}::#{aggregate.hecks_name}"`) and the parent record's
229
253
  # own id (`ctx.instance.id`), regardless of which level dispatched
230
254
  # it. `enforce_correction_target` (CommandRules::Admissibility)
231
255
  # looks a correction target up by exactly those two fields plus the
@@ -234,31 +258,31 @@ module Hecks
234
258
  # carry, and every entity-level correction would refuse
235
259
  # (NothingToCorrect) even against a real, already-emitted event.
236
260
  # `qa/stress_domains/corrections`' own `Entry.Amend` (corrects
237
- # "EntryRecorded", which `Ledger.Record` — an AGGREGATE-level
261
+ # "EntryRecorded", which `Ledger.Record` — an aggregate-level
238
262
  # command — actually emits) is exactly this shape: the corrected
239
- # event's `aggregate`/`id` are the LEDGER's, never the Entry's own
263
+ # event's `aggregate`/`id` are the ledger's, never the Entry's own
240
264
  # (an Entry has no id an event could be filed under in the first
241
265
  # place). `Fuzzing::Properties::Corrections#corrections_reference_
242
266
  # an_emitted_event` independently encodes the identical rule
243
- # (`aggregate_key` built off the OUTER aggregate for both the
267
+ # (`aggregate_key` built off the outer aggregate for both the
244
268
  # `corrects` target and the `emits` produced event, regardless of
245
269
  # entity nesting depth) — this is that property's dispatch-time
246
270
  # enforcement counterpart, not a new invention.
247
271
  #
248
272
  # One structural consequence, worth being explicit about for a
249
- # Rust port: because the lookup is scoped to the PARENT record
273
+ # Rust port: because the lookup is scoped to the parent record
250
274
  # (not to any one entity element within it), an entity-level
251
275
  # `corrects` only proves "this parent record has emitted the named
252
- # event at some point" — it does NOT, and cannot, further narrow
276
+ # event at some point" — it does not, and cannot, further narrow
253
277
  # to "...specifically for THIS entity element" (a Ledger with three
254
278
  # Entries all satisfy the same `EntryRecorded`-was-emitted check).
255
- # That is not a gap this fix introduces: it is the SAME granularity
279
+ # That is not a gap this fix introduces: it is the same granularity
256
280
  # the aggregate-level check already has (one record, one event
257
281
  # history), just observed from one level down. A command wanting a
258
282
  # tighter, element-specific correlation has to encode it itself, in
259
283
  # its own `given`s, off `correction`-bound payload fields.
260
284
  #
261
- # `correction:` bindings computed here are threaded through to BOTH
285
+ # `correction:` bindings computed here are threaded through to both
262
286
  # halves of the same command's admissibility, same as the
263
287
  # aggregate-level path: `ctx.correction_bindings` is read again by
264
288
  # `step_enforce_ensures`, below, so an `as:`-named binding is
@@ -292,7 +316,7 @@ module Hecks
292
316
  step(:advance_lifecycle) { ctx.element[ctx.entity.lifecycle.field] = ctx.transition.target }
293
317
  end
294
318
 
295
- # An ensures reads the SETTLED record, so it needs a view hydrated from
319
+ # An ensures reads the settled record, so it needs a view hydrated from
296
320
  # `element` as it stands now, mutations included — unlike `view` above,
297
321
  # built once and read pre-mutation by enforce_givens.
298
322
  def step_enforce_ensures(ctx)
@@ -309,9 +333,9 @@ module Hecks
309
333
  end
310
334
  end
311
335
 
312
- # THE PARENT AGGREGATE's own invariants — `ctx.instance` is the
336
+ # The parent aggregate's own invariants — `ctx.instance` is the
313
337
  # parent record an entity mutation writes into (this file's own
314
- # `Context` comment), the SAME boundary an aggregate-level
338
+ # `Context` comment), the same boundary an aggregate-level
315
339
  # invariant guards regardless of which interpreter changed it. No
316
340
  # separate "entity invariant" exists (S10, ADR 0025 scopes
317
341
  # `invariant` to the aggregate only) — see `Admissibility#
@@ -320,21 +344,23 @@ module Hecks
320
344
  step(:enforce_invariants) { @rules.enforce_invariants(ctx.instance, ctx.aggregate, domain: ctx.domain) }
321
345
  end
322
346
 
323
- # `dry_run:` skips this — see CommandInterpreter#step_save's own
324
- # comment, same reasoning and the same precedent
325
- # (`step_assign_creation_attributes`'s own conditional-skip).
347
+ # `dry_run:` skips the write half of this step — see
348
+ # CommandInterpreter#step_save's own comment (BUG#127): the
349
+ # reference-existence check stays unconditional, only the actual
350
+ # persist is behind the early return.
326
351
  def step_save(ctx)
352
+ step(:save) { @rules.resolve_state_references(ctx.domain, ctx.aggregate, ctx.instance.state) }
353
+
327
354
  return if ctx.dry_run
328
355
 
329
356
  step(:save) do
330
- @rules.resolve_state_references(ctx.domain, ctx.aggregate, ctx.instance.state)
331
357
  # `expected_version:` — see CommandInterpreter#step_save's own
332
358
  # comment: nil for a repository that isn't CAS-capable, or an
333
359
  # instance never read from storage, either of which falls
334
360
  # through to a plain save inside `AppendOnly#save`.
335
361
  ctx.persistence_outcome = ctx.repository.save(ctx.instance, expected_version: ctx.instance.version)
336
362
  if ctx.persistence_outcome.status == :stale
337
- # NOT a `RefusalWording.render` call — see
363
+ # Not a `RefusalWording.render` call — see
338
364
  # `CommandInterpreter#step_save`'s identical branch and
339
365
  # `Runtime::StaleWrite`'s own comment.
340
366
  raise(StaleWrite,
@@ -353,7 +379,7 @@ module Hecks
353
379
  ctx.result = step(:emit) { @rules.emit(ctx.command, ctx.domain, ctx.aggregate, ctx.instance, ctx.args, ctx.repository) }
354
380
  end
355
381
 
356
- # THE PARENT AGGREGATE, addressed exactly as `CommandInterpreter#hydrate`
382
+ # The parent aggregate, addressed exactly as `CommandInterpreter#hydrate`
357
383
  # addresses one acting on itself — derive from the declared identity first
358
384
  # (`Identity.of`), and let a bare `id:` name an already-derived record when
359
385
  # the identity itself is not what the caller is holding.
@@ -361,14 +387,14 @@ module Hecks
361
387
  parent_id = route&.aggregate ||
362
388
  Identity.of(aggregate, args) ||
363
389
  Identity.from(aggregate, args, :id) ||
364
- raise(NotFound, RefusalWording.render("NotFound", "entity_parent_no_identity",
365
- command: command_name, aggregate: aggregate.hecks_name,
366
- entity: entity_name, identity: Identity.reading(aggregate)))
390
+ raise(NotFound, RefusalWording.render_site("NotFound", "entity_parent_no_identity",
391
+ command: command_name, aggregate: aggregate.hecks_name,
392
+ entity: entity_name, identity: Identity.reading(aggregate)))
367
393
  found = repository.find(parent_id) ||
368
- raise(NotFound, RefusalWording.render("NotFound", "record_missing",
369
- aggregate: aggregate.hecks_name,
370
- identity: Identity.reading(aggregate),
371
- offered: Rendering.describe(parent_id)))
394
+ raise(NotFound, RefusalWording.render_site("NotFound", "record_missing",
395
+ aggregate: aggregate.hecks_name,
396
+ identity: Identity.reading(aggregate),
397
+ offered: Rendering.describe(parent_id)))
372
398
  found.dup
373
399
  end
374
400
 
@@ -378,7 +404,7 @@ module Hecks
378
404
  # `delegate_to_entity` step can locate and mutate the same element the
379
405
  # same way, against an aggregate record already held in memory. `call`,
380
406
  # above, and every `step_*` method reach them through that module now;
381
- # nothing about the STEPS themselves changed.
407
+ # nothing about the steps themselves changed.
382
408
  end
383
409
  end
384
410
  end
@@ -7,12 +7,12 @@ module Hecks
7
7
  class EnsuresNotMet < StandardError; end
8
8
 
9
9
  # `detail` — the failing comparison's own resolved operands, "left: X,
10
- # right: Y" — set only when the given's TOP-LEVEL shape is a bare
10
+ # right: Y" — set only when the given's top-level shape is a bare
11
11
  # comparison (`Evaluator.comparison_detail`'s own comment has the full
12
- # scoping); nil otherwise. Deliberately NOT folded into `#message`:
12
+ # scoping); nil otherwise. Deliberately not folded into `#message`:
13
13
  # that string is pinned byte-for-byte across this corpus's own specs
14
14
  # (`raise_error(GivenNotMet, "...")`, command_rules_spec.rb and every
15
- # domain that vendors this gem) as CONTRACT, so changing its shape by
15
+ # domain that vendors this gem) as contract, so changing its shape by
16
16
  # default would be a breaking change for every one of them. Riding on
17
17
  # `#detailed_message` instead (Ruby 3.2+, what irb/a Rails console's
18
18
  # own unhandled-exception banner already calls to show more than
@@ -39,7 +39,7 @@ module Hecks
39
39
  # is the right name carrying the wrong thing, this one is a name the command
40
40
  # never had. Both are the payload gate refusing before any rule runs.
41
41
  class UnknownArgument < StandardError; end
42
- # The third of the trio, and the one that was missing : a name the command DOES
42
+ # The third of the trio, and the one that was missing : a name the command does
43
43
  # declare, absent. TypeMismatch is the right name carrying the wrong thing,
44
44
  # UnknownArgument a name that was never declared, AbsentArgument a declared name
45
45
  # that never arrived. Between them they say a command takes exactly the
@@ -56,18 +56,18 @@ module Hecks
56
56
  # `given`/`ensures`/`invariant` that reads the field would otherwise
57
57
  # evaluate against a value nobody wrote, which is the same silent-
58
58
  # wrong-answer class as an unpopulated projection reading "not
59
- # active" (ADR 0025, "Added attributes and absence"). An OPTIONAL
59
+ # active" (ADR 0025, "Added attributes and absence"). An optional
60
60
  # attribute in the same spot reads nil instead — that is what
61
61
  # optional means, and this refusal is deliberately narrower than the
62
62
  # nil-read it sits beside, not a replacement for it.
63
63
  class AttributeAbsent < StandardError; end
64
- # THE SAME SILENT-WRONG-ANSWER CLASS AS ABOVE, one line up — a
64
+ # The same silent-wrong-answer class as above, one line up — a
65
65
  # `projects` field (S12, ADR 0025) this record predates, or that no
66
66
  # rebuild sweep has populated yet, read by a `given`/`ensures`/
67
67
  # `invariant` as though it carried a real value. `GuardState` is
68
68
  # the one place this is raised, the same way AttributeAbsent is —
69
- # a DECLARED field the record does not yet carry, distinguished
70
- # from that one only in WHY: an ordinary attribute is absent
69
+ # a declared field the record does not yet carry, distinguished
70
+ # from that one only in why: an ordinary attribute is absent
71
71
  # because nobody backfilled it, a projected field is absent
72
72
  # because nobody has swept it yet.
73
73
  class ProjectionAbsent < StandardError; end
@@ -79,14 +79,14 @@ module Hecks
79
79
  class Unauthorized < StandardError; end
80
80
  # `corrects` names a past event this record must have already emitted
81
81
  # (CommandBuilder#corrects_impl's own comment) — a fact the expression
82
- # evaluator cannot check (it is not a predicate over the record's OWN
82
+ # evaluator cannot check (it is not a predicate over the record's own
83
83
  # fields, it is "did this exact record ever announce this"), so it is
84
84
  # raised structurally, the same way AlreadyExists/NotFound are, rather
85
85
  # than being expressible as an ordinary `given`. Raised by
86
86
  # `CommandRules::Admissibility#enforce_correction_target`.
87
87
  class NothingToCorrect < StandardError; end
88
88
 
89
- # A RUNTIME FAULT, NOT A DOMAIN REFUSAL — deliberately absent from
89
+ # A runtime fault, not a domain refusal — deliberately absent from
90
90
  # `DOMAIN_REFUSALS` below and from `vocabulary.bluebook`'s own
91
91
  # `DomainRefusal` list. Raised when an optimistic-concurrency CAS write
92
92
  # (`AppendOnly#save`'s `expected_version:`) finds the stored version has
@@ -102,29 +102,29 @@ module Hecks
102
102
 
103
103
  # A Lambda-routed domain's own refusal (rust/host, `Runtime::
104
104
  # RemoteDispatcher`), carrying Rust's own refusal text verbatim —
105
- # NOT yet mapped back to the specific matching class above
105
+ # not yet mapped back to the specific matching class above
106
106
  # (GivenNotMet vs. EnsuresNotMet vs. ...), a real, known,
107
107
  # documented gap: the WASM projector's own event/refusal-wording
108
- # parity work (ADR 0021) makes the TEXT match Ruby's, but nothing
108
+ # parity work (ADR 0021) makes the text match Ruby's, but nothing
109
109
  # yet parses that text back into a typed Ruby exception the way a
110
110
  # local dispatch already raises one directly. Callers that only
111
111
  # need "the domain said no" (not which specific rule) are
112
- # unaffected; callers pattern-matching a SPECIFIC refusal class
112
+ # unaffected; callers pattern-matching a specific refusal class
113
113
  # against a Lambda-routed domain are the ones this gap would bite.
114
114
  class RemoteRefusal < StandardError; end
115
115
 
116
- # The domain saying NO — the errors a reaction may legitimately meet and
116
+ # The domain saying no — the errors a reaction may legitimately meet and
117
117
  # record as an undelivered outcome. A policy whose target refuses is a fact
118
118
  # about the domain ; the originating command still stands.
119
119
  #
120
- # Everything ELSE is a defect : a NoMethodError in an interpreter, a
120
+ # Everything else is a defect : a NoMethodError in an interpreter, a
121
121
  # NameError from a missing constant, a TypeError from a bad assumption. A
122
122
  # blanket `rescue StandardError` used to fold both into one line —
123
123
  # `delivered: false, reason: "..."` — so a crash in the runtime was
124
124
  # indistinguishable from a rule doing its job, and read as normal operation
125
125
  # in the log.
126
126
  #
127
- # UnknownVerb IS one of these, and deliberately : a cross-domain policy
127
+ # UnknownVerb is one of these, and deliberately : a cross-domain policy
128
128
  # (`across "Notifications"`) fires in deployments where that domain is not
129
129
  # loaded, and recording the undelivered reaction rather than raising is the
130
130
  # design — spec/policy_spec states it in so many words, "records a reaction
@@ -133,12 +133,12 @@ module Hecks
133
133
  # its own rule is the domain saying no as plainly as a given is — but the
134
134
  # class is declared over in value.rb and never made the list, so the policy
135
135
  # and saga interpreters, which rescue exactly these, would let it propagate
136
- # as though the RUNTIME had broken. A reaction whose target violates an
136
+ # as though the runtime had broken. A reaction whose target violates an
137
137
  # invariant is declined, not crashed. Found by spec/domain_refusal_spec on
138
138
  # its first run : every corpus refusal must be a class named here, and 23
139
139
  # of banking's were InvariantViolation.
140
- # THE NAMES COME FROM THE LANGUAGE, the classes from this module.
141
- # `DomainRefusal` declares WHICH refusals are the domain's own —
140
+ # The names come from the language, the classes from this module.
141
+ # `DomainRefusal` declares which refusals are the domain's own —
142
142
  # a rule the caller broke — as against a runtime fault. Resolving
143
143
  # each name here means a refusal declared but never defined fails
144
144
  # at load with a NameError, rather than being quietly absent from
@@ -2,7 +2,7 @@ require "time"
2
2
 
3
3
  module Hecks
4
4
  module Runtime
5
- # `correlation` is NOT on the wire — `to_h` below deliberately omits it,
5
+ # `correlation` is not on the wire — `to_h` below deliberately omits it,
6
6
  # the same as `bin/run`'s own event projection does. It is runtime
7
7
  # bookkeeping stamped by `Dispatcher#dispatch` when a saga leg's own
8
8
  # dispatch causes this event (see `SagaInterpreter#deliver_saga_dispatch`
@@ -11,19 +11,19 @@ module Hecks
11
11
  # one correlating on a different field. Absent for any event no saga
12
12
  # dispatch caused, which is most of them.
13
13
  Event = Struct.new(:name, :aggregate, :id, :payload, :occurred_at, :correlation, keyword_init: true) do
14
- # AN EMITTED EVENT IS A RECORD OF SOMETHING THAT HAPPENED, and a
15
- # mutable audit trail is not one. The PAYLOAD — the domain fact the
16
- # event carries — is frozen THROUGH on emission: freezing the Hash
14
+ # An emitted event is a record of something that happened, and a
15
+ # mutable audit trail is not one. The payload — the domain fact the
16
+ # event carries — is frozen through on emission: freezing the Hash
17
17
  # alone would leave every value in it editable in place, which is
18
18
  # the shape all four previous freezing bugs had.
19
19
  #
20
- # THE WHOLE EVENT, not just its payload. Correlation used to be
20
+ # The whole event, not just its payload. Correlation used to be
21
21
  # merged onto already-emitted events by `Dispatcher#dispatch`, which
22
22
  # is what kept an event writable after it had happened; it is set at
23
23
  # construction now, because it is part of the transaction and known
24
24
  # from `dispatch`'s own argument before anything is emitted.
25
25
  #
26
- # The LOG stays appendable: new events are still recorded. It is
26
+ # The log stays appendable: new events are still recorded. It is
27
27
  # each event that stops changing once it exists.
28
28
  def emit!
29
29
  Freezer.deep(payload)